diff --git a/src/mono/browser/runtime/dotnet.d.ts b/src/mono/browser/runtime/dotnet.d.ts index 09a1375726152e..0d8f2833e36748 100644 --- a/src/mono/browser/runtime/dotnet.d.ts +++ b/src/mono/browser/runtime/dotnet.d.ts @@ -54,26 +54,79 @@ type InstantiateWasmCallBack = (imports: WebAssembly.Imports, successCallback: I declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; interface DotnetHostBuilder { + /** + * @param config default values for the runtime configuration. It will be merged with the default values. + * Note that if you provide resources and don't provide custom configSrc URL, the blazor.boot.json will be downloaded and applied by default. + */ withConfig(config: MonoConfig): DotnetHostBuilder; + /** + * @param configSrc URL to the configuration file. ./blazor.boot.json is a default config file location. + */ withConfigSrc(configSrc: string): DotnetHostBuilder; + /** + * "command line" arguments for the Main() method. + * @param args + */ withApplicationArguments(...args: string[]): DotnetHostBuilder; + /** + * Sets the environment variable for the "process" + */ withEnvironmentVariable(name: string, value: string): DotnetHostBuilder; + /** + * Sets the environment variables for the "process" + */ withEnvironmentVariables(variables: { [i: string]: string; }): DotnetHostBuilder; + /** + * Sets the "current directory" for the "process" on the virtual file system. + */ withVirtualWorkingDirectory(vfsPath: string): DotnetHostBuilder; + /** + * @param enabled if "true", writes diagnostic messages during runtime startup and execution to the browser console. + */ withDiagnosticTracing(enabled: boolean): DotnetHostBuilder; + /** + * @param level + * level > 0 enables debugging and sets the logging level to debug + * level == 0 disables debugging and enables interpreter optimizations + * level < 0 enables debugging and disables debug logging. + */ withDebugging(level: number): DotnetHostBuilder; + /** + * @param mainAssemblyName Sets the name of the assembly with the Main() method. Default is the same as the .csproj name. + */ withMainAssembly(mainAssemblyName: string): DotnetHostBuilder; + /** + * Supply "command line" arguments for the Main() method from browser query arguments named "arg". Eg. `index.html?arg=A&arg=B&arg=C`. + * @param args + */ withApplicationArgumentsFromQuery(): DotnetHostBuilder; + /** + * Sets application environment, such as "Development", "Staging", "Production", etc. + */ withApplicationEnvironment(applicationEnvironment?: string): DotnetHostBuilder; + /** + * Sets application culture. This is a name specified in the BCP 47 format. See https://tools.ietf.org/html/bcp47 + */ withApplicationCulture(applicationCulture?: string): DotnetHostBuilder; /** * Overrides the built-in boot resource loading mechanism so that boot resources can be fetched * from a custom source, such as an external CDN. */ withResourceLoader(loadBootResource?: LoadBootResourceCallback): DotnetHostBuilder; + /** + * Starts the runtime and returns promise of the API object. + */ create(): Promise; + /** + * Runs the Main() method of the application and exits the runtime. + * You can provide "command line" arguments for the Main() method using + * - dotnet.withApplicationArguments(["A", "B", "C"]) + * - dotnet.withApplicationArgumentsFromQuery() + * Note: after the runtime exits, it would reject all further calls to the API. + * You can use runMain() if you want to keep the runtime alive. + */ run(): Promise; } type MonoConfig = { @@ -281,7 +334,7 @@ interface AssetEntry { } type SingleAssetBehaviors = /** - * The binary of the dotnet runtime. + * The binary of the .NET runtime. */ "dotnetwasm" /** @@ -373,45 +426,179 @@ type DotnetModuleConfig = { exports?: string[]; } & Partial; type APIType = { - runMain: (mainAssemblyName: string, args?: string[]) => Promise; - runMainAndExit: (mainAssemblyName: string, args?: string[]) => Promise; + /** + * Runs the Main() method of the application. + * Note: this will keep the .NET runtime alive and the APIs will be available for further calls. + * @param mainAssemblyName name of the assembly with the Main() method. Optional. Default is the same as the .csproj name. + * @param args command line arguments for the Main() method. Optional. + * @returns exit code of the Main() method. + */ + runMain: (mainAssemblyName?: string, args?: string[]) => Promise; + /** + * Runs the Main() method of the application and exits the runtime. + * Note: after the runtime exits, it would reject all further calls to the API. + * @param mainAssemblyName name of the assembly with the Main() method. Optional. Default is the same as the .csproj name. + * @param args command line arguments for the Main() method. Optional. + * @returns exit code of the Main() method. + */ + runMainAndExit: (mainAssemblyName?: string, args?: string[]) => Promise; + /** + * Sets the environment variable for the "process" + * @param name + * @param value + */ setEnvironmentVariable: (name: string, value: string) => void; + /** + * Returns the [JSExport] methods of the assembly with the given name + * @param assemblyName + */ getAssemblyExports(assemblyName: string): Promise; + /** + * Provides functions which could be imported by the managed code using [JSImport] + * @param moduleName maps to the second parameter of [JSImport] + * @param moduleImports object with functions which could be imported by the managed code. The keys map to the first parameter of [JSImport] + */ setModuleImports(moduleName: string, moduleImports: any): void; + /** + * Returns the configuration object used to start the runtime. + */ getConfig: () => MonoConfig; + /** + * Executes scripts which were loaded during runtime bootstrap. + * You can register the scripts using MonoConfig.resources.modulesAfterConfigLoaded and MonoConfig.resources.modulesAfterRuntimeReady. + */ invokeLibraryInitializers: (functionName: string, args: any[]) => Promise; + /** + * Writes to the WASM linear memory + */ setHeapB32: (offset: NativePointer, value: number | boolean) => void; + /** + * Writes to the WASM linear memory + */ setHeapU8: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapU16: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapU32: (offset: NativePointer, value: NativePointer | number) => void; + /** + * Writes to the WASM linear memory + */ setHeapI8: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapI16: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapI32: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapI52: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapU52: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapI64Big: (offset: NativePointer, value: bigint) => void; + /** + * Writes to the WASM linear memory + */ setHeapF32: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ setHeapF64: (offset: NativePointer, value: number) => void; + /** + * Reads from the WASM linear memory + */ getHeapB32: (offset: NativePointer) => boolean; + /** + * Reads from the WASM linear memory + */ getHeapU8: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapU16: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapU32: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapI8: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapI16: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapI32: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapI52: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapU52: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapI64Big: (offset: NativePointer) => bigint; + /** + * Reads from the WASM linear memory + */ getHeapF32: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ getHeapF64: (offset: NativePointer) => number; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewI8: () => Int8Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewI16: () => Int16Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewI32: () => Int32Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewI64Big: () => BigInt64Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewU8: () => Uint8Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewU16: () => Uint16Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewU32: () => Uint32Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewF32: () => Float32Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ localHeapViewF64: () => Float64Array; }; type RuntimeAPI = { @@ -425,7 +612,13 @@ type RuntimeAPI = { }; } & APIType; type ModuleAPI = { + /** + * The builder for the .NET runtime. + */ dotnet: DotnetHostBuilder; + /** + * Terminates the runtime "process" and reject all further calls to the API. + */ exit: (code: number, reason?: any) => void; }; type CreateDotnetRuntimeType = (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) => Promise; diff --git a/src/mono/browser/runtime/gc-handles.ts b/src/mono/browser/runtime/gc-handles.ts index 3e56b387ce8d5c..6aab770b7b4493 100644 --- a/src/mono/browser/runtime/gc-handles.ts +++ b/src/mono/browser/runtime/gc-handles.ts @@ -167,7 +167,7 @@ export function assert_not_disposed(result: any): GCHandle { } function _js_owned_object_finalized(gc_handle: GCHandle): void { - if (loaderHelpers.is_exited()) { + if (!loaderHelpers.is_runtime_running()) { // We're shutting down, so don't bother doing anything else. return; } diff --git a/src/mono/browser/runtime/globals.ts b/src/mono/browser/runtime/globals.ts index 702bb3fc94d8c1..bf7b5a01a997b9 100644 --- a/src/mono/browser/runtime/globals.ts +++ b/src/mono/browser/runtime/globals.ts @@ -10,6 +10,7 @@ import gitHash from "consts:gitHash"; import { RuntimeAPI } from "./types/index"; import type { GlobalObjects, EmscriptenInternals, RuntimeHelpers, LoaderHelpers, DotnetModuleInternal, PromiseAndController } from "./types/internal"; +import { mono_log_error } from "./logging"; // these are our public API (except internal) export let Module: DotnetModuleInternal; @@ -104,5 +105,6 @@ export function mono_assert(condition: unknown, messageFactory: string | (() => ? messageFactory() : messageFactory); const error = new Error(message); + mono_log_error(message, error); runtimeHelpers.nativeAbort(error); } diff --git a/src/mono/browser/runtime/loader/exit.ts b/src/mono/browser/runtime/loader/exit.ts index 7cf42e05ec0903..ab6f1d024179ad 100644 --- a/src/mono/browser/runtime/loader/exit.ts +++ b/src/mono/browser/runtime/loader/exit.ts @@ -13,8 +13,8 @@ export function is_runtime_running() { } export function assert_runtime_running() { - mono_assert(runtimeHelpers.runtimeReady, "mono runtime didn't start yet"); - mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `mono runtime already exited with ${loaderHelpers.exitCode} ${loaderHelpers.exitReason}`); + mono_assert(runtimeHelpers.runtimeReady, ".NET runtime didn't start yet. Please call dotnet.create() first."); + mono_assert(!loaderHelpers.assertAfterExit || !is_exited(), () => `.NET runtime already exited with ${loaderHelpers.exitCode} ${loaderHelpers.exitReason}. You can use runtime.runMain() which doesn't exit the runtime.`); } export function register_exit_handlers() { diff --git a/src/mono/browser/runtime/loader/globals.ts b/src/mono/browser/runtime/loader/globals.ts index 386624b119eb4d..5921596bbc315a 100644 --- a/src/mono/browser/runtime/loader/globals.ts +++ b/src/mono/browser/runtime/loader/globals.ts @@ -13,7 +13,7 @@ import type { MonoConfig, RuntimeAPI } from "../types"; import { assert_runtime_running, is_exited, is_runtime_running, mono_exit } from "./exit"; import { assertIsControllablePromise, createPromiseController, getPromiseController } from "./promise-controller"; import { mono_download_assets, resolve_single_asset_path, retrieve_asset_download } from "./assets"; -import { mono_set_thread_name, setup_proxy_console } from "./logging"; +import { mono_log_error, mono_set_thread_name, setup_proxy_console } from "./logging"; import { invokeLibraryInitializers } from "./libraryInitializers"; import { deep_merge_config, hasDebuggingEnabled } from "./config"; import { logDownloadStatsToConsole, purgeUnusedCacheEntriesAsync } from "./assetsCache"; @@ -139,5 +139,6 @@ export function mono_assert(condition: unknown, messageFactory: string | (() => ? messageFactory() : messageFactory); const error = new Error(message); + mono_log_error(message, error); runtimeHelpers.nativeAbort(error); } \ No newline at end of file diff --git a/src/mono/browser/runtime/loader/run.ts b/src/mono/browser/runtime/loader/run.ts index 40ea4cb19ada7b..b1eab939f678b8 100644 --- a/src/mono/browser/runtime/loader/run.ts +++ b/src/mono/browser/runtime/loader/run.ts @@ -382,8 +382,7 @@ export class HostBuilder implements DotnetHostBuilder { if (!this.instance) { await this.create(); } - mono_assert(emscriptenModule.config.mainAssemblyName, "Null moduleConfig.config.mainAssemblyName"); - return this.instance!.runMainAndExit(emscriptenModule.config.mainAssemblyName); + return this.instance!.runMainAndExit(); } catch (err) { mono_exit(1, err); throw err; diff --git a/src/mono/browser/runtime/rollup.config.js b/src/mono/browser/runtime/rollup.config.js index 769d6a01518ae2..babd914374b7e1 100644 --- a/src/mono/browser/runtime/rollup.config.js +++ b/src/mono/browser/runtime/rollup.config.js @@ -212,6 +212,7 @@ const typesConfig = { ], external: externalDependencies, plugins: [dts()], + onwarn: onwarn }; let diagnosticMockTypesConfig = undefined; @@ -239,6 +240,7 @@ if (isDebug) { ], external: externalDependencies, plugins: [dts()], + onwarn: onwarn }; } diff --git a/src/mono/browser/runtime/run.ts b/src/mono/browser/runtime/run.ts index 8443fc647749d3..6ba5076e53e219 100644 --- a/src/mono/browser/runtime/run.ts +++ b/src/mono/browser/runtime/run.ts @@ -3,7 +3,7 @@ import MonoWasmThreads from "consts:monoWasmThreads"; -import { ENVIRONMENT_IS_NODE, loaderHelpers, runtimeHelpers } from "./globals"; +import { ENVIRONMENT_IS_NODE, loaderHelpers, mono_assert, runtimeHelpers } from "./globals"; import { mono_wasm_wait_for_debugger } from "./debug"; import { mono_wasm_set_main_args } from "./startup"; import cwraps from "./cwraps"; @@ -15,7 +15,7 @@ import { cancelThreads } from "./pthreads/browser"; /** * Possible signatures are described here https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/main-command-line */ -export async function mono_run_main_and_exit(main_assembly_name: string, args?: string[]): Promise { +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); loaderHelpers.mono_exit(result); @@ -37,7 +37,11 @@ export async function mono_run_main_and_exit(main_assembly_name: string, args?: /** * Possible signatures are described here https://docs.microsoft.com/en-us/dotnet/csharp/fundamentals/program-structure/main-command-line */ -export async function mono_run_main(main_assembly_name: string, args?: string[]): Promise { +export async function mono_run_main(main_assembly_name?: string, args?: string[]): Promise { + if (main_assembly_name === undefined || main_assembly_name === null || main_assembly_name === "") { + main_assembly_name = loaderHelpers.config.mainAssemblyName; + mono_assert(main_assembly_name, "Null or empty config.mainAssemblyName"); + } if (args === undefined || args === null) { args = runtimeHelpers.config.applicationArguments; } diff --git a/src/mono/browser/runtime/types/index.ts b/src/mono/browser/runtime/types/index.ts index 13e71c1fc240eb..e6f7152b9bff9d 100644 --- a/src/mono/browser/runtime/types/index.ts +++ b/src/mono/browser/runtime/types/index.ts @@ -4,26 +4,81 @@ import type { EmscriptenModule, NativePointer } from "./emscripten"; export interface DotnetHostBuilder { - withConfig(config: MonoConfig): DotnetHostBuilder - withConfigSrc(configSrc: string): DotnetHostBuilder - withApplicationArguments(...args: string[]): DotnetHostBuilder - withEnvironmentVariable(name: string, value: string): DotnetHostBuilder - withEnvironmentVariables(variables: { [i: string]: string; }): DotnetHostBuilder - withVirtualWorkingDirectory(vfsPath: string): DotnetHostBuilder - withDiagnosticTracing(enabled: boolean): DotnetHostBuilder - withDebugging(level: number): DotnetHostBuilder - withMainAssembly(mainAssemblyName: string): DotnetHostBuilder - withApplicationArgumentsFromQuery(): DotnetHostBuilder + /** + * @param config default values for the runtime configuration. It will be merged with the default values. + * Note that if you provide resources and don't provide custom configSrc URL, the blazor.boot.json will be downloaded and applied by default. + */ + withConfig(config: MonoConfig): DotnetHostBuilder; + /** + * @param configSrc URL to the configuration file. ./blazor.boot.json is a default config file location. + */ + withConfigSrc(configSrc: string): DotnetHostBuilder; + /** + * "command line" arguments for the Main() method. + * @param args + */ + withApplicationArguments(...args: string[]): DotnetHostBuilder; + /** + * Sets the environment variable for the "process" + */ + withEnvironmentVariable(name: string, value: string): DotnetHostBuilder; + /** + * Sets the environment variables for the "process" + */ + withEnvironmentVariables(variables: { + [i: string]: string; + }): DotnetHostBuilder; + /** + * Sets the "current directory" for the "process" on the virtual file system. + */ + withVirtualWorkingDirectory(vfsPath: string): DotnetHostBuilder; + /** + * @param enabled if "true", writes diagnostic messages during runtime startup and execution to the browser console. + */ + withDiagnosticTracing(enabled: boolean): DotnetHostBuilder; + /** + * @param level + * level > 0 enables debugging and sets the logging level to debug + * level == 0 disables debugging and enables interpreter optimizations + * level < 0 enables debugging and disables debug logging. + */ + withDebugging(level: number): DotnetHostBuilder; + /** + * @param mainAssemblyName Sets the name of the assembly with the Main() method. Default is the same as the .csproj name. + */ + withMainAssembly(mainAssemblyName: string): DotnetHostBuilder; + /** + * Supply "command line" arguments for the Main() method from browser query arguments named "arg". Eg. `index.html?arg=A&arg=B&arg=C`. + * @param args + */ + withApplicationArgumentsFromQuery(): DotnetHostBuilder; + /** + * Sets application environment, such as "Development", "Staging", "Production", etc. + */ withApplicationEnvironment(applicationEnvironment?: string): DotnetHostBuilder; + /** + * Sets application culture. This is a name specified in the BCP 47 format. See https://tools.ietf.org/html/bcp47 + */ withApplicationCulture(applicationCulture?: string): DotnetHostBuilder; - /** * Overrides the built-in boot resource loading mechanism so that boot resources can be fetched * from a custom source, such as an external CDN. */ withResourceLoader(loadBootResource?: LoadBootResourceCallback): DotnetHostBuilder; - create(): Promise - run(): Promise + /** + * Starts the runtime and returns promise of the API object. + */ + create(): Promise; + + /** + * Runs the Main() method of the application and exits the runtime. + * You can provide "command line" arguments for the Main() method using + * - dotnet.withApplicationArguments(["A", "B", "C"]) + * - dotnet.withApplicationArgumentsFromQuery() + * Note: after the runtime exits, it would reject all further calls to the API. + * You can use runMain() if you want to keep the runtime alive. + */ + run(): Promise; } // when adding new fields, please consider if it should be impacting the snapshot hash. If not, please drop it in the snapshot getCacheKey() @@ -243,7 +298,7 @@ export interface AssetEntry { export type SingleAssetBehaviors = /** - * The binary of the dotnet runtime. + * The binary of the .NET runtime. */ | "dotnetwasm" /** @@ -340,48 +395,180 @@ export type DotnetModuleConfig = { } & Partial export type APIType = { - runMain: (mainAssemblyName: string, args?: string[]) => Promise, - runMainAndExit: (mainAssemblyName: string, args?: string[]) => Promise, - setEnvironmentVariable: (name: string, value: string) => void, - getAssemblyExports(assemblyName: string): Promise, - setModuleImports(moduleName: string, moduleImports: any): void, - getConfig: () => MonoConfig, - invokeLibraryInitializers: (functionName: string, args: any[]) => Promise, - - // memory management - setHeapB32: (offset: NativePointer, value: number | boolean) => void, - setHeapU8: (offset: NativePointer, value: number) => void, - setHeapU16: (offset: NativePointer, value: number) => void, - setHeapU32: (offset: NativePointer, value: NativePointer | number) => void, - setHeapI8: (offset: NativePointer, value: number) => void, - setHeapI16: (offset: NativePointer, value: number) => void, - setHeapI32: (offset: NativePointer, value: number) => void, - setHeapI52: (offset: NativePointer, value: number) => void, - setHeapU52: (offset: NativePointer, value: number) => void, - setHeapI64Big: (offset: NativePointer, value: bigint) => void, - setHeapF32: (offset: NativePointer, value: number) => void, - setHeapF64: (offset: NativePointer, value: number) => void, - getHeapB32: (offset: NativePointer) => boolean, - getHeapU8: (offset: NativePointer) => number, - getHeapU16: (offset: NativePointer) => number, - getHeapU32: (offset: NativePointer) => number, - getHeapI8: (offset: NativePointer) => number, - getHeapI16: (offset: NativePointer) => number, - getHeapI32: (offset: NativePointer) => number, - getHeapI52: (offset: NativePointer) => number, - getHeapU52: (offset: NativePointer) => number, - getHeapI64Big: (offset: NativePointer) => bigint, - getHeapF32: (offset: NativePointer) => number, - getHeapF64: (offset: NativePointer) => number, - localHeapViewI8: () => Int8Array, - localHeapViewI16: () => Int16Array, - localHeapViewI32: () => Int32Array, - localHeapViewI64Big: () => BigInt64Array, - localHeapViewU8: () => Uint8Array, - localHeapViewU16: () => Uint16Array, - localHeapViewU32: () => Uint32Array, - localHeapViewF32: () => Float32Array, - localHeapViewF64: () => Float64Array, + /** + * Runs the Main() method of the application. + * Note: this will keep the .NET runtime alive and the APIs will be available for further calls. + * @param mainAssemblyName name of the assembly with the Main() method. Optional. Default is the same as the .csproj name. + * @param args command line arguments for the Main() method. Optional. + * @returns exit code of the Main() method. + */ + runMain: (mainAssemblyName?: string, args?: string[]) => Promise; + /** + * Runs the Main() method of the application and exits the runtime. + * Note: after the runtime exits, it would reject all further calls to the API. + * @param mainAssemblyName name of the assembly with the Main() method. Optional. Default is the same as the .csproj name. + * @param args command line arguments for the Main() method. Optional. + * @returns exit code of the Main() method. + */ + runMainAndExit: (mainAssemblyName?: string, args?: string[]) => Promise; + /** + * Sets the environment variable for the "process" + * @param name + * @param value + */ + setEnvironmentVariable: (name: string, value: string) => void; + /** + * Returns the [JSExport] methods of the assembly with the given name + * @param assemblyName + */ + getAssemblyExports(assemblyName: string): Promise; + /** + * Provides functions which could be imported by the managed code using [JSImport] + * @param moduleName maps to the second parameter of [JSImport] + * @param moduleImports object with functions which could be imported by the managed code. The keys map to the first parameter of [JSImport] + */ + setModuleImports(moduleName: string, moduleImports: any): void; + /** + * Returns the configuration object used to start the runtime. + */ + getConfig: () => MonoConfig; + /** + * Executes scripts which were loaded during runtime bootstrap. + * You can register the scripts using MonoConfig.resources.modulesAfterConfigLoaded and MonoConfig.resources.modulesAfterRuntimeReady. + */ + invokeLibraryInitializers: (functionName: string, args: any[]) => Promise; + /** + * Writes to the WASM linear memory + */ + setHeapB32: (offset: NativePointer, value: number | boolean) => void; + /** + * Writes to the WASM linear memory + */ + setHeapU8: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapU16: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapU32: (offset: NativePointer, value: NativePointer | number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapI8: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapI16: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapI32: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapI52: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapU52: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapI64Big: (offset: NativePointer, value: bigint) => void; + /** + * Writes to the WASM linear memory + */ + setHeapF32: (offset: NativePointer, value: number) => void; + /** + * Writes to the WASM linear memory + */ + setHeapF64: (offset: NativePointer, value: number) => void; + /** + * Reads from the WASM linear memory + */ + getHeapB32: (offset: NativePointer) => boolean; + /** + * Reads from the WASM linear memory + */ + getHeapU8: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapU16: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapU32: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapI8: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapI16: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapI32: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapI52: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapU52: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapI64Big: (offset: NativePointer) => bigint; + /** + * Reads from the WASM linear memory + */ + getHeapF32: (offset: NativePointer) => number; + /** + * Reads from the WASM linear memory + */ + getHeapF64: (offset: NativePointer) => number; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewI8: () => Int8Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewI16: () => Int16Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewI32: () => Int32Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewI64Big: () => BigInt64Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewU8: () => Uint8Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewU16: () => Uint16Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewU32: () => Uint32Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewF32: () => Float32Array; + /** + * Returns a short term view of the WASM linear memory. Don't store the reference, don't use it after await. + */ + localHeapViewF64: () => Float64Array; } export type RuntimeAPI = { @@ -396,8 +583,14 @@ export type RuntimeAPI = { } & APIType export type ModuleAPI = { + /** + * The builder for the .NET runtime. + */ dotnet: DotnetHostBuilder; - exit: (code: number, reason?: any) => void + /** + * Terminates the runtime "process" and reject all further calls to the API. + */ + exit: (code: number, reason?: any) => void; } export type CreateDotnetRuntimeType = (moduleFactory: DotnetModuleConfig | ((api: RuntimeAPI) => DotnetModuleConfig)) => Promise; diff --git a/src/mono/browser/runtime/web-socket.ts b/src/mono/browser/runtime/web-socket.ts index f2be9bd5d5f923..ad5c71b10949da 100644 --- a/src/mono/browser/runtime/web-socket.ts +++ b/src/mono/browser/runtime/web-socket.ts @@ -66,20 +66,20 @@ export function ws_wasm_create(uri: string, sub_protocols: string[] | null, rece ws.binaryType = "arraybuffer"; const local_on_open = () => { if (ws[wasm_ws_is_aborted]) return; - if (loaderHelpers.is_exited()) return; + if (!loaderHelpers.is_runtime_running()) return; open_promise_control.resolve(ws); prevent_timer_throttling(); }; const local_on_message = (ev: MessageEvent) => { if (ws[wasm_ws_is_aborted]) return; - if (loaderHelpers.is_exited()) return; + if (!loaderHelpers.is_runtime_running()) return; _mono_wasm_web_socket_on_message(ws, ev); prevent_timer_throttling(); }; const local_on_close = (ev: CloseEvent) => { ws.removeEventListener("message", local_on_message); if (ws[wasm_ws_is_aborted]) return; - if (loaderHelpers.is_exited()) return; + if (!loaderHelpers.is_runtime_running()) return; ws[wasm_ws_close_received] = true; ws["close_status"] = ev.code; @@ -103,7 +103,7 @@ export function ws_wasm_create(uri: string, sub_protocols: string[] | null, rece }; const local_on_error = (ev: any) => { if (ws[wasm_ws_is_aborted]) return; - if (loaderHelpers.is_exited()) return; + if (!loaderHelpers.is_runtime_running()) return; ws.removeEventListener("message", local_on_message); const error = new Error(ev.message || "WebSocket error"); mono_log_warn("WebSocket error", error); diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/InterpPgoTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/InterpPgoTests.cs index 8982e50f7a301d..203ad80fc4dc77 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/InterpPgoTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/InterpPgoTests.cs @@ -40,14 +40,15 @@ public async Task FirstRunGeneratesTableAndSecondRunLoadsIt(string config) UpdateBrowserMainJs((js) => { // We need to capture INTERNAL so we can explicitly save the PGO table js = js.Replace( - "const { setModuleImports, getAssemblyExports, getConfig } = await dotnet", - "const { setModuleImports, getAssemblyExports, getConfig, INTERNAL } = await dotnet" + "const { setModuleImports, getAssemblyExports, getConfig, runMain } = await dotnet", + "const { setModuleImports, getAssemblyExports, getConfig, runMain, INTERNAL } = await dotnet" ); // Enable interpreter PGO + interpreter PGO logging + console output capturing js = js.Replace( ".create()", ".withConsoleForwarding().withElementOnExit().withExitCodeLogging().withExitOnUnhandledError().withRuntimeOptions(['--interp-pgo-logging']).withInterpreterPgo(true).create()" ); + js = js.Replace("runMain()", "dotnet.run()"); // Call Greeting in a loop to exercise enough code to cause something to tier, // then call INTERNAL.interp_pgo_save_data() to save the interp PGO table js = js.Replace( diff --git a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs index 788000d0788c32..fe0b903c16e79f 100644 --- a/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs +++ b/src/mono/wasm/Wasm.Build.Tests/Templates/WasmTemplateTests.cs @@ -44,6 +44,7 @@ private void UpdateBrowserMainJs(string targetFramework, string runtimeAssetsRel ? ".withConsoleForwarding().withElementOnExit().withExitCodeLogging().withExitOnUnhandledError().create()" : ".withConsoleForwarding().withElementOnExit().withExitCodeLogging().create()"); + mainJsContent = mainJsContent.Replace("runMain()", "dotnet.run()"); mainJsContent = mainJsContent.Replace("from './_framework/dotnet.js'", $"from '{runtimeAssetsRelativePath}dotnet.js'"); return mainJsContent; diff --git a/src/mono/wasm/templates/templates/browser/wwwroot/main.js b/src/mono/wasm/templates/templates/browser/wwwroot/main.js index a073fc9cf70327..afcf21e4e6510d 100644 --- a/src/mono/wasm/templates/templates/browser/wwwroot/main.js +++ b/src/mono/wasm/templates/templates/browser/wwwroot/main.js @@ -3,7 +3,7 @@ import { dotnet } from './_framework/dotnet.js' -const { setModuleImports, getAssemblyExports, getConfig } = await dotnet +const { setModuleImports, getAssemblyExports, getConfig, runMain } = await dotnet .withDiagnosticTracing(false) .withApplicationArgumentsFromQuery() .create(); @@ -22,4 +22,6 @@ const text = exports.MyClass.Greeting(); console.log(text); document.getElementById('out').innerHTML = text; -await dotnet.run(); \ No newline at end of file + +// run the C# Main() method and keep the runtime process running and executing further API calls +await runMain(); \ No newline at end of file diff --git a/src/mono/wasm/templates/templates/console/main.mjs b/src/mono/wasm/templates/templates/console/main.mjs index ea131be48c1bf5..d1e804453079ea 100644 --- a/src/mono/wasm/templates/templates/console/main.mjs +++ b/src/mono/wasm/templates/templates/console/main.mjs @@ -3,7 +3,7 @@ import { dotnet } from './_framework/dotnet.js' -const { setModuleImports, getAssemblyExports, getConfig } = await dotnet +const { setModuleImports, getAssemblyExports, getConfig, runMainAndExit } = await dotnet .withDiagnosticTracing(false) .create(); @@ -20,4 +20,5 @@ const exports = await getAssemblyExports(config.mainAssemblyName); const text = exports.MyClass.Greeting(); console.log(text); -await dotnet.run(); +// run the C# Main() method and exit the process +await runMainAndExit();