[browser] Fix System.Delegate.DynamicInvoke method can not be resolved (#47519)
authorKenneth Pouncey <kjpou@pt.lu>
Wed, 10 Feb 2021 08:58:35 +0000 (09:58 +0100)
committerGitHub <noreply@github.com>
Wed, 10 Feb 2021 08:58:35 +0000 (09:58 +0100)
src/libraries/System.Private.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Runtime.cs
src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System.Private.Runtime.InteropServices.JavaScript.Tests.csproj
src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs [new file with mode: 0644]
src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/HelperMarshal.cs
src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/MarshalTests.cs
src/mono/wasm/runtime/binding_support.js
src/mono/wasm/runtime/driver.c

index 57a2cac..4c2a55c 100644 (file)
@@ -262,12 +262,12 @@ namespace System.Runtime.InteropServices.JavaScript
             internal RuntimeMethodHandle handle;
         }
 
-        public static string GetCallSignature(IntPtr methodHandle)
+        public static string GetCallSignature(IntPtr methodHandle, object objForRuntimeType)
         {
             IntPtrAndHandle tmp = default(IntPtrAndHandle);
             tmp.ptr = methodHandle;
 
-            MethodBase? mb = MethodBase.GetMethodFromHandle(tmp.handle);
+            MethodBase? mb = objForRuntimeType == null ? MethodBase.GetMethodFromHandle(tmp.handle) : MethodBase.GetMethodFromHandle(tmp.handle, Type.GetTypeHandle(objForRuntimeType));
             if (mb == null)
                 return string.Empty;
 
index 11dec89..e97ca51 100644 (file)
@@ -12,8 +12,9 @@
     <Compile Include="System\Runtime\InteropServices\JavaScript\ArrayTests.cs" />
     <!-- <Compile Include="System\Runtime\InteropServices\JavaScript\MapTests.cs" /> -->
     <Compile Include="System\Runtime\InteropServices\JavaScript\MarshalTests.cs" />
+    <Compile Include="System\Runtime\InteropServices\JavaScript\DelegateTests.cs" />
     <Compile Include="System\Runtime\InteropServices\JavaScript\HelperMarshal.cs" />
-    <Compile Include="System\Runtime\InteropServices\JavaScript\Http\HttpRequestMessageTest.cs" />    
+    <Compile Include="System\Runtime\InteropServices\JavaScript\Http\HttpRequestMessageTest.cs" />
   </ItemGroup>
   <ItemGroup>
     <!-- Part of the shared framework but not exposed. -->
diff --git a/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs b/src/libraries/System.Private.Runtime.InteropServices.JavaScript/tests/System/Runtime/InteropServices/JavaScript/DelegateTests.cs
new file mode 100644 (file)
index 0000000..2414a6c
--- /dev/null
@@ -0,0 +1,199 @@
+// 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.InteropServices.JavaScript;
+using System.Collections.Generic;
+using Xunit;
+
+namespace System.Runtime.InteropServices.JavaScript.Tests
+{
+    public static class DelegateTests
+    {
+        private static Function _objectPrototype;
+
+        [Fact]
+        public static void InvokeFunction()
+        {
+            HelperMarshal._functionResultValue = 0;
+            HelperMarshal._i32Value = 0;
+
+            Runtime.InvokeJS(@"
+                var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [  ]);
+                var res = funcDelegate (10, 20);
+                App.call_test_method (""InvokeI32"", [ res, res ]);
+            ");
+
+            Assert.Equal(30, HelperMarshal._functionResultValue);
+            Assert.Equal(60, HelperMarshal._i32Value);
+        }
+
+        [Fact]
+        public static void InvokeFunctionInLoopUsingConstanceValues()
+        {
+            HelperMarshal._functionResultValue = 0;
+            HelperMarshal._i32Value = 0;
+
+            Runtime.InvokeJS(@"
+                var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [  ]);
+                var res = funcDelegate (10, 20);
+                for (x = 0; x < 1000; x++)
+                {
+                    res = funcDelegate (10, 20);
+                }
+                App.call_test_method (""InvokeI32"", [ res, res ]);
+            ");
+
+            Assert.Equal(30, HelperMarshal._functionResultValue);
+            Assert.Equal(60, HelperMarshal._i32Value);
+        }
+
+        [Fact]
+        public static void InvokeFunctionInLoopUsingIncrementedValues()
+        {
+            HelperMarshal._functionResultValue = 0;
+            HelperMarshal._i32Value = 0;
+            Runtime.InvokeJS(@"
+                var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [  ]);
+                var res = funcDelegate (10, 20);
+                for (x = 0; x < 1000; x++)
+                {
+                    res = funcDelegate (x, x);
+                }
+                App.call_test_method (""InvokeI32"", [ res, res ]);
+            ");
+
+            Assert.Equal(1998, HelperMarshal._functionResultValue);
+            Assert.Equal(3996, HelperMarshal._i32Value);
+        }
+
+        [Fact]
+        public static void InvokeActionTReturnedByInvokingFuncT()
+        {
+            HelperMarshal._functionActionResultValue = 0;
+            HelperMarshal._functionActionResultValueOfAction = 0;
+
+            Runtime.InvokeJS(@"
+                var funcDelegate = App.call_test_method (""CreateFunctionDelegateWithAction"", [  ]);
+                var actionDelegate = funcDelegate (10, 20);
+                actionDelegate(30,40);
+            ");
+
+            Assert.Equal(30, HelperMarshal._functionActionResultValue);
+            Assert.Equal(70, HelperMarshal._functionActionResultValueOfAction);
+        }
+
+        [Fact]
+        public static void InvokeActionIntInt()
+        {
+            HelperMarshal._actionResultValue = 0;
+
+            Runtime.InvokeJS(@"
+                var actionDelegate = App.call_test_method (""CreateActionDelegate"", [  ]);
+                actionDelegate(30,40);
+            ");
+
+            Assert.Equal(70, HelperMarshal._actionResultValue);
+        }
+
+        [Fact]
+        public static void InvokeActionFloatIntToIntInt()
+        {
+            HelperMarshal._actionResultValue = 0;
+            Runtime.InvokeJS(@"
+                var actionDelegate = App.call_test_method (""CreateActionDelegate"", [  ]);
+                actionDelegate(3.14,40);
+            ");
+
+            Assert.Equal(43, HelperMarshal._actionResultValue);
+        }
+
+        [Fact]
+        public static void InvokeDelegateMethod()
+        {
+            HelperMarshal._delMethodResultValue = string.Empty;
+            Runtime.InvokeJS(@"
+                var del = App.call_test_method (""CreateDelegateMethod"", [  ]);
+                del(""Hic sunt dracones"");
+            ");
+
+            Assert.Equal("Hic sunt dracones", HelperMarshal._delMethodResultValue);
+        }
+
+        [Fact]
+        public static void InvokeDelegateMethodReturnString()
+        {
+            HelperMarshal._delMethodStringResultValue = string.Empty;
+            Runtime.InvokeJS(@"
+                var del = App.call_test_method (""CreateDelegateMethodReturnString"", [  ]);
+                var res = del(""Hic sunt dracones"");
+                App.call_test_method (""SetTestString1"", [ res ]);
+            ");
+
+            Assert.Equal("Received: Hic sunt dracones", HelperMarshal._delMethodStringResultValue);
+        }
+
+        [Theory]
+        [InlineData("CreateCustomMultiCastDelegate_VoidString", "Moin")]
+        [InlineData("CreateMultiCastAction_VoidString", "MoinMoin")]
+        public static void InvokeMultiCastDelegate_VoidString(string creator, string testStr)
+        {
+            HelperMarshal._delegateCallResult = string.Empty;
+            Runtime.InvokeJS($@"
+                var del = App.call_test_method (""{creator}"", [  ]);
+                del(""{testStr}"");
+            ");
+            Assert.Equal($"  Hello, {testStr}!  GoodMorning, {testStr}!", HelperMarshal._delegateCallResult);
+        }
+        
+        [Theory]
+        [InlineData("CreateDelegateFromAnonymousMethod_VoidString")]
+        [InlineData("CreateDelegateFromLambda_VoidString")]
+        [InlineData("CreateDelegateFromMethod_VoidString")]
+        [InlineData("CreateActionT_VoidString")]
+        public static void InvokeDelegate_VoidString(string creator)
+        {
+            HelperMarshal._delegateCallResult = string.Empty;
+            var s = Runtime.InvokeJS($@"
+                var del = App.call_test_method (""{creator}"", [  ]);
+                del(""Hic sunt dracones"");
+            ");
+
+            Assert.Equal("Notification received for: Hic sunt dracones", HelperMarshal._delegateCallResult);
+        }
+
+        public static IEnumerable<object[]> ArrayType_TestData()
+        {
+            _objectPrototype ??= new Function("return Object.prototype.toString;");
+            yield return new object[] { _objectPrototype.Call(), "Uint8Array", Uint8Array.From(new byte[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Uint8ClampedArray", Uint8ClampedArray.From(new byte[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Int8Array", Int8Array.From(new sbyte[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Uint16Array", Uint16Array.From(new ushort[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Int16Array", Int16Array.From(new short[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Uint32Array", Uint32Array.From(new uint[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Int32Array", Int32Array.From(new int[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Float32Array", Float32Array.From(new float[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Float64Array", Float64Array.From(new double[10]) };
+            yield return new object[] { _objectPrototype.Call(), "Array", new Array(10) };
+        }
+
+        [Theory]
+        [MemberData(nameof(ArrayType_TestData))]
+        public static void InvokeFunctionAcceptingArrayTypes(Function objectPrototype, string creator, JSObject arrayType )
+        {
+            HelperMarshal._funcActionBufferObjectResultValue = arrayType;
+            Assert.Equal(10, HelperMarshal._funcActionBufferObjectResultValue.Length);
+            Assert.Equal($"[object {creator}]", objectPrototype.Call(HelperMarshal._funcActionBufferObjectResultValue));
+
+            Runtime.InvokeJS($@"
+                var buffer = new {creator}(50);
+                var del = App.call_test_method (""CreateFunctionAccepting{creator}"", [  ]);
+                var setAction = del(buffer);
+                setAction(buffer);
+            ");
+
+            Assert.Equal(50, HelperMarshal._funcActionBufferObjectResultValue.Length);
+            Assert.Equal(HelperMarshal._funcActionBufferObjectResultValue.Length, HelperMarshal._funcActionBufferResultLengthValue);
+            Assert.Equal($"[object {creator}]", objectPrototype.Call(HelperMarshal._funcActionBufferObjectResultValue));
+        }
+    }
+}
index 968d386..8c2e8fe 100644 (file)
@@ -128,16 +128,6 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
             _jsAddAsFunctionResult = (int)func.Call(null, 20, 30);
         }
 
-        internal static int _functionResultValue;
-        private static Func<int, int, int> CreateFunctionDelegate()
-        {
-            return (a, b) =>
-            {
-                _functionResultValue = a + b;
-                return _functionResultValue;
-            };
-        }
-
         internal static int _intValue;
         private static void InvokeInt(int value)
         {
@@ -382,6 +372,259 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
         {
             return UInt64.MaxValue;
         }
+
+        internal static int _functionResultValue;
+        private static Func<int, int, int> CreateFunctionDelegate()
+        {
+            return (a, b) =>
+            {
+                _functionResultValue = a + b;
+                return _functionResultValue;
+            };
+        }
+
+        internal static int _functionActionResultValue;
+        internal static int _functionActionResultValueOfAction;
+        private static Func<int, int, Action<int,int>> CreateFunctionDelegateWithAction()
+        {
+            return (a, b) =>
+            {
+                _functionActionResultValue = a + b;
+                return (i1, i2) =>
+                {
+                    _functionActionResultValueOfAction = i1 + i2;
+                };
+            };
+        }
+
+        internal static int _actionResultValue;
+        private static Action<int,int> CreateActionDelegate()
+        {
+            return (a1, a2) =>
+            {
+                _actionResultValue = a1 + a2;
+            };
+        }
+
+        private static bool AreEqual(int a, int b)
+        {
+            return a == b;
+        }
+
+        private static string TestString1(string a)
+        {
+            return "Received: " + a;
+        }
+
+        private static void SetTestString1(string a)
+        {
+            _delMethodStringResultValue = a;
+        }
+
+        // Create a method for a delegate.
+        public static void DelegateMethod(string message)
+        {
+            _delMethodResultValue = message;
+        }
+
+        delegate void Del(string message);
+        internal static string _delMethodResultValue;
+        private static Del CreateDelegateMethod()
+        {
+            // Instantiate the delegate.
+            Del handler = DelegateMethod;
+            return handler;
+        }
+
+        delegate string Del2(string message);
+        internal static string _delMethodStringResultValue;
+        private static Del2 CreateDelegateMethodReturnString()
+        {
+            // Instantiate the delegate.
+            Del2 handler = TestString1;
+            return handler;
+        }
+
+        internal static string _delegateCallResult;
+        private static Del CreateDelegateFromAnonymousMethod_VoidString()
+        {
+            // Instantiate the delegate.
+            Del handler = delegate(string name) { _delegateCallResult = $"Notification received for: {name}"; };
+            return handler;
+        }
+
+        private static Del CreateDelegateFromLambda_VoidString()
+        {
+            // Instantiate the delegate.
+            Del handler = (string name) => { _delegateCallResult = $"Notification received for: {name}"; };
+            return handler;
+        }
+
+        public static void DelegateMethod_VoidString(string name) => _delegateCallResult = $"Notification received for: {name}";
+
+        private static Del CreateDelegateFromMethod_VoidString()
+        {
+            // Instantiate the delegate.
+            Del handler = DelegateMethod_VoidString;
+            return handler;
+        }
+
+        private static Action<string> CreateActionT_VoidString()
+            => (string name) => _delegateCallResult = $"Notification received for: {name}";
+
+        static void Hello(string s)
+        {
+            _delegateCallResult += $"  Hello, {s}!";
+        }
+
+        static void GoodMorning(string s)
+        {
+            _delegateCallResult += $"  GoodMorning, {s}!";
+        }
+
+        delegate void CustomDelStr(string s);
+        private static CustomDelStr CreateCustomMultiCastDelegate_VoidString()
+        {
+            CustomDelStr hiDel, mornDel, multiDel;
+            hiDel = Hello;
+            mornDel = GoodMorning;
+            multiDel = hiDel + mornDel;
+
+            return multiDel;
+        }
+
+        private static Action<string> CreateMultiCastAction_VoidString()
+        {
+            Action<string> hiDel, mornDel, multiDel;
+            hiDel = Hello;
+            mornDel = GoodMorning;
+            multiDel = hiDel + mornDel;
+
+            return multiDel;
+        }
+
+        internal static JSObject _funcActionBufferObjectResultValue;
+        internal static int _funcActionBufferResultLengthValue;
+        private static Func<Uint8ClampedArray, Action<Uint8ClampedArray>> CreateFunctionAcceptingUint8ClampedArray()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Uint8Array, Action<Uint8Array>> CreateFunctionAcceptingUint8Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Int8Array, Action<Int8Array>> CreateFunctionAcceptingInt8Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Uint16Array, Action<Uint16Array>> CreateFunctionAcceptingUint16Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Int16Array, Action<Int16Array>> CreateFunctionAcceptingInt16Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Uint32Array, Action<Uint32Array>> CreateFunctionAcceptingUint32Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Int32Array, Action<Int32Array>> CreateFunctionAcceptingInt32Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Float32Array, Action<Float32Array>> CreateFunctionAcceptingFloat32Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Float64Array, Action<Float64Array>> CreateFunctionAcceptingFloat64Array()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
+        private static Func<Array, Action<Array>> CreateFunctionAcceptingArray()
+        {
+            return (buffer) =>
+            {
+                _funcActionBufferObjectResultValue = buffer;
+                return (i1) =>
+                {
+                    _funcActionBufferResultLengthValue = i1.Length;
+                };
+            };
+        }
+
     }
 
     public enum TestEnum : uint {
index e15716f..934673c 100644 (file)
@@ -255,20 +255,6 @@ namespace System.Runtime.InteropServices.JavaScript.Tests
         }
 
         [Fact]
-        public static void MarshalDelegate()
-        {
-            HelperMarshal._object1 = null;
-            Runtime.InvokeJS(@"
-                var funcDelegate = App.call_test_method (""CreateFunctionDelegate"", [  ]);
-                var res = funcDelegate (10, 20);
-                App.call_test_method (""InvokeI32"", [ res, res ]);
-            ");
-
-            Assert.Equal(30, HelperMarshal._functionResultValue);
-            Assert.Equal(60, HelperMarshal._i32Value);
-        }
-
-        [Fact]
         public static void BindStaticMethod()
         {
             HelperMarshal._intValue = 0;
index 34dac47..4617772 100644 (file)
@@ -69,6 +69,7 @@ var BindingSupportLib = {
                        this.mono_wasm_box_primitive = Module.cwrap ('mono_wasm_box_primitive', 'number', ['number', 'number', 'number']);
                        this.mono_wasm_intern_string = Module.cwrap ('mono_wasm_intern_string', 'number', ['number']);
                        this.assembly_get_entry_point = Module.cwrap ('mono_wasm_assembly_get_entry_point', 'number', ['number']);
+                       this.mono_wasm_get_delegate_invoke = Module.cwrap ('mono_wasm_get_delegate_invoke', 'number', ['number']);
 
                        this._box_buffer = Module._malloc(16);
                        this._unbox_buffer = Module._malloc(16);
@@ -780,10 +781,10 @@ var BindingSupportLib = {
                        return this.wasm_get_raw_obj (js_obj.__mono_gchandle__);
                },
 
-               mono_method_get_call_signature: function(method) {
+               mono_method_get_call_signature: function(method, mono_obj) {
                        this.bindings_lazy_init ();
 
-                       return this.call_method (this.get_call_sig, null, "i", [ method ]);
+                       return this.call_method (this.get_call_sig, null, "im", [ method, mono_obj ]);
                },
 
                get_task_and_bind: function (tcs, js_obj) {
@@ -1457,25 +1458,19 @@ var BindingSupportLib = {
                                        throw new Error("The delegate target that is being invoked is no longer available.  Please check if it has been prematurely GC'd.");
                        }
 
-                       var [delegateRoot, argsRoot] = MONO.mono_wasm_new_roots ([this.extract_mono_obj (delegate_obj), undefined]);
+                       var [delegateRoot] = MONO.mono_wasm_new_roots ([this.extract_mono_obj (delegate_obj)]);
                        try {
-                               if (!this.delegate_dynamic_invoke) {
-                                       if (!this.corlib)
-                                               this.corlib = this.assembly_load ("System.Private.CoreLib");
-                                       if (!this.delegate_class)
-                                               this.delegate_class = this.find_class (this.corlib, "System", "Delegate");
-                                       if (!this.delegate_class)
-                                       {
-                                               throw new Error("System.Delegate class can not be resolved.");
-                                       }
-                                       this.delegate_dynamic_invoke = this.find_method (this.delegate_class, "DynamicInvoke", -1);
-                               }
-                               argsRoot.value = this.js_array_to_mono_array (js_args);
-                               if (!this.delegate_dynamic_invoke)
-                                       throw new Error("System.Delegate.DynamicInvoke method can not be resolved.");
-                               return this.call_method (this.delegate_dynamic_invoke, delegateRoot.value, "m", [ argsRoot.value ]);
+                               if (typeof delegate_obj.__mono_delegate_invoke__ === "undefined")
+                                       delegate_obj.__mono_delegate_invoke__ = this.mono_wasm_get_delegate_invoke(delegateRoot.value);
+                               if (!delegate_obj.__mono_delegate_invoke__)
+                                       throw new Error("System.Delegate Invoke method can not be resolved.");
+
+                               if (typeof delegate_obj.__mono_delegate_invoke_sig__ === "undefined")
+                                       delegate_obj.__mono_delegate_invoke_sig__ = Module.mono_method_get_call_signature (delegate_obj.__mono_delegate_invoke__, delegateRoot.value);
+
+                               return this.call_method (delegate_obj.__mono_delegate_invoke__, delegateRoot.value, delegate_obj.__mono_delegate_invoke_sig__, js_args);
                        } finally {
-                               MONO.mono_wasm_release_roots (delegateRoot, argsRoot);
+                               MONO.mono_wasm_release_roots (delegateRoot);
                        }
                },
 
index 9b6d683..c2ed732 100644 (file)
@@ -602,6 +602,12 @@ mono_wasm_assembly_find_method (MonoClass *klass, const char *name, int argument
        return mono_class_get_method_from_name (klass, name, arguments);
 }
 
+EMSCRIPTEN_KEEPALIVE MonoMethod*
+mono_wasm_get_delegate_invoke (MonoObject *delegate)
+{
+       return mono_get_delegate_invoke(mono_object_get_class (delegate));
+}
+
 EMSCRIPTEN_KEEPALIVE MonoObject*
 mono_wasm_box_primitive (MonoClass *klass, void *value, int value_size)
 {