| File: System\Runtime\InteropServices\JavaScript\Marshaling\JSMarshalerArgument.Object.cs | Web Access |
| Project: src\runtime\src\libraries\System.Runtime.InteropServices.JavaScript\src\System.Runtime.InteropServices.JavaScript.csproj (System.Runtime.InteropServices.JavaScript) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; using System.Threading.Tasks; namespace System.Runtime.InteropServices.JavaScript { // Methods in this file are marshaling System.Object signature to Any JS signature dynamically. // In order to do that, we are referring to all well know marshaled types // therefore they could not be linked out during AOT, when user uses System.Object signature in his [JSImport] or [JSExport] // it is pay for play public partial struct JSMarshalerArgument { /// <summary> /// Implementation of the argument marshaling. /// It's used by JSImport code generator and should not be used by developers in source code. /// </summary> /// <param name="value">The value to be marshaled.</param> #if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif public void ToManaged(out object? value) { if (slot.Type == MarshalerType.None) { value = default; } else if (slot.Type == MarshalerType.Object) { value = ((GCHandle)slot.GCHandle).Target; } else if (slot.Type == MarshalerType.Boolean) { ToManaged(out bool v); value = v; } else if (slot.Type == MarshalerType.Double) { ToManaged(out double v); value = v; } else if (slot.Type == MarshalerType.Single) { ToManaged(out float v); value = v; } else if (slot.Type == MarshalerType.JSObject) { ToManaged(out JSObject? val); value = val; } else if (slot.Type == MarshalerType.String) { ToManaged(out string? val); value = val; } else if (slot.Type == MarshalerType.Exception) { ToManaged(out Exception? val); value = val; } else if (slot.Type == MarshalerType.DateTime) { ToManaged(out DateTime? val); value = val; } else if (slot.Type == MarshalerType.JSException) { ToManaged(out Exception? val); value = val; } else if (slot.Type == MarshalerType.Array) { if (slot.ElementType == MarshalerType.Byte) { ToManaged(out byte[]? val); value = val; } else if (slot.ElementType == MarshalerType.Double) { ToManaged(out double[]? val); value = val; } else if (slot.ElementType == MarshalerType.Single) { ToManaged(out float[]? val); value = val; } else if (slot.ElementType == MarshalerType.Int32) { ToManaged(out int[]? val); value = val; } else if (slot.ElementType == MarshalerType.Object) { ToManaged(out object?[]? val); value = val; } else { throw new NotSupportedException(SR.Format(SR.ToManagedNotImplemented, slot.ElementType + "[]")); } } else if (slot.Type == MarshalerType.Task || slot.Type == MarshalerType.TaskResolved || slot.Type == MarshalerType.TaskRejected) { ToManaged(out Task<object?>? val, static (ref JSMarshalerArgument arg, out object? value) => { arg.ToManaged(out value); }); value = val; } else { throw new NotSupportedException(SR.Format(SR.ToManagedNotImplemented, slot.Type)); } } /// <summary> /// Implementation of the argument marshaling. /// It's used by JSImport code generator and should not be used by developers in source code. /// </summary> /// <param name="value">The value to be marshaled.</param> #if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif public void ToJS(object? value) { if (value == null) { slot.Type = MarshalerType.None; return; } Type type = value.GetType(); if (type.IsPrimitive) { if (typeof(long) == type) { // we do it because not all Int64 could fit into Int52 of the JS Number throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } else if (typeof(int) == type) { var v = (int)value; ToJS(v); } else if (typeof(short) == type) { var v = (short)value; ToJS(v); } else if (typeof(byte) == type) { var v = (byte)value; ToJS(v); } else if (typeof(char) == type) { var v = (char)value; ToJS(v); } else if (typeof(bool) == type) { var v = (bool)value; ToJS(v); } else if (typeof(double) == type) { var v = (double)value; ToJS(v); } else if (typeof(float) == type) { var v = (float)value; ToJS(v); } else if (typeof(IntPtr) == type) { var v = (IntPtr)value; ToJS(v); } else { throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } } else if (typeof(string) == type) { string? str = value as string; ToJS(str); } else if (typeof(DateTimeOffset) == type) { var v = (DateTimeOffset)value; ToJS(v); } else if (typeof(DateTime) == type) { var v = (DateTime)value; ToJS(v); } else if (Nullable.GetUnderlyingType(type) is Type ut && ut != null) { if (typeof(long) == ut) { // we do it because not all Int64 could fit into Int52 of the JS Number throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } else if (typeof(int) == ut) { var nv = value as int?; ToJS(nv); } else if (typeof(short) == ut) { var nv = value as short?; ToJS(nv); } else if (typeof(byte) == ut) { var nv = value as byte?; ToJS(nv); } else if (typeof(char) == ut) { var nv = value as char?; ToJS(nv); } else if (typeof(bool) == ut) { var nv = value as bool?; ToJS(nv); } else if (typeof(double) == ut) { var nv = value as double?; ToJS(nv); } else if (typeof(float) == ut) { var nv = value as float?; ToJS(nv); } else if (typeof(IntPtr) == ut) { var nv = value as IntPtr?; ToJS(nv); } else if (typeof(DateTimeOffset) == ut) { var nv = value as DateTimeOffset?; ToJS(nv); } else if (typeof(DateTime) == ut) { var nv = value as DateTime?; ToJS(nv); } else { throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } } else if (typeof(JSObject).IsAssignableFrom(type)) { JSObject? val = value as JSObject; ToJS(val); } else if (typeof(Exception).IsAssignableFrom(type)) { Exception? val = value as Exception; ToJS(val); } else if (typeof(Task<object>) == type) { Task<object>? val = value as Task<object>; ToJS<object>(val, (ref JSMarshalerArgument arg, object value) => { object? valueRef = value; arg.ToJS(valueRef); }); } else if (typeof(Task).IsAssignableFrom(type)) { Task? val = value as Task; ToJSDynamic(val); } else if (typeof(byte[]) == type) { byte[] val = (byte[])value; ToJS(val); } else if (typeof(int[]) == type) { int[] val = (int[])value; ToJS(val); } else if (typeof(float[]) == type) { float[] val = (float[])value; ToJS(val); } else if (typeof(double[]) == type) { double[] val = (double[])value; ToJS(val); } else if (typeof(string[]) == type) { string[] val = (string[])value; ToJS(val); } else if (typeof(object[]) == type) { object[] val = (object[])value; ToJS(val); } else if (type.IsArray) { throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } else if (typeof(MulticastDelegate).IsAssignableFrom(type.BaseType)) { throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ArraySegment<>)) { throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Span<>)) { throw new NotSupportedException(SR.Format(SR.ToJSNotImplemented, type.FullName)); } else { slot.Type = MarshalerType.Object; var ctx = ToJSContext; slot.GCHandle = ctx.GetJSOwnedObjectGCHandle(value); } } /// <summary> /// Implementation of the argument marshaling. /// It's used by JSImport code generator and should not be used by developers in source code. /// </summary> /// <param name="value">The value to be marshaled.</param> #if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif public unsafe void ToManaged(out object?[]? value) { if (slot.Type == MarshalerType.None) { value = null; return; } value = new object?[slot.Length]; JSMarshalerArgument* payload = (JSMarshalerArgument*)slot.IntPtrValue; for (int i = 0; i < slot.Length; i++) { ref JSMarshalerArgument arg = ref payload[i]; object? val; arg.ToManaged(out val); value[i] = val; } #if !ENABLE_JS_INTEROP_BY_VALUE Interop.Runtime.DeregisterGCRoot(slot.IntPtrValue); #endif NativeMemory.Free((void*)slot.IntPtrValue); } /// <summary> /// Implementation of the argument marshaling. /// It's used by JSImport code generator and should not be used by developers in source code. /// </summary> /// <param name="value">The value to be marshaled.</param> #if !DEBUG [MethodImpl(MethodImplOptions.AggressiveInlining)] #endif public unsafe void ToJS(object?[] value) { if (value == null) { slot.Type = MarshalerType.None; return; } slot.Length = value.Length; int bytes = value.Length * sizeof(JSMarshalerArgument); slot.Type = MarshalerType.Array; JSMarshalerArgument* payload = (JSMarshalerArgument*)NativeMemory.Alloc((nuint)bytes); Unsafe.InitBlock(payload, 0, (uint)bytes); #if !ENABLE_JS_INTEROP_BY_VALUE Interop.Runtime.RegisterGCRoot(payload, bytes, IntPtr.Zero); #endif for (int i = 0; i < slot.Length; i++) { ref JSMarshalerArgument arg = ref payload[i]; object? val = value[i]; arg.ToJS(val); value[i] = val; } slot.ElementType = MarshalerType.Object; slot.IntPtrValue = (IntPtr)payload; } } }