diff --git a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs index e3b0398841fa2..2ca3660b3cba5 100644 --- a/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs +++ b/src/libraries/Common/src/Interop/Browser/Interop.Runtime.cs @@ -38,8 +38,6 @@ internal static partial class Runtime [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern object ReleaseObject(int jsObjHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] - internal static extern object NewObjectJS(int jsObjHandle, object[] parms, out int exceptionalResult); - [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern object BindCoreObject(int jsObjHandle, int gcHandle, out int exceptionalResult); [MethodImplAttribute(MethodImplOptions.InternalCall)] internal static extern object BindHostObject(int jsObjHandle, int gcHandle, out int exceptionalResult); @@ -91,13 +89,6 @@ public static int New(string hostClassName, params object[] parms) return (int)res; } - public static JSObject? NewJSObject(JSObject? jsFuncPtr = null, params object[] parms) - { - object res = NewObjectJS(jsFuncPtr?.JSHandle ?? 0, parms, out int exception); - if (exception != 0) - throw new JSException((string)res); - return res as JSObject; - } public static object GetGlobalObject(string? str = null) { int exception; diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln index 284e65cfede9f..1a0db563634ee 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/System.Runtime.InteropServices.JavaScript.sln @@ -2,27 +2,23 @@ # Visual Studio 15 VisualStudioVersion = 15.0.27213.1 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript.Tests", "tests\System.Runtime.InteropServices.JavaScript.Tests.csproj", "{7A93B8A0-E663-4CB1-B6A5-850E5EC56146}" - ProjectSection(ProjectDependencies) = postProject - {4AC5343E-6E31-4BA5-A795-0493AE7E9008} = {4AC5343E-6E31-4BA5-A795-0493AE7E9008} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Runtime.InteropServices.JavaScript", "src\System.Runtime.InteropServices.JavaScript.csproj", "{4AC5343E-6E31-4BA5-A795-0493AE7E9008}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1A2F9F4A-A032-433E-B914-ADD5992BB178}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E107E9C1-E893-4E87-987E-04EF0DCEAEFD}" EndProject +Project("{9344BDBB-3E7F-41FC-A0DD-8665D75EE146}") = "System.Runtime.InteropServices.JavaScript.Tests", "tests\System.Runtime.InteropServices.JavaScript.Tests.csproj", "{4F753464-0719-4559-9E0F-86175CECA903}" + ProjectSection(ProjectDependencies) = postProject + {644FCC7F-1478-4D30-BF66-40E0ADF8723C} = {644FCC7F-1478-4D30-BF66-40E0ADF8723C} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.Runtime.InteropServices.JavaScript", "src\System.Runtime.InteropServices.JavaScript.csproj", "{644FCC7F-1478-4D30-BF66-40E0ADF8723C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7A93B8A0-E663-4CB1-B6A5-850E5EC56146}.Release|Any CPU.Build.0 = Release|Any CPU {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Debug|Any CPU.Build.0 = Debug|Any CPU {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -31,10 +27,6 @@ Global {96AF3242-A368-4F13-B006-A722CC3B8517}.Debug|Any CPU.Build.0 = Debug|Any CPU {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.ActiveCfg = Release|Any CPU {96AF3242-A368-4F13-B006-A722CC3B8517}.Release|Any CPU.Build.0 = Release|Any CPU - {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4AC5343E-6E31-4BA5-A795-0493AE7E9008}.Release|Any CPU.Build.0 = Release|Any CPU {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Debug|Any CPU.Build.0 = Debug|Any CPU {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -43,17 +35,25 @@ Global {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Debug|Any CPU.Build.0 = Debug|Any CPU {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Release|Any CPU.ActiveCfg = Release|Any CPU {375E5A4A-9026-411E-9CD8-606874CFF7F0}.Release|Any CPU.Build.0 = Release|Any CPU + {4F753464-0719-4559-9E0F-86175CECA903}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F753464-0719-4559-9E0F-86175CECA903}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F753464-0719-4559-9E0F-86175CECA903}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F753464-0719-4559-9E0F-86175CECA903}.Release|Any CPU.Build.0 = Release|Any CPU + {644FCC7F-1478-4D30-BF66-40E0ADF8723C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {644FCC7F-1478-4D30-BF66-40E0ADF8723C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {644FCC7F-1478-4D30-BF66-40E0ADF8723C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {644FCC7F-1478-4D30-BF66-40E0ADF8723C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {7A93B8A0-E663-4CB1-B6A5-850E5EC56146} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {B0FFC4A8-BAC3-4A7F-8FD5-5B680209371C} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {96AF3242-A368-4F13-B006-A722CC3B8517} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} - {4AC5343E-6E31-4BA5-A795-0493AE7E9008} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} {CEFAB3B8-3B7F-4B6C-9A5F-2DCDD1A551B5} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} {375E5A4A-9026-411E-9CD8-606874CFF7F0} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} + {4F753464-0719-4559-9E0F-86175CECA903} = {1A2F9F4A-A032-433E-B914-ADD5992BB178} + {644FCC7F-1478-4D30-BF66-40E0ADF8723C} = {E107E9C1-E893-4E87-987E-04EF0DCEAEFD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2151451C-8B2A-44DB-881E-B922A4795A30} diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs index 812e8a346fdab..07204d09c943e 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/AnyRef.cs @@ -2,25 +2,48 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.Win32.SafeHandles; + namespace System.Runtime.InteropServices.JavaScript { - public class AnyRef + public abstract class AnyRef : SafeHandleMinusOneIsInvalid { - public int JSHandle { get; protected private set; } - internal GCHandle Handle; + private GCHandle AnyRefHandle; + public int JSHandle => (int)handle; + + internal AnyRef(int jsHandle, bool ownsHandle) : this((IntPtr)jsHandle, ownsHandle) + { } + + internal AnyRef(IntPtr jsHandle, bool ownsHandle) : base(ownsHandle) + { + SetHandle(jsHandle); + AnyRefHandle = GCHandle.Alloc(this, ownsHandle ? GCHandleType.Weak : GCHandleType.Normal); + } + internal int Int32Handle => (int)(IntPtr)AnyRefHandle; + + protected void FreeGCHandle() + { + AnyRefHandle.Free(); + } +#if DEBUG_HANDLE + private int _refCount; - internal AnyRef(int jsHandle) + internal void AddRef() { - JSHandle = jsHandle; - Handle = GCHandle.Alloc(this); + Interlocked.Increment(ref _refCount); } - internal AnyRef(IntPtr jsHandle) + internal void Release() { - JSHandle = (int)jsHandle; - Handle = GCHandle.Alloc(this); + Debug.Assert(_refCount > 0, "AnyRefSafeHandle: Release() called more times than AddRef"); + Interlocked.Decrement(ref _refCount); } - internal int Int32Handle => (int)(IntPtr)Handle; + internal int RefCount => _refCount; +#endif } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs index 6be725222fd79..1aa575a7ae988 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Array.cs @@ -22,7 +22,8 @@ public Array(params object[] _params) : base(Interop.Runtime.New(_params) /// Initializes a new instance of the Array/> class. /// /// Js handle. - internal Array(IntPtr jsHandle) : base(jsHandle) + /// Whether or not the handle is owned by the clr or not. + internal Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs index 04e652db81cbb..56087a21c46c1 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/ArrayBuffer.cs @@ -25,7 +25,8 @@ public ArrayBuffer(int length) : base(Interop.Runtime.New(length)) /// Initializes a new instance of the JavaScript Core ArrayBuffer class. /// /// Js handle. - internal ArrayBuffer(IntPtr jsHandle) : base(jsHandle) + /// Whether or not the handle is owned by the clr or not. + internal ArrayBuffer(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs index 97bbe4de65f38..d0bad9c77d9cc 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/CoreObject.cs @@ -19,14 +19,14 @@ namespace System.Runtime.InteropServices.JavaScript /// public abstract class CoreObject : JSObject { - protected CoreObject(int jsHandle) : base(jsHandle) + protected CoreObject(int jsHandle) : base(jsHandle, true) { - object result = Interop.Runtime.BindCoreObject(jsHandle, (int)(IntPtr)Handle, out int exception); + object result = Interop.Runtime.BindCoreObject(jsHandle, Int32Handle, out int exception); if (exception != 0) throw new JSException($"CoreObject Error binding: {result}"); } - internal CoreObject(IntPtr js_handle) : base(js_handle) + internal CoreObject(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs index 0fd921a8e9f11..d4da3aa1c6618 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/DataView.cs @@ -63,19 +63,23 @@ public DataView(SharedArrayBuffer buffer, int byteOffset, int byteLength) : base /// /// Initializes a new instance of the DataView class. /// - /// Js handle. - internal DataView(IntPtr js_handle) : base(js_handle) + /// Js handle. + /// Managed owned + internal DataView(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } + /// /// Gets the length (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only. /// /// The length (in bytes) of this view. public int ByteLength => (int)GetObjectProperty("byteLength"); + /// /// Gets the offset (in bytes) of this view from the start of its ArrayBuffer. Fixed at construction time and thus read only. /// /// The offset (in bytes) of this view. public int ByteOffset => (int)GetObjectProperty("byteOffset"); + /// /// Gets the ArrayBuffer referenced by this view. Fixed at construction time and thus read only. /// @@ -97,6 +101,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Byte offset. /// Indicates whether the 64-bit float is stored in little- or big-endian format. If false, a big-endian value is read. public double GetFloat64(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getFloat64", byteOffset, littleEndian)); + /// /// Gets the signed 16-bit integer (short) at the specified byte offset from the start of the DataView. /// @@ -104,6 +109,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Byte offset. /// Indicates whether the 16-bit integer (short) is stored in little- or big-endian format. If false, a big-endian value is read. public short GetInt16(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getInt16", byteOffset, littleEndian)); + /// /// Gets the signed 32-bit integer (int) at the specified byte offset from the start of the DataView. /// @@ -111,6 +117,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Byte offset. /// Indicates whether the 32-bit integer (int) is stored in little- or big-endian format. If false, a big-endian value is read. public int GetInt32(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getInt32", byteOffset, littleEndian)); + /// /// Gets the signed 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. /// @@ -119,6 +126,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Indicates whether the 8-bit byte is stored in little- or big-endian format. If false, a big-endian value is read. [CLSCompliant(false)] public sbyte GetInt8(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getInt8", byteOffset, littleEndian)); + /// /// Gets the unsigned 16-bit integer (short) at the specified byte offset from the start of the DataView. /// @@ -127,6 +135,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Indicates whether the unsigned 16-bit float is stored in little- or big-endian format. If false, a big-endian value is read. [CLSCompliant(false)] public ushort GetUint16(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getUint16", byteOffset, littleEndian)); + /// /// Gets the usigned 32-bit integer (uint) at the specified byte offset from the start of the DataView. /// @@ -135,6 +144,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. [CLSCompliant(false)] public uint GetUint32(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getUint32", byteOffset, littleEndian)); + /// /// Gets the unsigned 8-bit byte (byte) at the specified byte offset from the start of the DataView. /// @@ -142,6 +152,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Byte offset. /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. public byte GetUint8(int byteOffset, bool littleEndian = false) => UnBoxValue(Invoke("getUint8", byteOffset, littleEndian)); + /// /// Sets the signed 32-bit float (float) at the specified byte offset from the start of the DataView. /// @@ -157,6 +168,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// double value. /// Indicates whether the 64-bit float is stored in little- or big-endian format. If false, a big-endian value is read. public void SetFloat64(int byteOffset, double value, bool littleEndian = false) => Invoke("setFloat64", byteOffset, value, littleEndian); + /// /// Sets the signed 16-bit integer (short) at the specified byte offset from the start of the DataView. /// @@ -164,6 +176,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// short value. /// Indicates whether the 16-bit integer (short) is stored in little- or big-endian format. If false, a big-endian value is read. public void SetInt16(int byteOffset, short value, bool littleEndian = false) => Invoke("setInt16", byteOffset, value, littleEndian); + /// /// Sets the signed 32-bit integer (int) at the specified byte offset from the start of the DataView. /// @@ -171,6 +184,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// int value. /// Indicates whether the 32-bit integer (int) is stored in little- or big-endian format. If false, a big-endian value is read. public void SetInt32(int byteOffset, int value, bool littleEndian = false) => Invoke("setInt32", byteOffset, value, littleEndian); + /// /// Sets the signed 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. /// @@ -179,6 +193,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Indicates whether the 8-bit byte is stored in little- or big-endian format. If false, a big-endian value is read. [CLSCompliant(false)] public void SetInt8(int byteOffset, sbyte value, bool littleEndian = false) => Invoke("setInt8", byteOffset, value, littleEndian); + /// /// Sets the unsigned 16-bit integer (short) at the specified byte offset from the start of the DataView. /// @@ -187,6 +202,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Indicates whether the unsigned 16-bit float is stored in little- or big-endian format. If false, a big-endian value is read. [CLSCompliant(false)] public void SetUint16(int byteOffset, ushort value, bool littleEndian = false) => Invoke("setUint16", byteOffset, value, littleEndian); + /// /// Sets the usigned 32-bit integer (uint) at the specified byte offset from the start of the DataView. /// @@ -195,6 +211,7 @@ internal DataView(IntPtr js_handle) : base(js_handle) /// Indicates whether the 32-bit float is stored in little- or big-endian format. If false, a big-endian value is read. [CLSCompliant(false)] public void SetUint32(int byteOffset, uint value, bool littleEndian = false) => Invoke("setUint32", byteOffset, value, littleEndian); + /// /// Sets the unsigned 8-bit byte (sbyte) at the specified byte offset from the start of the DataView. /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs index 892e35591be50..c6c8d8be3c563 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float32Array.cs @@ -12,7 +12,6 @@ public Float32Array() { } public Float32Array(int length) : base(length) { } - public Float32Array(ArrayBuffer buffer) : base(buffer) { } public Float32Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } @@ -25,7 +24,8 @@ public Float32Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byt public Float32Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Float32Array(IntPtr js_handle) : base(js_handle) { } + internal Float32Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) + { } /// /// Defines an implicit conversion of Float32Array class to a float diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs index f5c96e9755248..7dc669a3670f7 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Float64Array.cs @@ -12,7 +12,6 @@ public Float64Array() { } public Float64Array(int length) : base(length) { } - public Float64Array(ArrayBuffer buffer) : base(buffer) { } public Float64Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } @@ -25,7 +24,8 @@ public Float64Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byt public Float64Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Float64Array(IntPtr js_handle) : base(js_handle) { } + internal Float64Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) + { } /// /// Defines an implicit conversion of Float64Array class to a double diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs index e5abb9e62ec52..f39791664de5e 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Function.cs @@ -20,7 +20,7 @@ public class Function : CoreObject public Function(params object[] args) : base(Interop.Runtime.New(args)) { } - internal Function(IntPtr jHandle) : base(jHandle) + internal Function(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs index cd5e41d960ac9..2ebde5f7c12cb 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/HostObject.cs @@ -28,14 +28,14 @@ public HostObject(string hostName, params object[] _params) : base(Interop.Runti public abstract class HostObjectBase : JSObject, IHostObject { - protected HostObjectBase(int jHandle) : base(jHandle) + protected HostObjectBase(int jHandle) : base(jHandle, true) { - object result = Interop.Runtime.BindHostObject(jHandle, (int)(IntPtr)Handle, out int exception); + object result = Interop.Runtime.BindHostObject(jHandle, Int32Handle, out int exception); if (exception != 0) throw new JSException($"HostObject Error binding: {result}"); } - internal HostObjectBase(IntPtr js_handle) : base(js_handle) + internal HostObjectBase(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs index 12f8936aef2ee..2d81f26f3dc51 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int16Array.cs @@ -12,7 +12,6 @@ public Int16Array() { } public Int16Array(int length) : base(length) { } - public Int16Array(ArrayBuffer buffer) : base(buffer) { } public Int16Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } @@ -25,7 +24,7 @@ public Int16Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteO public Int16Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Int16Array(IntPtr js_handle) : base(js_handle) + internal Int16Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs index f55d426787faf..af5e791ab5dcd 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int32Array.cs @@ -12,7 +12,6 @@ public Int32Array() { } public Int32Array(int length) : base(length) { } - public Int32Array(ArrayBuffer buffer) : base(buffer) { } public Int32Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } @@ -25,7 +24,8 @@ public Int32Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteO public Int32Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Int32Array(IntPtr js_handle) : base(js_handle) { } + internal Int32Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) + { } /// /// Defines an implicit conversion of Int32Array class to a int diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs index 106baec6a7923..ac5cdf1ca673b 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Int8Array.cs @@ -15,7 +15,6 @@ public Int8Array() public Int8Array(int length) : base(length) { } - public Int8Array(ArrayBuffer buffer) : base(buffer) { } @@ -34,7 +33,7 @@ public Int8Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteOf public Int8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Int8Array(IntPtr js_handle) : base(js_handle) + internal Int8Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs index f13cfc04d9a65..f0d0012628a21 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/JSObject.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using Console = System.Diagnostics.Debug; + namespace System.Runtime.InteropServices.JavaScript { public interface IJSObject @@ -9,6 +11,7 @@ public interface IJSObject int JSHandle { get; } int Length { get; } } + /// /// JSObjects are wrappers for a native JavaScript object, and /// they retain a reference to the JavaScript object for the lifetime of this C# object. @@ -17,28 +20,55 @@ public class JSObject : AnyRef, IJSObject, IDisposable { internal object? RawObject; + // Right now this is used for wrapping Delegates + private WeakReference? WeakRawObject; + // to detect redundant calls public bool IsDisposed { get; private set; } - public JSObject() : this(Interop.Runtime.New()) + public JSObject() : this(Interop.Runtime.New(), true) { - object result = Interop.Runtime.BindCoreObject(JSHandle, (int)(IntPtr)Handle, out int exception); + object result = Interop.Runtime.BindCoreObject(JSHandle, Int32Handle, out int exception); if (exception != 0) throw new JSException($"JSObject Error binding: {result}"); } - internal JSObject(IntPtr js_handle) : base(js_handle) + internal JSObject(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } - internal JSObject(int js_handle) : base((IntPtr)js_handle) + internal JSObject(int jsHandle, bool ownsHandle) : base((IntPtr)jsHandle, ownsHandle) { } - internal JSObject(int js_handle, object raw_obj) : base(js_handle) + internal JSObject(int jsHandle, object rawObj) : base(jsHandle, false) + { + RawObject = rawObj; + } + + internal JSObject(int jsHandle, Delegate rawDelegate, bool ownsHandle = true) : base(jsHandle, ownsHandle) { - RawObject = raw_obj; + WeakRawObject = new WeakReference(rawDelegate, false); } + /// + /// Invoke a named method of the object, or throws a JSException on error. + /// + /// The name of the method to invoke. + /// The argument list to pass to the invoke command. + /// + /// + /// The return value can either be a primitive (string, int, double), a JSObject for JavaScript objects, a + /// System.Threading.Tasks.Task(object) for JavaScript promises, an array of + /// a byte, int or double (for Javascript objects typed as ArrayBuffer) or a + /// System.Func to represent JavaScript functions. The specific version of + /// the Func that will be returned depends on the parameters of the Javascript function + /// and return value. + /// + /// + /// The value of a returned promise (The Task(object) return) can in turn be any of the above + /// valuews. + /// + /// public object Invoke(string method, params object?[] args) { object res = Interop.Runtime.InvokeJSWithArgs(JSHandle, method, args, out int exception); @@ -47,25 +77,52 @@ public object Invoke(string method, params object?[] args) return res; } + /// + /// Returns the named property from the object, or throws a JSException on error. + /// + /// The name of the property to lookup + /// + /// This method can raise a JSException if fetching the property in Javascript raises an exception. + /// + /// + /// + /// The return value can either be a primitive (string, int, double), a + /// JSObject for JavaScript objects, a + /// System.Threading.Tasks.Task (object) for JavaScript promises, an array of + /// a byte, int or double (for Javascript objects typed as ArrayBuffer) or a + /// System.Func to represent JavaScript functions. The specific version of + /// the Func that will be returned depends on the parameters of the Javascript function + /// and return value. + /// + /// + /// The value of a returned promise (The Task(object) return) can in turn be any of the above + /// valuews. + /// + /// public object GetObjectProperty(string name) { - object propertyValue = Interop.Runtime.GetObjectProperty(JSHandle, name, out int exception); - if (exception != 0) throw new JSException((string)propertyValue); - return propertyValue; - } + /// + /// Sets the named property to the provided value. + /// + /// + /// + /// The name of the property to lookup + /// The value can be a primitive type (int, double, string, bool), an + /// array that will be surfaced as a typed ArrayBuffer (byte[], sbyte[], short[], ushort[], + /// float[], double[]) + /// Defaults to and creates the property on the javascript object if not found, if set to it will not create the property if it does not exist. If the property exists, the value is updated with the provided value. + /// public void SetObjectProperty(string name, object value, bool createIfNotExists = true, bool hasOwnProperty = false) { - object setPropResult = Interop.Runtime.SetObjectProperty(JSHandle, name, value, createIfNotExists, hasOwnProperty, out int exception); if (exception != 0) - throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); - + throw new JSException($"Error setting {name} on (js-obj js '{JSHandle}' .NET '{Int32Handle} raw '{RawObject != null})"); } /// @@ -92,64 +149,60 @@ public int Length /// The String name or Symbol of the property to test. public bool PropertyIsEnumerable(string prop) => (bool)Invoke("propertyIsEnumerable", prop); - internal void ReleaseHandle() + internal bool IsWeakWrapper => WeakRawObject?.Target != null; + + internal object? GetWrappedObject() { - FreeHandle(); - JSHandle = -1; - IsDisposed = true; - RawObject = null; - Handle.Free(); + return RawObject ?? WeakRawObject?.Target; } - - private void FreeHandle() + internal void FreeHandle() { - Interop.Runtime.ReleaseHandle(JSHandle, out int exception); - if (exception != 0) - throw new JSException($"Error releasing handle on (js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"); + Runtime.ReleaseJSObject(this); + SetHandleAsInvalid(); + IsDisposed = true; + RawObject = null; + WeakRawObject = null; + FreeGCHandle(); } public override bool Equals(object? obj) => obj is JSObject other && JSHandle == other.JSHandle; public override int GetHashCode() => JSHandle; - ~JSObject() + protected override bool ReleaseHandle() { - Dispose(false); - } - - public void Dispose() - { - // Dispose of unmanaged resources. - Dispose(true); - // Suppress finalization. - GC.SuppressFinalize(this); - } + bool ret = false; - // Protected implementation of Dispose pattern. - protected virtual void Dispose(bool disposing) - { +#if DEBUG_HANDLE + Console.WriteLine($"Release Handle handle:{handle}"); + try + { +#endif + FreeHandle(); + ret = true; - if (!IsDisposed) +#if DEBUG_HANDLE + } + catch (Exception exception) + { + Console.WriteLine($"ReleaseHandle: {exception.Message}"); + ret = true; // Avoid a second assert. + throw; + } + finally { - if (disposing) + if (!ret) { - - // Free any other managed objects here. - // - RawObject = null; + Console.WriteLine($"ReleaseHandle failed. handle:{handle}"); } - - IsDisposed = true; - - // Free any unmanaged objects here. - FreeHandle(); - } +#endif + return ret; } public override string ToString() { - return $"(js-obj js '{JSHandle}' .NET '{(IntPtr)Handle} raw '{RawObject != null})"; + return $"(js-obj js '{Int32Handle}' raw '{RawObject != null}' weak_raw '{WeakRawObject != null}')"; } } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs index 658cc1ef8f380..211c17a195e20 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Map.cs @@ -24,7 +24,8 @@ public Map(params object[] _params) : base(Runtime.New(_params)) /// Initializes a new instance of the Map class. /// /// Js handle. - internal Map(IntPtr jsHandle) : base(jsHandle) + /// Whether or not the handle is owned by the clr or not. + internal Map(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs index 1aff941a5aa49..e7b1ab58d8d56 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs @@ -6,19 +6,27 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; +using System.Diagnostics; namespace System.Runtime.InteropServices.JavaScript { public static class Runtime { - private static readonly Dictionary _boundObjects = new Dictionary(); + [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicMethods, typeof(Runtime))] + private static readonly Dictionary _boundObjects = new Dictionary(); private static readonly Dictionary _rawToJS = new Dictionary(); - - // / - // / Execute the provided string in the JavaScript context - // / - // / The js. - // / String. + // _weakDelegateTable is a ConditionalWeakTable with the Delegate and associated JSObject: + // Key Lifetime: + // Once the key dies, the dictionary automatically removes the key/value entry. + // No need to lock as it is thread safe. + private static readonly ConditionalWeakTable _weakDelegateTable = new ConditionalWeakTable(); + + // + // Execute the provided string in the JavaScript context + // + // The js. + // String. public static string InvokeJS(string str) { return Interop.Runtime.InvokeJS(str); @@ -41,6 +49,11 @@ public static int New(string hostClassName, params object[] parms) public static void FreeObject(object obj) { + if (obj is Delegate) + { + return; + } + JSObject? jsobj; lock (_rawToJS) { @@ -49,8 +62,6 @@ public static void FreeObject(object obj) throw new JSException($"Error releasing object {obj}"); } } - - jsobj.ReleaseHandle(); } public static object GetGlobalObject(string? str = null) @@ -58,86 +69,80 @@ public static object GetGlobalObject(string? str = null) return Interop.Runtime.GetGlobalObject(str); } - public static int BindJSObject(int jsId, int mappedType) + private static int BindJSObject(int jsId, bool ownsHandle, int mappedType) { - JSObject? obj; + WeakReference? reference; lock (_boundObjects) { - if (!_boundObjects.TryGetValue(jsId, out obj)) + if (!_boundObjects.TryGetValue(jsId, out reference)) { IntPtr jsIntPtr = (IntPtr)jsId; - obj = mappedType > 0 ? BindJSType(jsIntPtr, mappedType) : new JSObject(jsIntPtr); - _boundObjects.Add(jsId, obj); + reference = new WeakReference(mappedType > 0 ? BindJSType(jsIntPtr, ownsHandle, mappedType) : new JSObject(jsIntPtr, ownsHandle), true); + _boundObjects.Add(jsId, reference); } } - - return obj.Int32Handle; + return reference.Target is JSObject target ? target.Int32Handle : 0; } - public static int BindCoreCLRObject(int jsId, int gcHandle) + private static int BindCoreCLRObject(int jsId, int gcHandle) { GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj; lock (_boundObjects) { - if (_boundObjects.TryGetValue(jsId, out JSObject? existingObj)) + if (_boundObjects.TryGetValue(jsId, out WeakReference? existingObj)) { - if (existingObj.Handle != h && h.IsAllocated) - throw new JSException($"Multiple handles pointing at js_id: {jsId}"); + var instance = existingObj.Target as JSObject; + if (instance?.Int32Handle != (int)(IntPtr)h && h.IsAllocated) + throw new JSException($"Multiple handles pointing at jsId: {jsId}"); - obj = existingObj; + obj = instance; } else { obj = h.Target as JSObject; + _boundObjects.Add(jsId, new WeakReference(obj, true)); } } - return obj?.Int32Handle ?? 0; } - private static JSObject BindJSType(IntPtr jsIntPtr, int coreType) => + private static JSObject BindJSType(IntPtr jsIntPtr, bool ownsHandle, int coreType) => coreType switch { - 1 => new Array(jsIntPtr), - 2 => new ArrayBuffer(jsIntPtr), - 3 => new DataView(jsIntPtr), - 4 => new Function(jsIntPtr), - 5 => new Map(jsIntPtr), - 6 => new SharedArrayBuffer(jsIntPtr), - 10 => new Int8Array(jsIntPtr), - 11 => new Uint8Array(jsIntPtr), - 12 => new Uint8ClampedArray(jsIntPtr), - 13 => new Int16Array(jsIntPtr), - 14 => new Uint16Array(jsIntPtr), - 15 => new Int32Array(jsIntPtr), - 16 => new Uint32Array(jsIntPtr), - 17 => new Float32Array(jsIntPtr), - 18 => new Float64Array(jsIntPtr), + 1 => new Array(jsIntPtr, ownsHandle), + 2 => new ArrayBuffer(jsIntPtr, ownsHandle), + 3 => new DataView(jsIntPtr, ownsHandle), + 4 => new Function(jsIntPtr, ownsHandle), + 5 => new Map(jsIntPtr, ownsHandle), + 6 => new SharedArrayBuffer(jsIntPtr, ownsHandle), + 10 => new Int8Array(jsIntPtr, ownsHandle), + 11 => new Uint8Array(jsIntPtr, ownsHandle), + 12 => new Uint8ClampedArray(jsIntPtr, ownsHandle), + 13 => new Int16Array(jsIntPtr, ownsHandle), + 14 => new Uint16Array(jsIntPtr, ownsHandle), + 15 => new Int32Array(jsIntPtr, ownsHandle), + 16 => new Uint32Array(jsIntPtr, ownsHandle), + 17 => new Float32Array(jsIntPtr, ownsHandle), + 18 => new Float64Array(jsIntPtr, ownsHandle), _ => throw new ArgumentOutOfRangeException(nameof(coreType)) }; - public static int UnBindJSObject(int jsId) + internal static bool ReleaseJSObject(JSObject objToRelease) { - lock (_boundObjects) - { - return _boundObjects.Remove(jsId, out JSObject? obj) ? obj.Int32Handle : 0; - } - } + Interop.Runtime.ReleaseHandle(objToRelease.JSHandle, out int exception); + if (exception != 0) + throw new JSException($"Error releasing handle on (js-obj js '{objToRelease.JSHandle}' mono '{objToRelease.Int32Handle} raw '{objToRelease.RawObject != null}' weak raw '{objToRelease.IsWeakWrapper}' )"); - public static void UnBindJSObjectAndFree(int jsId) - { - JSObject? obj; lock (_boundObjects) { - _boundObjects.Remove(jsId, out obj); + _boundObjects.Remove(objToRelease.JSHandle); } - - obj?.ReleaseHandle(); + return true; } - public static void UnBindRawJSObjectAndFree(int gcHandle) + private static void UnBindRawJSObjectAndFree(int gcHandle) { GCHandle h = (GCHandle)(IntPtr)gcHandle; JSObject? obj = h.Target as JSObject; @@ -146,91 +151,109 @@ public static void UnBindRawJSObjectAndFree(int gcHandle) if (obj?.RawObject != null) { _rawToJS.Remove(obj.RawObject); - - obj.ReleaseHandle(); + obj.FreeHandle(); } } } - public static object CreateTaskSource(int jsId) + private static object CreateTaskSource(int jsId) { return new TaskCompletionSource(); } - public static void SetTaskSourceResult(TaskCompletionSource tcs, object result) + private static void SetTaskSourceResult(TaskCompletionSource tcs, object result) { tcs.SetResult(result); } - public static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) + private static void SetTaskSourceFailure(TaskCompletionSource tcs, string reason) { tcs.SetException(new JSException(reason)); } - public static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) + private static int GetTaskAndBind(TaskCompletionSource tcs, int jsId) { return BindExistingObject(tcs.Task, jsId); } - public static int BindExistingObject(object rawObj, int jsId) + private static int BindExistingObject(object rawObj, int jsId) { - var obj = rawObj as JSObject; - if (obj != null) - return obj.Int32Handle; - - lock (_rawToJS) + JSObject? jsObject; + if (rawObj is Delegate dele) { - if (!_rawToJS.TryGetValue(rawObj, out obj)) { - obj = new JSObject(jsId, rawObj); - _rawToJS.Add(rawObj, obj); + jsObject = new JSObject(jsId, dele); + lock (_boundObjects) + { + _boundObjects.Add(jsId, new WeakReference(jsObject)); + } + lock (_weakDelegateTable) + { + _weakDelegateTable.Add(dele, jsObject); } } - - return obj.Int32Handle; + else + { + lock (_rawToJS) + { + if (!_rawToJS.TryGetValue(rawObj, out jsObject)) + { + _rawToJS.Add(jsId, jsObject = new JSObject(jsId, rawObj)); + } + } + } + return jsObject.Int32Handle; } - public static int GetJSObjectId(object rawObj) + private static int GetJSObjectId(object rawObj) { - if (rawObj is JSObject js) - return js.JSHandle; - - lock (_rawToJS) + JSObject? jsObject; + if (rawObj is Delegate dele) { - if (_rawToJS.TryGetValue(rawObj, out JSObject? ojs)) - return ojs.JSHandle; + lock (_weakDelegateTable) + { + _weakDelegateTable.TryGetValue(dele, out jsObject); + } } - - return -1; + else + { + lock (_rawToJS) + { + _rawToJS.TryGetValue(rawObj, out jsObject); + } + } + return jsObject?.JSHandle ?? -1; } - public static object? GetDotNetObject(int gcHandle) + private static object? GetDotNetObject(int gcHandle) { GCHandle h = (GCHandle)(IntPtr)gcHandle; - return h.Target is JSObject js ? js.RawObject : null; + + return h.Target is JSObject js ? + js.GetWrappedObject() ?? h.Target : h.Target; } - public static object BoxInt(int i) + private static object BoxInt(int i) { return i; } - public static object BoxDouble(double d) + private static object BoxDouble(double d) { return d; } - public static object BoxBool(int b) + private static object BoxBool(int b) { return b == 0 ? false : true; } - public static bool IsSimpleArray(object a) + private static bool IsSimpleArray(object a) { return a is System.Array arr && arr.Rank == 1 && arr.GetLowerBound(0) == 0; } [StructLayout(LayoutKind.Explicit)] - public struct IntPtrAndHandle + private struct IntPtrAndHandle { [FieldOffset(0)] internal IntPtr ptr; @@ -239,7 +262,7 @@ public struct IntPtrAndHandle internal RuntimeMethodHandle handle; } - public static string GetCallSignature(IntPtr methodHandle) + private static string GetCallSignature(IntPtr methodHandle) { IntPtrAndHandle tmp = default(IntPtrAndHandle); tmp.ptr = methodHandle; @@ -299,6 +322,10 @@ public static string GetCallSignature(IntPtr methodHandle) { res[c] = 'u'; } + else if (t == typeof(SafeHandle)) + { + res[c] = 'h'; + } else { if (t.IsValueType) @@ -311,7 +338,7 @@ public static string GetCallSignature(IntPtr methodHandle) return new string(res); } - public static void SetupJSContinuation(Task task, JSObject continuationObj) + private static void SetupJSContinuation(Task task, JSObject continuationObj) { if (task.IsCompleted) Complete(); @@ -353,12 +380,12 @@ void Complete() } } - public static string ObjectToString(object o) + private static string ObjectToString(object o) { return o.ToString() ?? string.Empty; } - public static double GetDateValue(object dtv) + private static double GetDateValue(object dtv) { if (dtv == null) throw new ArgumentNullException(nameof(dtv)); @@ -371,15 +398,89 @@ public static double GetDateValue(object dtv) return new DateTimeOffset(dt).ToUnixTimeMilliseconds(); } - public static DateTime CreateDateTime(double ticks) + private static DateTime CreateDateTime(double ticks) { DateTimeOffset unixTime = DateTimeOffset.FromUnixTimeMilliseconds((long)ticks); return unixTime.DateTime; } - public static Uri CreateUri(string uri) + private static Uri CreateUri(string uri) { return new Uri(uri); } + + private static bool SafeHandleAddRef(SafeHandle safeHandle) + { + bool _addRefSucceeded = false; +#if DEBUG_HANDLE + var _anyref = safeHandle as AnyRef; +#endif + try + { + safeHandle.DangerousAddRef(ref _addRefSucceeded); +#if DEBUG_HANDLE + if (_addRefSucceeded && _anyref != null) + _anyref.AddRef(); +#endif + } + catch + { + if (_addRefSucceeded) + { + safeHandle.DangerousRelease(); +#if DEBUG_HANDLE + if (_anyref != null) + _anyref.Release(); +#endif + _addRefSucceeded = false; + } + } +#if DEBUG_HANDLE + Debug.WriteLine($"\tSafeHandleAddRef: {safeHandle.DangerousGetHandle()} / RefCount: {((_anyref == null) ? 0 : _anyref.RefCount)}"); +#endif + return _addRefSucceeded; + } + + private static void SafeHandleRelease(SafeHandle safeHandle) + { + safeHandle.DangerousRelease(); +#if DEBUG_HANDLE + var _anyref = safeHandle as AnyRef; + if (_anyref != null) + { + _anyref.Release(); + Debug.WriteLine($"\tSafeHandleRelease: {safeHandle.DangerousGetHandle()} / RefCount: {_anyref.RefCount}"); + } +#endif + } + + private static void SafeHandleReleaseByHandle(int jsId) + { +#if DEBUG_HANDLE + Debug.WriteLine($"SafeHandleReleaseByHandle: {jsId}"); +#endif + lock (_boundObjects) + { + if (_boundObjects.TryGetValue(jsId, out WeakReference? reference)) + { + Debug.Assert(reference.Target != null, $"\tSafeHandleReleaseByHandle: did not find active target {jsId} / target: {reference.Target}"); + SafeHandleRelease((AnyRef)reference.Target); + } + else + { + Debug.Fail($"\tSafeHandleReleaseByHandle: did not find reference for {jsId}"); + } + } + } + + public static IntPtr SafeHandleGetHandle(SafeHandle safeHandle, bool addRef) + { +#if DEBUG_HANDLE + Debug.WriteLine($"SafeHandleGetHandle: {safeHandle.DangerousGetHandle()} / addRef {addRef}"); +#endif + if (addRef && !SafeHandleAddRef(safeHandle)) return IntPtr.Zero; + return safeHandle.DangerousGetHandle(); + } + } } diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs index cc3e90ac5662e..0b3e5b2ac1b42 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/SharedArrayBuffer.cs @@ -15,7 +15,7 @@ public class SharedArrayBuffer : CoreObject public SharedArrayBuffer(int length) : base(Interop.Runtime.New(length)) { } - internal SharedArrayBuffer(IntPtr js_handle) : base(js_handle) + internal SharedArrayBuffer(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs index be2fa43cb1c6c..7100f0b8c329d 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/TypedArray.cs @@ -76,7 +76,7 @@ protected TypedArray(SharedArrayBuffer buffer, int byteOffset) : base(Interop.Ru protected TypedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(Interop.Runtime.New(buffer, byteOffset, length)) { } - internal TypedArray(IntPtr jsHandle) : base(jsHandle) + internal TypedArray(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } public TypedArrayTypeCode GetTypedArrayType() diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs index 8c3a42dee25c0..503b1f0416e45 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint16Array.cs @@ -13,7 +13,6 @@ public Uint16Array() { } public Uint16Array(int length) : base(length) { } - public Uint16Array(ArrayBuffer buffer) : base(buffer) { } public Uint16Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } @@ -26,7 +25,7 @@ public Uint16Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byte public Uint16Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Uint16Array(IntPtr js_handle) : base(js_handle) + internal Uint16Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs index 5e4d14e594465..d8fadf6891390 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint32Array.cs @@ -13,7 +13,6 @@ public Uint32Array() { } public Uint32Array(int length) : base(length) { } - public Uint32Array(ArrayBuffer buffer) : base(buffer) { } public Uint32Array(ArrayBuffer buffer, int byteOffset) : base(buffer, byteOffset) { } @@ -26,7 +25,8 @@ public Uint32Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byte public Uint32Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Uint32Array(IntPtr js_handle) : base(js_handle) { } + internal Uint32Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) + { } /// /// Defines an implicit conversion of Uint32Array class to a uint diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs index cf3781df62924..aac5d88f4e811 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8Array.cs @@ -17,7 +17,6 @@ public Uint8Array() public Uint8Array(int length) : base(length) { } - public Uint8Array(ArrayBuffer buffer) : base(buffer) { } @@ -36,7 +35,7 @@ public Uint8Array(SharedArrayBuffer buffer, int byteOffset) : base(buffer, byteO public Uint8Array(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Uint8Array(IntPtr js_handle) : base(js_handle) + internal Uint8Array(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs index 913d7a89a65f5..4cddec6d3a3a7 100644 --- a/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs +++ b/src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Uint8ClampedArray.cs @@ -17,7 +17,6 @@ public Uint8ClampedArray() public Uint8ClampedArray(int length) : base(length) { } - public Uint8ClampedArray(ArrayBuffer buffer) : base(buffer) { } @@ -36,7 +35,7 @@ public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset) : base(buffer public Uint8ClampedArray(SharedArrayBuffer buffer, int byteOffset, int length) : base(buffer, byteOffset, length) { } - internal Uint8ClampedArray(IntPtr js_handle) : base(js_handle) + internal Uint8ClampedArray(IntPtr jsHandle, bool ownsHandle) : base(jsHandle, ownsHandle) { } /// diff --git a/src/mono/wasm/runtime/binding_support.js b/src/mono/wasm/runtime/binding_support.js index c1b4fa1019af5..f9fd4d5b291b7 100644 --- a/src/mono/wasm/runtime/binding_support.js +++ b/src/mono/wasm/runtime/binding_support.js @@ -9,6 +9,8 @@ var BindingSupportLib = { mono_wasm_object_registry: [], mono_wasm_ref_counter: 0, mono_wasm_free_list: [], + mono_wasm_owned_objects_frames: [], + mono_wasm_owned_objects_LMF: [], mono_wasm_marshal_enum_as_int: false, mono_bindings_init: function (binding_asm) { this.BINDING_ASM = binding_asm; @@ -79,8 +81,6 @@ var BindingSupportLib = { this.bind_js_obj = get_method ("BindJSObject"); this.bind_core_clr_obj = get_method ("BindCoreCLRObject"); this.bind_existing_obj = get_method ("BindExistingObject"); - this.unbind_js_obj = get_method ("UnBindJSObject"); - this.unbind_js_obj_and_free = get_method ("UnBindJSObjectAndFree"); this.unbind_raw_obj_and_free = get_method ("UnBindRawJSObjectAndFree"); this.get_js_id = get_method ("GetJSObjectId"); this.get_raw_mono_obj = get_method ("GetDotNetObject"); @@ -102,6 +102,11 @@ var BindingSupportLib = { this.create_date_time = get_method ("CreateDateTime"); this.create_uri = get_method ("CreateUri"); + this.safehandle_addref = get_method ("SafeHandleAddRef"); + this.safehandle_release = get_method ("SafeHandleRelease"); + this.safehandle_get_handle = get_method ("SafeHandleGetHandle"); + this.safehandle_release_by_handle = get_method ("SafeHandleReleaseByHandle"); + this.init = true; }, @@ -161,6 +166,7 @@ var BindingSupportLib = { throw new Error ("no idea on how to unbox value types"); case 5: { // delegate var obj = this.extract_js_obj (mono_obj); + obj.__mono_delegate_alive__ = true; return function () { return BINDING.invoke_delegate (obj, arguments); }; @@ -225,6 +231,18 @@ var BindingSupportLib = { case 22: // clr .NET Uri var uriValue = this.call_method(this.object_to_string, null, "m", [ mono_obj ]); return uriValue; + case 23: // clr .NET SafeHandle + var addRef = true; + var js_handle = this.call_method(this.safehandle_get_handle, null, "mii", [ mono_obj, addRef ]); + var requiredObject = BINDING.mono_wasm_require_handle (js_handle); + if (addRef) + { + if (typeof this.mono_wasm_owned_objects_LMF === "undefined") + this.mono_wasm_owned_objects_LMF = []; + + this.mono_wasm_owned_objects_LMF.push(js_handle); + } + return requiredObject; default: throw new Error ("no idea on how to unbox object kind " + type); } @@ -495,9 +513,9 @@ var BindingSupportLib = { // return the unboxed enum value. return this.mono_unbox_enum(monoEnum); }, - wasm_binding_obj_new: function (js_obj_id, type) + wasm_binding_obj_new: function (js_obj_id, ownsHandle, type) { - return this.call_method (this.bind_js_obj, null, "ii", [js_obj_id, type]); + return this.call_method (this.bind_js_obj, null, "iii", [js_obj_id, ownsHandle, type]); }, wasm_bind_existing: function (mono_obj, js_id) { @@ -509,16 +527,6 @@ var BindingSupportLib = { return this.call_method (this.bind_core_clr_obj, null, "ii", [js_id, gc_handle]); }, - wasm_unbind_js_obj: function (js_obj_id) - { - this.call_method (this.unbind_js_obj, null, "i", [js_obj_id]); - }, - - wasm_unbind_js_obj_and_free: function (js_obj_id) - { - this.call_method (this.unbind_js_obj_and_free, null, "i", [js_obj_id]); - }, - wasm_get_js_id: function (mono_obj) { return this.call_method (this.get_js_id, null, "m", [mono_obj]); @@ -711,6 +719,12 @@ var BindingSupportLib = { invoke_delegate: function (delegate_obj, js_args) { this.bindings_lazy_init (); + // Check to make sure the delegate is still alive on the CLR side of things. + if (typeof delegate_obj.__mono_delegate_alive__ !== "undefined") { + if (!delegate_obj.__mono_delegate_alive__) + throw new Error("The delegate target that is being invoked is no longer available. Please check if it has been prematurely GC'd."); + } + if (!this.delegate_dynamic_invoke) { if (!this.corlib) this.corlib = this.assembly_load ("System.Private.CoreLib"); @@ -879,6 +893,12 @@ var BindingSupportLib = { obj.__mono_jshandle__ = handle; // Obtain the JS -> C# type mapping. var wasm_type = this.get_wasm_type(obj); + var ownsHandle = true; + + // If this is the global object we do not want to release it. + if (typeof ___mono_wasm_global___ !== "undefined" && ___mono_wasm_global___ === obj) + ownsHandle = false; + gc_handle = obj.__mono_gchandle__ = this.wasm_binding_obj_new(handle + 1, typeof wasm_type === "undefined" ? -1 : wasm_type); this.mono_wasm_object_registry[handle] = obj; @@ -901,11 +921,16 @@ var BindingSupportLib = { var gc_handle = obj.__mono_gchandle__; if (typeof gc_handle !== "undefined") { - this.wasm_unbind_js_obj_and_free(js_id); obj.__mono_gchandle__ = undefined; obj.__mono_jshandle__ = undefined; + // If we are unregistering a delegate then mark it as not being alive + // this will be checked in the delegate invoke and throw an appropriate + // error. + if (typeof obj.__mono_delegate_alive__ !== "undefined") + obj.__mono_delegate_alive__ = false; + this.mono_wasm_object_registry[js_id - 1] = undefined; this.mono_wasm_free_list.push(js_id - 1); } @@ -961,7 +986,36 @@ var BindingSupportLib = { } throw Error('Unable to get mono wasm global object.'); }, - + mono_wasm_parse_args : function (args) { + var js_args = this.mono_array_to_js_array(args); + this.mono_wasm_save_LMF(); + return js_args; + }, + mono_wasm_save_LMF : function () { + //console.log("save LMF: " + BINDING.mono_wasm_owned_objects_frames.length) + BINDING.mono_wasm_owned_objects_frames.push(BINDING.mono_wasm_owned_objects_LMF); + BINDING.mono_wasm_owned_objects_LMF = undefined; + }, + mono_wasm_unwind_LMF : function () { + var __owned_objects__ = this.mono_wasm_owned_objects_frames.pop(); + // Release all managed objects that are loaded into the LMF + if (typeof __owned_objects__ !== "undefined") + { + // Look into passing the array of owned object handles in one pass. + var refidx; + for (refidx = 0; refidx < __owned_objects__.length; refidx++) + { + var ownerRelease = __owned_objects__[refidx]; + this.call_method(this.safehandle_release_by_handle, null, "i", [ ownerRelease ]); + } + } + //console.log("restore LMF: " + BINDING.mono_wasm_owned_objects_frames.length) + + }, + mono_wasm_convert_return_value: function (ret) { + this.mono_wasm_unwind_LMF(); + return this.js_to_mono_obj (ret); + }, }, mono_wasm_invoke_js_with_args: function(js_handle, method_name, args, is_exception) { @@ -979,7 +1033,7 @@ var BindingSupportLib = { return BINDING.js_string_to_mono_string ("Invalid method name object '" + method_name + "'"); } - var js_args = BINDING.mono_array_to_js_array(args); + var js_args = BINDING.mono_wasm_parse_args(args); var res; try { @@ -987,8 +1041,10 @@ var BindingSupportLib = { if (typeof m === "undefined") throw new Error("Method: '" + js_name + "' not found for: '" + Object.prototype.toString.call(obj) + "'"); var res = m.apply (obj, js_args); - return BINDING.js_to_mono_obj (res); + return BINDING.mono_wasm_convert_return_value(res); } catch (e) { + // make sure we release object reference counts on errors. + BINDING.mono_wasm_unwind_LMF(); var res = e.toString (); setValue (is_exception, 1, "i32"); if (res === null || res === undefined) @@ -1045,6 +1101,7 @@ var BindingSupportLib = { var result = false; var js_value = BINDING.unbox_mono_obj(value); + BINDING.mono_wasm_save_LMF(); if (createIfNotExist) { requireObject[property] = js_value; @@ -1068,7 +1125,8 @@ var BindingSupportLib = { result = true; } - } + } + BINDING.mono_wasm_unwind_LMF(); return BINDING.call_method (BINDING.box_js_bool, null, "im", [ result ]); }, mono_wasm_get_by_index: function(js_handle, property_index, is_exception) { @@ -1101,9 +1159,11 @@ var BindingSupportLib = { } var js_value = BINDING.unbox_mono_obj(value); + BINDING.mono_wasm_save_LMF(); try { obj [property_index] = js_value; + BINDING.mono_wasm_unwind_LMF(); return true; } catch (e) { var res = e.toString (); @@ -1155,6 +1215,7 @@ var BindingSupportLib = { BINDING.wasm_bind_core_clr_obj(js_handle, gc_handle ); requireObject.__mono_gchandle__ = gc_handle; + requireObject.__js_handle__ = js_handle; return gc_handle; }, mono_wasm_bind_host_object: function(js_handle, gc_handle, is_exception) { @@ -1187,7 +1248,7 @@ var BindingSupportLib = { return BINDING.js_string_to_mono_string ("JavaScript host object '" + js_name + "' not found."); } - var js_args = BINDING.mono_array_to_js_array(args); + var js_args = BINDING.mono_wasm_parse_args(args); try { @@ -1205,7 +1266,7 @@ var BindingSupportLib = { var res = allocator(coreObj, js_args); var gc_handle = BINDING.mono_wasm_free_list.length ? BINDING.mono_wasm_free_list.pop() : BINDING.mono_wasm_ref_counter++; BINDING.mono_wasm_object_registry[gc_handle] = res; - return BINDING.js_to_mono_obj (gc_handle + 1); + return BINDING.mono_wasm_convert_return_value(gc_handle + 1); } catch (e) { var res = e.toString (); setValue (is_exception, 1, "i32"); @@ -1215,52 +1276,7 @@ var BindingSupportLib = { } }, - mono_wasm_new_object: function(object_handle_or_function, args, is_exception) { - BINDING.bindings_lazy_init (); - - if (!object_handle_or_function) { - return BINDING.js_to_mono_obj ({}); - } - else { - - var requireObject; - if (typeof object_handle_or_function === 'function') - requireObject = object_handle_or_function; - else - requireObject = BINDING.mono_wasm_require_handle (object_handle_or_function); - if (!requireObject) { - setValue (is_exception, 1, "i32"); - return BINDING.js_string_to_mono_string ("Invalid JS object handle '" + object_handle_or_function + "'"); - } - - var js_args = BINDING.mono_array_to_js_array(args); - - try { - - // This is all experimental !!!!!! - var allocator = function(constructor, js_args) { - // Not sure if we should be checking for anything here - var argsList = new Array(); - argsList[0] = constructor; - if (js_args) - argsList = argsList.concat(js_args); - var obj = new (constructor.bind.apply(constructor, argsList )); - return obj; - }; - - var res = allocator(requireObject, js_args); - return BINDING.extract_mono_obj (res); - } catch (e) { - var res = e.toString (); - setValue (is_exception, 1, "i32"); - if (res === null || res === undefined) - res = "Error allocating object."; - return BINDING.js_string_to_mono_string (res); - } - } - - }, mono_wasm_typed_array_to_array: function(js_handle, is_exception) { BINDING.bindings_lazy_init (); diff --git a/src/mono/wasm/runtime/corebindings.c b/src/mono/wasm/runtime/corebindings.c index 4b78715bc0b21..ffc8e9fe7f69f 100644 --- a/src/mono/wasm/runtime/corebindings.c +++ b/src/mono/wasm/runtime/corebindings.c @@ -19,7 +19,6 @@ extern MonoObject* mono_wasm_set_by_index (int js_handle, int property_index, Mo extern MonoObject* mono_wasm_get_global_object (MonoString *global_name, int *is_exception); extern void* mono_wasm_release_handle (int js_handle, int *is_exception); extern void* mono_wasm_release_object (int js_handle, int *is_exception); -extern MonoObject* mono_wasm_new_object (int js_handle, MonoArray *args, int *is_exception); extern MonoObject* mono_wasm_new (MonoString *core_name, MonoArray *args, int *is_exception); extern int mono_wasm_bind_core_object (int js_handle, int gc_handle, int *is_exception); extern int mono_wasm_bind_host_object (int js_handle, int gc_handle, int *is_exception); @@ -79,7 +78,6 @@ void core_initialize_internals () mono_add_internal_call ("Interop/Runtime::GetGlobalObject", mono_wasm_get_global_object); mono_add_internal_call ("Interop/Runtime::ReleaseHandle", mono_wasm_release_handle); mono_add_internal_call ("Interop/Runtime::ReleaseObject", mono_wasm_release_object); - mono_add_internal_call ("Interop/Runtime::NewObjectJS", mono_wasm_new_object); mono_add_internal_call ("Interop/Runtime::BindCoreObject", mono_wasm_bind_core_object); mono_add_internal_call ("Interop/Runtime::BindHostObject", mono_wasm_bind_host_object); mono_add_internal_call ("Interop/Runtime::New", mono_wasm_new); diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c index e536f1d8aa50d..a1f4e0cdc1678 100644 --- a/src/mono/wasm/runtime/driver.c +++ b/src/mono/wasm/runtime/driver.c @@ -42,6 +42,7 @@ int32_t mini_parse_debug_option (const char *option); static MonoClass* datetime_class; static MonoClass* datetimeoffset_class; static MonoClass* uri_class; +static MonoClass* safehandle_class; int mono_wasm_enable_gc; @@ -512,6 +513,7 @@ MonoClass* mono_get_uri_class(MonoException** exc) #define MARSHAL_TYPE_DATE 20 #define MARSHAL_TYPE_DATEOFFSET 21 #define MARSHAL_TYPE_URI 22 +#define MARSHAL_TYPE_SAFEHANDLE 23 // typed array marshalling #define MARSHAL_ARRAY_BYTE 11 @@ -537,6 +539,8 @@ mono_wasm_get_obj_type (MonoObject *obj) MonoException** exc = NULL; uri_class = mono_get_uri_class(exc); } + if (!safehandle_class) + safehandle_class = mono_class_from_name (mono_get_corlib(), "System.Runtime.InteropServices", "SafeHandle"); MonoClass *klass = mono_object_get_class (obj); MonoType *type = mono_class_get_type (klass); @@ -600,6 +604,9 @@ mono_wasm_get_obj_type (MonoObject *obj) return MARSHAL_TYPE_DELEGATE; if (class_is_task(klass)) return MARSHAL_TYPE_TASK; + if (safehandle_class && (klass == safehandle_class || mono_class_is_subclass_of(klass, safehandle_class, 0))) { + return MARSHAL_TYPE_SAFEHANDLE; + } return MARSHAL_TYPE_OBJECT; }