Skip to content

Commit

Permalink
[wasm] marshling call to assembly entrypoint via new interop (#73156)
Browse files Browse the repository at this point in the history
- 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 <kg@luminance.org>
Co-authored-by: Marek Fišera <mara@neptuo.com>
  • Loading branch information
3 people authored Aug 4, 2022
1 parent 29992de commit 44cb67e
Show file tree
Hide file tree
Showing 27 changed files with 478 additions and 484 deletions.
Original file line number Diff line number Diff line change
@@ -1,14 +1,94 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace System.Runtime.InteropServices.JavaScript
{
// this maps to src\mono\wasm\runtime\corebindings.ts
// the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
internal static unsafe partial class JavaScriptExports
{
[MethodImpl(MethodImplOptions.NoInlining)] // https://github.com/dotnet/runtime/issues/71425
// the marshaled signature is:
// Task<int>? CallEntrypoint(MonoMethod* entrypointPtr, string[] args)
public static void CallEntrypoint(JSMarshalerArgument* arguments_buffer)
{
ref JSMarshalerArgument arg_exc = ref arguments_buffer[0]; // initialized by caller in alloc_stack_frame()
ref JSMarshalerArgument arg_result = ref arguments_buffer[1]; // initialized by caller in alloc_stack_frame()
ref JSMarshalerArgument arg_1 = ref arguments_buffer[2]; // initialized and set by caller
ref JSMarshalerArgument arg_2 = ref arguments_buffer[3]; // initialized and set by caller
try
{
arg_1.ToManaged(out IntPtr entrypointPtr);
if (entrypointPtr == IntPtr.Zero)
{
throw new MissingMethodException("Missing entrypoint");
}

RuntimeMethodHandle methodHandle = JSHostImplementation.GetMethodHandleFromIntPtr(entrypointPtr);
// this would not work for generic types. But Main() could not be generic, so we are fine.
MethodInfo? method = MethodBase.GetMethodFromHandle(methodHandle) as MethodInfo;
if (method == null)
{
throw new InvalidProgramException("Can't resolve entrypoint handle");
}

arg_2.ToManaged(out string?[]? args);
object[] argsToPass = System.Array.Empty<object>();
Task<int>? result = null;
var parameterInfos = method.GetParameters();
if (parameterInfos.Length > 0 && parameterInfos[0].ParameterType == typeof(string[]))
{
argsToPass = new object[] { args ?? System.Array.Empty<string>() };
}
if (method.ReturnType == typeof(void))
{
method.Invoke(null, argsToPass);
}
else if (method.ReturnType == typeof(int))
{
int intResult = (int)method.Invoke(null, argsToPass)!;
result = Task.FromResult(intResult);
}
else if (method.ReturnType == typeof(Task))
{
Task methodResult = (Task)method.Invoke(null, argsToPass)!;
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
result = tcs.Task;
methodResult.ContinueWith((t) =>
{
if (t.IsFaulted)
{
tcs.SetException(t.Exception!);
}
else
{
tcs.SetResult(0);
}
}, TaskScheduler.Default);
}
else if (method.ReturnType == typeof(Task<int>))
{
result = (Task<int>)method.Invoke(null, argsToPass)!;
}
else
{
throw new InvalidProgramException($"Return type '{method.ReturnType.FullName}' from main method in not supported");
}
arg_result.ToJS(result, (ref JSMarshalerArgument arg, int value) =>
{
arg.ToJS(value);
});
}
catch (Exception ex)
{
arg_exc.ToJS(ex);
}
}


// The JS layer invokes this method when the JS wrapper for a JS owned object
// has been collected by the JS garbage collector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,9 @@ static void MarshalResult(ref JSMarshalerArgument arg, object? taskResult)
/// Implementation of the argument marshaling.
/// It's used by JSImport code generator and should not be used by developers in source code.
/// </summary>
public void ToJS(Task value)
public void ToJS(Task? value)
{
Task task = value;
Task? task = value;

if (task == null)
{
Expand Down
5 changes: 3 additions & 2 deletions src/mono/wasm/runtime/debug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
// The .NET Foundation licenses this file to you under the MIT license.

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

export function mono_wasm_runtime_ready(): void {
runtimeHelpers.mono_wasm_runtime_is_ready = true;
INTERNAL.mono_wasm_runtime_is_ready = runtimeHelpers.mono_wasm_runtime_is_ready = true;

// FIXME: where should this go?
_next_call_function_res_id = 0;
Expand Down
Loading

0 comments on commit 44cb67e

Please sign in to comment.