From 25c207351c4f57cf2daa98caaf327a8b8d83edb8 Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Fri, 10 Dec 2021 17:13:11 +0100 Subject: [PATCH] [wasm] Decrease the startup ceremony (#62587) * new API MONO.mono_run_main_and_exit and MONO.mono_run_main * default onAbort * console-v8-cjs sample * reduced setup ceremony --- .../sample/wasm/console-v8-cjs/Program.cs | 18 +++++++++ .../Wasm.Console.V8.CJS.Sample.csproj | 15 +++++++ src/mono/sample/wasm/console-v8-cjs/main.js | 11 +++++ src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js | 6 ++- src/mono/wasm/runtime/dotnet.d.ts | 12 +++++- src/mono/wasm/runtime/es6/dotnet.es6.pre.js | 5 +++ src/mono/wasm/runtime/export-types.ts | 2 +- src/mono/wasm/runtime/exports.ts | 13 +++++- src/mono/wasm/runtime/method-calls.ts | 23 +++++------ src/mono/wasm/runtime/run.ts | 40 +++++++++++++++++++ src/mono/wasm/runtime/types/emscripten.ts | 1 + src/mono/wasm/test-main.js | 6 +-- 12 files changed, 128 insertions(+), 24 deletions(-) create mode 100644 src/mono/sample/wasm/console-v8-cjs/Program.cs create mode 100644 src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj create mode 100644 src/mono/sample/wasm/console-v8-cjs/main.js create mode 100644 src/mono/wasm/runtime/run.ts diff --git a/src/mono/sample/wasm/console-v8-cjs/Program.cs b/src/mono/sample/wasm/console-v8-cjs/Program.cs new file mode 100644 index 00000000000000..d5ea012a33c792 --- /dev/null +++ b/src/mono/sample/wasm/console-v8-cjs/Program.cs @@ -0,0 +1,18 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Threading.Tasks; + +public class Test +{ + public static async Task Main(string[] args) + { + await Task.Delay(1); + Console.WriteLine("Hello World!"); + for (int i = 0; i < args.Length; i++) { + Console.WriteLine($"args[{i}] = {args[i]}"); + } + return args.Length; + } +} diff --git a/src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj b/src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj new file mode 100644 index 00000000000000..0b28f10d49c3a9 --- /dev/null +++ b/src/mono/sample/wasm/console-v8-cjs/Wasm.Console.V8.CJS.Sample.csproj @@ -0,0 +1,15 @@ + + + true + main.js + true + false + + + + <_SampleProject>Wasm.Console.V8.CJS.Sample.csproj + <_SampleAssembly>Wasm.Console.V8.CJS.Sample.dll + + + + diff --git a/src/mono/sample/wasm/console-v8-cjs/main.js b/src/mono/sample/wasm/console-v8-cjs/main.js new file mode 100644 index 00000000000000..5cca1845b8a259 --- /dev/null +++ b/src/mono/sample/wasm/console-v8-cjs/main.js @@ -0,0 +1,11 @@ +load("./dotnet.js") + +const dllName = "Wasm.Console.V8.CJS.Sample.dll"; +const app_args = Array.from(arguments); + +async function main() { + const { MONO } = await createDotnetRuntime(); + await MONO.mono_run_main_and_exit(dllName, app_args); +} + +main(); \ No newline at end of file diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js index 404b3b3734f57a..6b55ff1360bee7 100644 --- a/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js +++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js @@ -6,8 +6,12 @@ if (ENVIRONMENT_IS_GLOBAL) { globalThis.Module.ready = Module.ready; Module = createDotnetRuntime = globalThis.Module; } +else if (typeof createDotnetRuntime === "object") { + Module = { ready: Module.ready, __undefinedConfig: Object.keys(createDotnetRuntime).length === 1 }; + Object.assign(Module, createDotnetRuntime); + createDotnetRuntime = Module; +} else if (typeof createDotnetRuntime === "function") { - ENVIRONMENT_IS_GLOBAL = false; Module = { ready: Module.ready }; const extension = createDotnetRuntime({ MONO, BINDING, INTERNAL, Module }) if (extension.ready) { diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts index cc4341a54dd3cd..0ee7f4c80e16d8 100644 --- a/src/mono/wasm/runtime/dotnet.d.ts +++ b/src/mono/wasm/runtime/dotnet.d.ts @@ -47,6 +47,9 @@ declare interface EmscriptenModule { preInit?: (() => any)[]; preRun?: (() => any)[]; postRun?: (() => any)[]; + onAbort?: { + (error: any): void; + }; onRuntimeInitialized?: () => any; instantiateWasm: (imports: any, successCallback: Function) => any; } @@ -258,7 +261,7 @@ declare function unbox_mono_obj(mono_obj: MonoObject): any; declare function mono_array_to_js_array(mono_array: MonoArray): any[] | null; declare function mono_bind_static_method(fqn: string, signature?: ArgsMarshalString): Function; -declare function mono_call_assembly_entry_point(assembly: string, args: any[], signature: ArgsMarshalString): any; +declare function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: ArgsMarshalString): number; declare function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr; @@ -282,6 +285,9 @@ declare function getI64(offset: _MemOffset): number; declare function getF32(offset: _MemOffset): number; declare function getF64(offset: _MemOffset): number; +declare function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise; +declare function mono_run_main(main_assembly_name: string, args: string[]): Promise; + declare const MONO: { mono_wasm_setenv: typeof mono_wasm_setenv; mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap; @@ -293,6 +299,8 @@ declare const MONO: { mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer; mono_wasm_new_root: typeof mono_wasm_new_root; mono_wasm_release_roots: typeof mono_wasm_release_roots; + mono_run_main: typeof mono_run_main; + mono_run_main_and_exit: typeof mono_run_main_and_exit; mono_wasm_add_assembly: (name: string, data: VoidPtr, size: number) => number; mono_wasm_load_runtime: (unused: string, debug_level: number) => void; config: MonoConfig | MonoConfigError; @@ -342,7 +350,7 @@ interface DotnetPublicAPI { }; } -declare function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise; +declare function createDotnetRuntime(moduleFactory: DotnetModuleConfig | ((api: DotnetPublicAPI) => DotnetModuleConfig)): Promise; declare type CreateDotnetRuntimeType = typeof createDotnetRuntime; declare global { function getDotnetRuntime(runtimeId: number): DotnetPublicAPI | undefined; diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.pre.js b/src/mono/wasm/runtime/es6/dotnet.es6.pre.js index 02d8cffb481854..b9b456f88e4e54 100644 --- a/src/mono/wasm/runtime/es6/dotnet.es6.pre.js +++ b/src/mono/wasm/runtime/es6/dotnet.es6.pre.js @@ -9,6 +9,11 @@ if (typeof createDotnetRuntime === "function") { Object.assign(Module, extension); createDotnetRuntime = Module; } +else if (typeof createDotnetRuntime === "object") { + Module = { ready: Module.ready, __undefinedConfig: Object.keys(createDotnetRuntime).length === 1 }; + Object.assign(Module, createDotnetRuntime); + createDotnetRuntime = Module; +} else { throw new Error("MONO_WASM: Can't use moduleFactory callback of createDotnetRuntime function.") } diff --git a/src/mono/wasm/runtime/export-types.ts b/src/mono/wasm/runtime/export-types.ts index 77bca1f53b70db..3cb8c0df090773 100644 --- a/src/mono/wasm/runtime/export-types.ts +++ b/src/mono/wasm/runtime/export-types.ts @@ -9,7 +9,7 @@ import { EmscriptenModule, VoidPtr } from "./types/emscripten"; // this files has all public exports from the dotnet.js module // ----------------------------------------------------------- -declare function createDotnetRuntime(moduleFactory: (api: DotnetPublicAPI) => DotnetModuleConfig): Promise; +declare function createDotnetRuntime(moduleFactory: DotnetModuleConfig | ((api: DotnetPublicAPI) => DotnetModuleConfig)): Promise; declare type CreateDotnetRuntimeType = typeof createDotnetRuntime; // Here, declare things that go in the global namespace, or augment existing declarations in the global namespace diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts index c4a33574c5870c..911d0382afa049 100644 --- a/src/mono/wasm/runtime/exports.ts +++ b/src/mono/wasm/runtime/exports.ts @@ -31,7 +31,6 @@ import { mono_load_runtime_and_bcl_args, mono_wasm_load_config, mono_wasm_setenv, mono_wasm_set_runtime_options, mono_wasm_load_data_archive, mono_wasm_asm_loaded, - mono_wasm_set_main_args, mono_wasm_pre_init, mono_wasm_runtime_is_initialized, mono_wasm_on_runtime_initialized @@ -68,6 +67,7 @@ import { import { create_weak_ref } from "./weak-ref"; import { fetch_like, readAsync_like } from "./polyfills"; import { EmscriptenModule } from "./types/emscripten"; +import { mono_on_abort, mono_run_main, mono_run_main_and_exit } from "./run"; const MONO = { // current "public" MONO API @@ -81,6 +81,8 @@ const MONO = { mono_wasm_new_root_buffer, mono_wasm_new_root, mono_wasm_release_roots, + mono_run_main, + mono_run_main_and_exit, // for Blazor's future! mono_wasm_add_assembly: cwraps.mono_wasm_add_assembly, @@ -157,6 +159,10 @@ function initializeImportsAndExports( Configuration } }; + if (exports.module.__undefinedConfig) { + module.disableDotnet6Compatibility = true; + module.configSrc = "./mono-config.json"; + } // these could be overriden on DotnetModuleConfig if (!module.preInit) { @@ -271,6 +277,10 @@ function initializeImportsAndExports( }); } + if (!module.onAbort) { + module.onAbort = () => mono_on_abort; + } + // this code makes it possible to find dotnet runtime on a page via global namespace, even when there are multiple runtimes at the same time let list: RuntimeList; if (!globalThisAny.getDotnetRuntime) { @@ -345,7 +355,6 @@ const INTERNAL: any = { mono_wasm_enable_on_demand_gc: cwraps.mono_wasm_enable_on_demand_gc, mono_profiler_init_aot: cwraps.mono_profiler_init_aot, mono_wasm_set_runtime_options, - mono_wasm_set_main_args: mono_wasm_set_main_args, mono_wasm_exec_regression: cwraps.mono_wasm_exec_regression, mono_method_resolve,//MarshalTests.cs mono_bind_static_method,// MarshalTests.cs diff --git a/src/mono/wasm/runtime/method-calls.ts b/src/mono/wasm/runtime/method-calls.ts index 64d4d06eb8dcb5..698da9121555c5 100644 --- a/src/mono/wasm/runtime/method-calls.ts +++ b/src/mono/wasm/runtime/method-calls.ts @@ -261,7 +261,7 @@ export function mono_bind_static_method(fqn: string, signature?: ArgsMarshalStri return mono_bind_method(method, null, signature, fqn); } -export function mono_bind_assembly_entry_point(assembly: string, signature: ArgsMarshalString): Function { +export function mono_bind_assembly_entry_point(assembly: string, signature?: ArgsMarshalString): Function { bindings_lazy_init();// TODO remove this once Blazor does better startup const asm = cwraps.mono_wasm_assembly_load(assembly); @@ -272,23 +272,20 @@ export function mono_bind_assembly_entry_point(assembly: string, signature: Args if (!method) throw new Error("Could not find entry point for assembly: " + assembly); - if (typeof signature === "undefined") + if (!signature) signature = mono_method_get_call_signature(method); - return function (...args: any[]) { - try { - if (args.length > 0 && Array.isArray(args[0])) - args[0] = js_array_to_mono_array(args[0], true, false); - - const result = call_method(method, undefined, signature, args); - return Promise.resolve(result); - } catch (error) { - return Promise.reject(error); - } + return async function (...args: any[]) { + if (args.length > 0 && Array.isArray(args[0])) + args[0] = js_array_to_mono_array(args[0], true, false); + return call_method(method, undefined, signature!, args); }; } -export function mono_call_assembly_entry_point(assembly: string, args: any[], signature: ArgsMarshalString): any { +export function mono_call_assembly_entry_point(assembly: string, args?: any[], signature?: ArgsMarshalString): number { + if (!args) { + args = [[]]; + } return mono_bind_assembly_entry_point(assembly, signature)(...args); } diff --git a/src/mono/wasm/runtime/run.ts b/src/mono/wasm/runtime/run.ts new file mode 100644 index 00000000000000..738873e360ffff --- /dev/null +++ b/src/mono/wasm/runtime/run.ts @@ -0,0 +1,40 @@ +import { Module } from "./imports"; +import { mono_call_assembly_entry_point } from "./method-calls"; +import { mono_wasm_set_main_args, runtime_is_initialized_reject } from "./startup"; + + +export async function mono_run_main_and_exit(main_assembly_name: string, args: string[]): Promise { + try { + const result = await mono_run_main(main_assembly_name, args); + set_exit_code(result); + } catch (error) { + set_exit_code(1, error); + } +} + +export async function mono_run_main(main_assembly_name: string, args: string[]): Promise { + mono_wasm_set_main_args(main_assembly_name, args); + return mono_call_assembly_entry_point(main_assembly_name, [args], "m"); +} + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export function mono_on_abort(error: any): void { + runtime_is_initialized_reject(error); + set_exit_code(1, error); +} + +function set_exit_code(exit_code: number, reason?: any) { + if (reason) { + Module.printErr(reason.toString()); + if (reason.stack) { + Module.printErr(reason.stack); + } + } + const globalThisAny: any = globalThis; + if (typeof globalThisAny.exit === "function") { + globalThisAny.exit(exit_code); + } + else if (typeof globalThisAny.quit === "function") { + globalThisAny.quit(exit_code); + } +} diff --git a/src/mono/wasm/runtime/types/emscripten.ts b/src/mono/wasm/runtime/types/emscripten.ts index 88fe9548bced4b..0866d056289a66 100644 --- a/src/mono/wasm/runtime/types/emscripten.ts +++ b/src/mono/wasm/runtime/types/emscripten.ts @@ -56,6 +56,7 @@ export declare interface EmscriptenModule { preInit?: (() => any)[]; preRun?: (() => any)[]; postRun?: (() => any)[]; + onAbort?: { (error: any): void }; onRuntimeInitialized?: () => any; instantiateWasm: (imports: any, successCallback: Function) => any; } diff --git a/src/mono/wasm/test-main.js b/src/mono/wasm/test-main.js index 6aaca16861fb43..58f4a4d1f22453 100644 --- a/src/mono/wasm/test-main.js +++ b/src/mono/wasm/test-main.js @@ -208,13 +208,9 @@ const App = { return; } try { - const main_assembly_name = processedArguments.applicationArgs[1]; const app_args = processedArguments.applicationArgs.slice(2); - INTERNAL.mono_wasm_set_main_args(main_assembly_name, app_args); - - // Automatic signature isn't working correctly - const result = await BINDING.call_assembly_entry_point(main_assembly_name, [app_args], "m"); + const result = await MONO.mono_run_main(main_assembly_name, app_args); set_exit_code(result); } catch (error) { set_exit_code(1, error);