Skip to content

Commit

Permalink
[wasm] improve startup (#72275)
Browse files Browse the repository at this point in the history
- streaming wasm instantiation
- more parallel download of DLLs
- new hook `downloadResource` and `config.assets[].resolvedUrl `
- improved `locateFile` allows to run dotnet with another working directory
- call `init_crypto` in blazor startup sequence

Co-authored-by: Marek Fišera <mara@neptuo.com>
  • Loading branch information
pavelsavara and maraf committed Jul 26, 2022
1 parent 86a7742 commit 73374e8
Show file tree
Hide file tree
Showing 35 changed files with 1,145 additions and 861 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

export function log(message) {
// uncomment for debugging
console.log(message);
// console.log(message);
}

export function install() {
Expand Down
2 changes: 2 additions & 0 deletions src/mono/mono/component/mini-wasm-debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ mono_wasm_send_dbg_command_with_parms (int id, MdbgProtCommandSet command_set, i
gboolean result = FALSE;
MONO_ENTER_GC_UNSAFE;
if (!debugger_enabled) {
PRINT_ERROR_MSG ("DEBUGGING IS NOT ENABLED\n");
mono_wasm_add_dbg_command_received (0, id, 0, 0);
result = TRUE;
goto done;
Expand All @@ -401,6 +402,7 @@ mono_wasm_send_dbg_command (int id, MdbgProtCommandSet command_set, int command,
gboolean result = FALSE;
MONO_ENTER_GC_UNSAFE;
if (!debugger_enabled) {
PRINT_ERROR_MSG ("DEBUGGING IS NOT ENABLED\n");
mono_wasm_add_dbg_command_received(0, id, 0, 0);
result = TRUE;
goto done;
Expand Down
2 changes: 1 addition & 1 deletion src/mono/sample/wasm/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
<Exec Command="dotnet tool install -g dotnet-serve" IgnoreExitCode="true" />
</Target>
<Target Name="RunSampleWithBrowser" DependsOnTargets="BuildSampleInTree;CheckServe">
<Exec Command="$(_Dotnet) serve -o -d:bin/$(Configuration)/AppBundle -p:8000 --mime .mjs=text/javascript --mime .js=text/javascript --mime .cjs=text/javascript" IgnoreExitCode="true" YieldDuringToolExecution="true" />
<Exec Command="$(_Dotnet) serve -o -d:bin/$(Configuration)/AppBundle -p:8000 --mime .mjs=text/javascript --mime .js=text/javascript --mime .cjs=text/javascript -h Cross-Origin-Opener-Policy:same-origin -h Cross-Origin-Embedder-Policy:require-corp " IgnoreExitCode="true" YieldDuringToolExecution="true" />
</Target>
<Target Name="RunSampleWithBrowserAndSimpleServer" DependsOnTargets="BuildSampleInTree">
<Exec Command="$(_Dotnet) build -c $(Configuration) ..\simple-server\HttpServer.csproj" />
Expand Down
4 changes: 2 additions & 2 deletions src/mono/sample/wasm/browser/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function sub(a, b) {

try {
const { MONO, RuntimeBuildInfo, IMPORTS } = await createDotnetRuntime(() => {
console.log('user code in createDotnetRuntime');
console.log('user code in createDotnetRuntime callback');
return {
configSrc: "./mono-config.json",
preInit: () => { console.log('user code Module.preInit'); },
Expand All @@ -31,7 +31,7 @@ try {
postRun: () => { console.log('user code Module.postRun'); },
}
});
console.log('after createDotnetRuntime');
console.log('user code after createDotnetRuntime()');
IMPORTS.Sample = {
Test: {
add,
Expand Down
18 changes: 9 additions & 9 deletions src/mono/wasm/debugger/DebuggerTestSuite/BreakpointTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public async Task CreateJSBreakpoint()
{
// Test that js breakpoints get set correctly
// 13 24
// 13 53
// 14 24
var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 24);

Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString());
Expand All @@ -52,23 +52,23 @@ public async Task CreateJSBreakpoint()
Assert.Equal(13, (int)loc["lineNumber"]);
Assert.Equal(24, (int)loc["columnNumber"]);

var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 53);
var bp2_res = await SetBreakpoint("/debugger-driver.html", 14, 24);

Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString());
Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);

var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];

Assert.NotNull(loc2["scriptId"]);
Assert.Equal(13, (int)loc2["lineNumber"]);
Assert.Equal(53, (int)loc2["columnNumber"]);
Assert.Equal(14, (int)loc2["lineNumber"]);
Assert.Equal(24, (int)loc2["columnNumber"]);
}

[ConditionalFact(nameof(RunningOnChrome))]
public async Task CreateJS0Breakpoint()
{
// 13 24
// 13 53
// 14 24
var bp1_res = await SetBreakpoint("/debugger-driver.html", 13, 0);

Assert.EndsWith("debugger-driver.html", bp1_res.Value["breakpointId"].ToString());
Expand All @@ -78,18 +78,18 @@ public async Task CreateJS0Breakpoint()

Assert.NotNull(loc["scriptId"]);
Assert.Equal(13, (int)loc["lineNumber"]);
Assert.Equal(4, (int)loc["columnNumber"]);
Assert.Equal(24, (int)loc["columnNumber"]);

var bp2_res = await SetBreakpoint("/debugger-driver.html", 13, 53);
var bp2_res = await SetBreakpoint("/debugger-driver.html", 14, 0);

Assert.EndsWith("debugger-driver.html", bp2_res.Value["breakpointId"].ToString());
Assert.Equal(1, bp2_res.Value["locations"]?.Value<JArray>()?.Count);

var loc2 = bp2_res.Value["locations"]?.Value<JArray>()[0];

Assert.NotNull(loc2["scriptId"]);
Assert.Equal(13, (int)loc2["lineNumber"]);
Assert.Equal(53, (int)loc2["columnNumber"]);
Assert.Equal(14, (int)loc2["lineNumber"]);
Assert.Equal(24, (int)loc2["columnNumber"]);
}

[ConditionalTheory(nameof(RunningOnChrome))]
Expand Down
20 changes: 10 additions & 10 deletions src/mono/wasm/debugger/tests/debugger-test/debugger-driver.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,30 @@
var App = {
static_method_table: {},
init: function () {
this.int_add = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:IntAdd");
this.use_complex = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:UseComplex");
this.delegates_test = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:DelegatesTest");
this.generic_types_test = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:GenericTypesTest");
this.outer_method = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math:OuterMethod");
this.async_method = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] Math/NestedInMath:AsyncTest");
this.method_with_structs = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalStructs");
this.run_all = getDotnetRuntime(0).INTERNAL.mono_bind_static_method ("[debugger-test] DebuggerTest:run_all");
this.int_add = App.BINDING.bind_static_method("[debugger-test] Math:IntAdd");
this.use_complex = App.BINDING.bind_static_method("[debugger-test] Math:UseComplex");
this.delegates_test = App.BINDING.bind_static_method("[debugger-test] Math:DelegatesTest");
this.generic_types_test = App.BINDING.bind_static_method("[debugger-test] Math:GenericTypesTest");
this.outer_method = App.BINDING.bind_static_method("[debugger-test] Math:OuterMethod");
this.async_method = App.BINDING.bind_static_method("[debugger-test] Math/NestedInMath:AsyncTest");
this.method_with_structs = App.BINDING.bind_static_method("[debugger-test] DebuggerTests.ValueTypesTest:MethodWithLocalStructs");
this.run_all = App.BINDING.bind_static_method("[debugger-test] DebuggerTest:run_all");
this.static_method_table = {};
console.log ("ready");
},
};
function invoke_static_method (method_name, ...args) {
var method = App.static_method_table [method_name];
if (method == undefined)
method = App.static_method_table [method_name] = getDotnetRuntime(0).INTERNAL.mono_bind_static_method (method_name);
method = App.static_method_table[method_name] = App.BINDING.bind_static_method(method_name);

return method (...args);
}

async function invoke_static_method_async (method_name, ...args) {
var method = App.static_method_table [method_name];
if (method == undefined) {
method = App.static_method_table [method_name] = getDotnetRuntime(0).INTERNAL.mono_bind_static_method (method_name);
method = App.static_method_table[method_name] = App.BINDING.bind_static_method(method_name);
}

return await method (...args);
Expand Down
11 changes: 6 additions & 5 deletions src/mono/wasm/debugger/tests/debugger-test/debugger-main.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,23 @@
import createDotnetRuntime from './dotnet.js'

try {
const { BINDING, INTERNAL } = await createDotnetRuntime(() => ({
const { BINDING } = await createDotnetRuntime(({ INTERNAL }) => ({
configSrc: "./mono-config.json",
onConfigLoaded: (config) => {
config.environment_variables["DOTNET_MODIFIABLE_ASSEMBLIES"] = "debug";
config.diagnostic_tracing = true;
/* For custom logging patch the functions below
config.diagnostic_tracing = true;
config.environment_variables["MONO_LOG_LEVEL"] = "debug";
config.environment_variables["MONO_LOG_MASK"] = "all";
INTERNAL.logging = {
trace: function (domain, log_level, message, isFatal, dataPtr) { },
debugger: function (level, message) { }
trace: (domain, log_level, message, isFatal, dataPtr) => console.log({ domain, log_level, message, isFatal, dataPtr }),
debugger: (level, message) => console.log({ level, message }),
};
*/
},
}));
App.init({ BINDING, INTERNAL })
App.BINDING = BINDING;
App.init()
} catch (err) {
console.log(`WASM ERROR ${err}`);
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<EnableDefaultWasmAssembliesToBundle>false</EnableDefaultWasmAssembliesToBundle>
<WasmAppDir>$(AppDir)</WasmAppDir>
<WasmMainJSPath>debugger-main.js</WasmMainJSPath>
<!-- like is used on blazor -->
<!-- -1 enabled debugging and disables debug logging. -->
<WasmDebugLevel Condition="'$(WasmDebugLevel)'==''">-1</WasmDebugLevel>

<WasmResolveAssembliesBeforeBuild>true</WasmResolveAssembliesBeforeBuild>
Expand Down
1 change: 1 addition & 0 deletions src/mono/wasm/runtime/cjs/dotnet.cjs.extpost.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var require = require || undefined;
var __dirname = __dirname || "";
// if loaded into global namespace and configured with global Module, we will self start in compatibility mode
const __isWorker = typeof globalThis.importScripts === "function";
let ENVIRONMENT_IS_GLOBAL = !__isWorker && (typeof globalThis.Module === "object" && globalThis.__dotnet_runtime === __dotnet_runtime);
Expand Down
17 changes: 12 additions & 5 deletions src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,30 @@ const isPThread = `false`;
const DotnetSupportLib = {
$DOTNET: {},
// these lines will be placed early on emscripten runtime creation, passing import and export objects into __dotnet_runtime IFFE
// we replace implementation of readAsync and fetch
// we replace implementation of fetch
// replacement of require is there for consistency with ES6 code
$DOTNET__postset: `
let __dotnet_replacement_PThread = ${usePThreads} ? {} : undefined;
if (${usePThreads}) {
__dotnet_replacement_PThread.loadWasmModuleToWorker = PThread.loadWasmModuleToWorker;
__dotnet_replacement_PThread.threadInitTLS = PThread.threadInitTLS;
}
let __dotnet_replacements = {readAsync, fetch: globalThis.fetch, require, updateGlobalBufferAndViews, pthreadReplacements: __dotnet_replacement_PThread};
let __dotnet_replacements = {scriptUrl: undefined, fetch: globalThis.fetch, require, updateGlobalBufferAndViews, pthreadReplacements: __dotnet_replacement_PThread};
if (ENVIRONMENT_IS_NODE) {
__dotnet_replacements.requirePromise = Promise.resolve(require);
}
let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
{ isESM:false, isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isWorker:ENVIRONMENT_IS_WORKER, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, isPThread:${isPThread}, locateFile, quit_, ExitStatus, requirePromise:Promise.resolve(require)},
{ isESM:false, isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isWorker:ENVIRONMENT_IS_WORKER, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, isPThread:${isPThread}, quit_, ExitStatus, requirePromise:Promise.resolve(require)},
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module, marshaled_exports: EXPORTS, marshaled_imports: IMPORTS },
__dotnet_replacements);
updateGlobalBufferAndViews = __dotnet_replacements.updateGlobalBufferAndViews;
readAsync = __dotnet_replacements.readAsync;
var fetch = __dotnet_replacements.fetch;
require = __dotnet_replacements.requireOut;
_scriptDir = __dirname = scriptDirectory = __dotnet_replacements.scriptDirectory;
if (ENVIRONMENT_IS_NODE) {
__dotnet_replacements.requirePromise.then(someRequire => {
require = someRequire;
});
}
var noExitRuntime = __dotnet_replacements.noExitRuntime;
if (${usePThreads}) {
PThread.loadWasmModuleToWorker = __dotnet_replacements.pthreadReplacements.loadWasmModuleToWorker;
Expand Down
5 changes: 4 additions & 1 deletion src/mono/wasm/runtime/cjs/dotnet.cjs.pre.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ if (ENVIRONMENT_IS_GLOBAL) {
}
globalThis.Module.ready = Module.ready;
Module = createDotnetRuntime = globalThis.Module;
if (!createDotnetRuntime.locateFile) createDotnetRuntime.locateFile = createDotnetRuntime.__locateFile = (path) => scriptDirectory + path;
}
else if (typeof createDotnetRuntime === "object") {
Module = { ready: Module.ready, __undefinedConfig: Object.keys(createDotnetRuntime).length === 1 };
Object.assign(Module, createDotnetRuntime);
createDotnetRuntime = Module;
if (!createDotnetRuntime.locateFile) createDotnetRuntime.locateFile = createDotnetRuntime.__locateFile = (path) => scriptDirectory + path;
}
else if (typeof createDotnetRuntime === "function") {
Module = { ready: Module.ready };
Expand All @@ -19,7 +21,8 @@ else if (typeof createDotnetRuntime === "function") {
}
Object.assign(Module, extension);
createDotnetRuntime = Module;
if (!createDotnetRuntime.locateFile) createDotnetRuntime.locateFile = createDotnetRuntime.__locateFile = (path) => scriptDirectory + path;
}
else {
throw new Error("MONO_WASM: Can't locate global Module object or moduleFactory callback of createDotnetRuntime function.")
}
}
29 changes: 14 additions & 15 deletions src/mono/wasm/runtime/crypto-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function dotnet_browser_encrypt_decrypt(isEncrypting: boolean, key_buffer
}

if (result.length > output_len) {
console.error(`ENCRYPT DECRYPT: Encrypt/Decrypt length exceeds output length: ${result.length} > ${output_len}`);
console.error(`MONO_WASM_ENCRYPT_DECRYPT: Encrypt/Decrypt length exceeds output length: ${result.length} > ${output_len}`);
return ERR_ARGS;
}

Expand Down Expand Up @@ -91,7 +91,7 @@ function _send_simple_msg(msg: any, prefix: string, output_buffer: number, outpu
}

if (result.length > output_len) {
console.error(`${prefix}: Result length exceeds output length: ${result.length} > ${output_len}`);
console.error(`MONO_WASM_ENCRYPT_DECRYPT: ${prefix}: Result length exceeds output length: ${result.length} > ${output_len}`);
return ERR_ARGS;
}

Expand Down Expand Up @@ -132,7 +132,7 @@ function _send_msg_worker(msg: any): number | any {
const responseJson = JSON.parse(response);

if (responseJson.error !== undefined) {
console.error(`Worker failed with: ${responseJson.error}`);
console.error(`MONO_WASM_ENCRYPT_DECRYPT: Worker failed with: ${responseJson.error}`);
if (responseJson.error_type == "ArgumentsError")
return ERR_ARGS;
if (responseJson.error_type == "WorkerFailedError")
Expand All @@ -144,9 +144,9 @@ function _send_msg_worker(msg: any): number | any {
return responseJson.result;
} catch (err) {
if (err instanceof Error && err.stack !== undefined)
console.error(`${err.stack}`);
console.error(`MONO_WASM_ENCRYPT_DECRYPT: ${err.stack}`);
else
console.error(`_send_msg_worker failed: ${err}`);
console.error(`MONO_WASM_ENCRYPT_DECRYPT: _send_msg_worker failed: ${err}`);
return ERR_OP_FAILED;
}
}
Expand Down Expand Up @@ -202,10 +202,9 @@ class LibraryChannel {

public send_msg(msg: string): string {
try {
let state = Atomics.load(this.comm, this.STATE_IDX);
if (state !== this.STATE_IDLE)
console.log(`send_msg, waiting for idle now, ${state}`);
state = this.wait_for_state(pstate => pstate == this.STATE_IDLE, "waiting");
// const state = Atomics.load(this.comm, this.STATE_IDX);
// if (state !== this.STATE_IDLE) console.debug(`MONO_WASM_ENCRYPT_DECRYPT: send_msg, waiting for idle now, ${state}`);
this.wait_for_state(pstate => pstate == this.STATE_IDLE, "waiting");

this.send_request(msg);
return this.read_response();
Expand All @@ -214,14 +213,13 @@ class LibraryChannel {
throw err;
}
finally {
const state = Atomics.load(this.comm, this.STATE_IDX);
if (state !== this.STATE_IDLE)
console.log(`state at end of send_msg: ${state}`);
// const state = Atomics.load(this.comm, this.STATE_IDX);
// if (state !== this.STATE_IDLE) console.debug(`MONO_WASM_ENCRYPT_DECRYPT: state at end of send_msg: ${state}`);
}
}

public shutdown(): void {
console.debug("Shutting down crypto");
// console.debug("MONO_WASM_ENCRYPT_DECRYPT: Shutting down crypto");
const state = Atomics.load(this.comm, this.STATE_IDX);
if (state !== this.STATE_IDLE)
throw new Error(`OWNER: Invalid sync communication channel state: ${state}`);
Expand All @@ -232,14 +230,15 @@ class LibraryChannel {
Atomics.notify(this.comm, this.STATE_IDX);
}

// eslint-disable-next-line @typescript-eslint/no-unused-vars
private reset(reason: string): void {
console.debug(`reset: ${reason}`);
// console.debug(`MONO_WASM_ENCRYPT_DECRYPT: reset: ${reason}`);
const state = Atomics.load(this.comm, this.STATE_IDX);
if (state === this.STATE_SHUTDOWN)
return;

if (state === this.STATE_RESET || state === this.STATE_IDLE) {
console.debug(`state is already RESET or idle: ${state}`);
// console.debug(`MONO_WASM_ENCRYPT_DECRYPT: state is already RESET or idle: ${state}`);
return;
}

Expand Down

0 comments on commit 73374e8

Please sign in to comment.