Skip to content

Commit 4730697

Browse files
lewingkjpou1
andauthoredNov 4, 2020
Wasm async entrypoint (#44045)
* [browser][bindings] Add support for calling the async Task entry point. - When the entry point of an assembly is async we need to execute that method and return the Task object - The Task object that is return from the execution of the async entry point will be marshaled to a JavaScript Promise. Co-authored-by: Kenneth Pouncey <kjpou@pt.lu>
1 parent b33cc60 commit 4730697

File tree

4 files changed

+61
-41
lines changed

4 files changed

+61
-41
lines changed
 

‎src/mono/netcore/sample/wasm/console/Program.cs

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

44
using System;
5+
using System.Threading.Tasks;
56

67
public class Test
78
{
8-
public static void Main (string[] args)
9+
10+
public static async Task<int> Main(string[] args)
911
{
10-
Console.WriteLine ("Hello, World!");
12+
await Task.Delay(1);
13+
Console.WriteLine("Hello World!");
14+
for (int i = 0; i < args.Length; i++) {
15+
Console.WriteLine($"args[{i}] = {args[i]}");
16+
}
17+
return args.Length;
1118
}
12-
}
19+
}

‎src/mono/wasm/runtime-test.js

+23-37
Original file line numberDiff line numberDiff line change
@@ -338,17 +338,8 @@ var App = {
338338
fail_exec ("Error: Missing main executable argument.");
339339
return;
340340
}
341-
main_assembly = assembly_load (args[1]);
342-
if (main_assembly == 0) {
343-
fail_exec ("Error: Unable to load main executable '" + args[1] + "'");
344-
return;
345-
}
346-
main_method = assembly_get_entry_point (main_assembly);
347-
if (main_method == 0) {
348-
fail_exec ("Error: Main (string[]) method not found.");
349-
return;
350-
}
351341

342+
main_assembly_name = args[1];
352343
var app_args = string_array_new (args.length - 2);
353344
for (var i = 2; i < args.length; ++i) {
354345
obj_array_set (app_args, i - 2, string_from_js (args [i]));
@@ -365,42 +356,37 @@ var App = {
365356
}
366357
wasm_set_main_args (main_argc, main_argv);
367358

359+
function isThenable (js_obj) {
360+
// When using an external Promise library the Promise.resolve may not be sufficient
361+
// to identify the object as a Promise.
362+
return Promise.resolve (js_obj) === js_obj ||
363+
((typeof js_obj === "object" || typeof js_obj === "function") && typeof js_obj.then === "function")
364+
}
365+
368366
try {
369-
var invoke_args = Module._malloc (4);
370-
Module.setValue (invoke_args, app_args, "i32");
371-
var eh_exc = Module._malloc (4);
372-
Module.setValue (eh_exc, 0, "i32");
373-
var res = runtime_invoke (main_method, 0, invoke_args, eh_exc);
374-
var eh_res = Module.getValue (eh_exc, "i32");
375-
if (eh_res != 0) {
376-
print ("Exception:" + string_get_utf8 (res));
377-
test_exit (1);
367+
// Automatic signature isn't working correctly
368+
let exit_code = Module.mono_call_assembly_entry_point (main_assembly_name, [app_args], "m");
369+
370+
if (isThenable (exit_code))
371+
{
372+
exit_code.then (
373+
(result) => {
374+
test_exit (result);
375+
},
376+
(reason) => {
377+
console.error (reason);
378+
test_exit (1);
379+
});
380+
} else {
381+
test_exit (exit_code);
378382
return;
379383
}
380-
var exit_code = unbox_int (res);
381-
test_exit (exit_code);
382384
} catch (ex) {
383385
print ("JS exception: " + ex);
384386
print (ex.stack);
385387
test_exit (1);
386388
return;
387389
}
388-
389-
/*
390-
// For testing tp/timers etc.
391-
while (true) {
392-
// Sleep by busy waiting
393-
var start = performance.now ();
394-
useconds = 1e6 / 10;
395-
while (performance.now() - start < useconds / 1000) {
396-
// Do nothing.
397-
}
398-
399-
Module.pump_message ();
400-
}
401-
*/
402-
403-
return;
404390
} else {
405391
fail_exec ("Unhandled argument: " + args [0]);
406392
}

‎src/mono/wasm/runtime/driver.c

+27-1
Original file line numberDiff line numberDiff line change
@@ -624,8 +624,34 @@ mono_wasm_assembly_get_entry_point (MonoAssembly *assembly)
624624
return NULL;
625625

626626
mono_domain_ensure_entry_assembly (root_domain, assembly);
627+
method = mono_get_method (image, entry, NULL);
627628

628-
return mono_get_method (image, entry, NULL);
629+
/*
630+
* If the entry point looks like a compiler generated wrapper around
631+
* an async method in the form "<Name>" then try to look up the async method
632+
* "Name" it is wrapping. We do this because the generated sync wrapper will
633+
* call task.GetAwaiter().GetResult() when we actually want to yield
634+
* to the host runtime.
635+
*/
636+
if (mono_method_get_flags (method, NULL) & 0x0800 /* METHOD_ATTRIBUTE_SPECIAL_NAME */) {
637+
const char *name = mono_method_get_name (method);
638+
int name_length = strlen (name);
639+
640+
if ((*name != '<') || (name [name_length - 1] != '>'))
641+
return method;
642+
643+
MonoClass *klass = mono_method_get_class (method);
644+
char *async_name = strdup (name);
645+
646+
async_name [name_length - 1] = '\0';
647+
648+
MonoMethodSignature *sig = mono_method_get_signature (method, image, mono_method_get_token (method));
649+
MonoMethod *async_method = mono_class_get_method_from_name (klass, async_name + 1, mono_signature_get_param_count (sig));
650+
free (async_name);
651+
if (async_method != NULL)
652+
return async_method;
653+
}
654+
return method;
629655
}
630656

631657
EMSCRIPTEN_KEEPALIVE char *

‎src/tests/Common/wasm-test-runner/WasmTestRunner.proj

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
MicrosoftNetCoreAppRuntimePackDir="$(MicrosoftNetCoreAppRuntimePackDir)"
3131
MainAssembly="$(TestAssembly)"
3232
MainJS="$(CORE_ROOT)\runtime-test\runtime-test.js"
33+
ExtraAssemblies="$(CORE_ROOT)\System.Private.Runtime.InteropServices.JavaScript.dll"
3334
AssemblySearchPaths="@(AssemblySearchPaths)"
3435
SkipMissingAssemblies="True"
3536
/>

0 commit comments

Comments
 (0)
Please sign in to comment.