Skip to content

Commit 44cb67e

Browse files
pavelsavarakgmaraf
authored
[wasm] marshling call to assembly entrypoint via new interop (#73156)
- added JavaScriptExports.CallEntrypoint replacing legacy mono_bind_assembly_entry_point - moved legacy imports and split RuntimeHelpers - simplified dotnet-legacy.d.ts - set_arg_type on manually written JavaScriptExports calls Co-authored-by: Katelyn Gadd <[email protected]> Co-authored-by: Marek Fišera <[email protected]>
1 parent 29992de commit 44cb67e

27 files changed

+478
-484
lines changed

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Interop/JavaScriptExports.cs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,94 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Reflection;
45
using System.Runtime.CompilerServices;
6+
using System.Threading.Tasks;
57

68
namespace System.Runtime.InteropServices.JavaScript
79
{
810
// this maps to src\mono\wasm\runtime\corebindings.ts
911
// the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
1012
internal static unsafe partial class JavaScriptExports
1113
{
14+
[MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
15+
// the marshaled signature is:
16+
// Task<int>? CallEntrypoint(MonoMethod* entrypointPtr, string[] args)
17+
public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
18+
{
19+
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
20+
ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // initialized by caller in alloc_stack_frame()
21+
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller
22+
ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // initialized and set by caller
23+
try
24+
{
25+
arg_1.ToManaged(out IntPtr entrypointPtr);
26+
if (entrypointPtr == IntPtr.Zero)
27+
{
28+
throw new MissingMethodException("Missing entrypoint");
29+
}
30+
31+
RuntimeMethodHandle methodHandle = JSHostImplementation.GetMethodHandleFromIntPtr(entrypointPtr);
32+
// this would not work for generic types. But Main() could not be generic, so we are fine.
33+
MethodInfo? method = MethodBase.GetMethodFromHandle(methodHandle) as MethodInfo;
34+
if (method == null)
35+
{
36+
throw new InvalidProgramException("Can't resolve entrypoint handle");
37+
}
38+
39+
arg_2.ToManaged(out string?[]? args);
40+
object[] argsToPass = System.Array.Empty<object>();
41+
Task<int>? result = null;
42+
var parameterInfos = method.GetParameters();
43+
if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(string[]))
44+
{
45+
argsToPass = new object[] { args ?? System.Array.Empty<string>() };
46+
}
47+
if (method.ReturnType == typeof(void))
48+
{
49+
method.Invoke(null, argsToPass);
50+
}
51+
else if (method.ReturnType == typeof(int))
52+
{
53+
int intResult = (int)method.Invoke(null, argsToPass)!;
54+
result = Task.FromResult(intResult);
55+
}
56+
else if (method.ReturnType == typeof(Task))
57+
{
58+
Task methodResult = (Task)method.Invoke(null, argsToPass)!;
59+
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
60+
result = tcs.Task;
61+
methodResult.ContinueWith((t) =>
62+
{
63+
if (t.IsFaulted)
64+
{
65+
tcs.SetException(t.Exception!);
66+
}
67+
else
68+
{
69+
tcs.SetResult(0);
70+
}
71+
}, TaskScheduler.Default);
72+
}
73+
else if (method.ReturnType == typeof(Task<int>))
74+
{
75+
result = (Task<int>)method.Invoke(null, argsToPass)!;
76+
}
77+
else
78+
{
79+
throw new InvalidProgramException($"Return type '{method.ReturnType.FullName}' from main method in not supported");
80+
}
81+
arg_result.ToJS(result, (ref JSMarshalerArgument arg, int value) =>
82+
{
83+
arg.ToJS(value);
84+
});
85+
}
86+
catch (Exception ex)
87+
{
88+
arg_exc.ToJS(ex);
89+
}
90+
}
91+
1292

1393
// The JS layer invokes this method when the JS wrapper for a JS owned object
1494
// has been collected by the JS garbage collector

src/libraries/System.Runtime.InteropServices.JavaScript/src/System/Runtime/InteropServices/JavaScript/Marshaling/JSMarshalerArgument.Task.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,9 @@ static void MarshalResult(ref JSMarshalerArgument arg, object? taskResult)
175175
/// Implementation of the argument marshaling.
176176
/// It's used by JSImport code generator and should not be used by developers in source code.
177177
/// </summary>
178-
public void ToJS(Task value)
178+
public void ToJS(Task? value)
179179
{
180-
Task task = value;
180+
Task? task = value;
181181

182182
if (task == null)
183183
{

src/mono/wasm/runtime/debug.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
import Configuration from "consts:configuration";
5-
import { INTERNAL, Module, MONO, runtimeHelpers } from "./imports";
5+
import { INTERNAL, Module, runtimeHelpers } from "./imports";
66
import { toBase64StringImpl } from "./base64";
77
import cwraps from "./cwraps";
88
import { VoidPtr, CharPtr } from "./types/emscripten";
9+
import { MONO } from "./net6-legacy/imports";
910
const commands_received: any = new Map<number, CommandResponse>();
1011
const wasm_func_map = new Map<number, string>();
1112
commands_received.remove = function (key: number): CommandResponse { const value = this.get(key); this.delete(key); return value; };
@@ -34,7 +35,7 @@ regexes.push(/(?<replaceSection>[a-z]+:\/\/[^ )]*:wasm-function\[(?<funcNum>\d+)
3435
regexes.push(/(?<replaceSection><[^ >]+>[.:]wasm-function\[(?<funcNum>[0-9]+)\])/);
3536

3637
export function mono_wasm_runtime_ready(): void {
37-
runtimeHelpers.mono_wasm_runtime_is_ready = true;
38+
INTERNAL.mono_wasm_runtime_is_ready = runtimeHelpers.mono_wasm_runtime_is_ready = true;
3839

3940
// FIXME: where should this go?
4041
_next_call_function_res_id = 0;

0 commit comments

Comments
 (0)