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;
<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. -->
--- /dev/null
+// 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));
+ }
+ }
+}
_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)
{
{
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 {
}
[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;
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);
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) {
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);
}
},
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)
{