|
1 | 1 | // Licensed to the .NET Foundation under one or more agreements.
|
2 | 2 | // The .NET Foundation licenses this file to you under the MIT license.
|
3 | 3 |
|
| 4 | +using System.Reflection; |
4 | 5 | using System.Runtime.CompilerServices;
|
| 6 | +using System.Threading.Tasks; |
5 | 7 |
|
6 | 8 | namespace System.Runtime.InteropServices.JavaScript
|
7 | 9 | {
|
8 | 10 | // this maps to src\mono\wasm\runtime\corebindings.ts
|
9 | 11 | // the methods are protected from trimming by DynamicDependency on JSFunctionBinding.BindJSFunction
|
10 | 12 | internal static unsafe partial class JavaScriptExports
|
11 | 13 | {
|
| 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 | + |
12 | 92 |
|
13 | 93 | // The JS layer invokes this method when the JS wrapper for a JS owned object
|
14 | 94 | // has been collected by the JS garbage collector
|
|
0 commit comments