<WasmGenerateAppBundle Condition="'$(WasmGenerateAppBundle)' == ''">true</WasmGenerateAppBundle>
<BundleTestAppTargets>$(BundleTestAppTargets);BundleTestWasmApp</BundleTestAppTargets>
<DebuggerSupport Condition="'$(DebuggerSupport)' == '' and '$(Configuration)' == 'Debug'">true</DebuggerSupport>
- <WasmNativeStrip Condition="'$(WasmNativeStrip)' == ''">false</WasmNativeStrip>
<!-- Some tests expect to load satellite assemblies by path, eg. System.Runtime.Loader.Tests,
so, just setting it true by default -->
<IncludeSatelliteAssembliesInVFS Condition="'$(IncludeSatelliteAssembliesInVFS)' == ''">true</IncludeSatelliteAssembliesInVFS>
- <WasmEmitSymbolMap Condition="'$(WasmEmitSymbolMap)' == ''">true</WasmEmitSymbolMap>
+
+ <!--
+ - For regular library tests, it will use the symbols file from the runtime pack.
+ - for AOT library tests, we use WasmNativeStrip=false, so we already have symbols
+ -->
+ <WasmNativeStrip Condition="'$(WasmNativeStrip)' == ''">false</WasmNativeStrip>
+ <WasmEmitSymbolMap Condition="'$(WasmEmitSymbolMap)' == '' and '$(RunAOTCompilation)' != 'true'">true</WasmEmitSymbolMap>
<!-- Run only if previous command succeeded -->
<_ShellCommandSeparator Condition="'$(OS)' == 'Windows_NT'">&&</_ShellCommandSeparator>
fi
run-console:
- cd bin/$(CONFIG)/AppBundle && $(V8_PATH) --stack-trace-limit=1000 --single-threaded --expose_wasm $(MAIN_JS) -- $(DOTNET_MONO_LOG_LEVEL) --run $(CONSOLE_DLL) $(ARGS)
+ cd bin/$(CONFIG)/AppBundle && $(V8_PATH) --stack-trace-limit=1000 --single-threaded --expose_wasm $(MAIN_JS) -- $(ARGS)
run-console-node:
- cd bin/$(CONFIG)/AppBundle && node --stack-trace-limit=1000 --single-threaded --expose_wasm $(MAIN_JS) -- $(DOTNET_MONO_LOG_LEVEL) --run $(CONSOLE_DLL) $(ARGS)
+ cd bin/$(CONFIG)/AppBundle && node --stack-trace-limit=1000 --single-threaded --expose_wasm $(MAIN_JS) $(ARGS)
<EnableDefaultWasmAssembliesToBundle Condition="'$(EnableDefaultWasmAssembliesToBundle)' == ''">true</EnableDefaultWasmAssembliesToBundle>
<WasmBuildOnlyAfterPublish Condition="'$(WasmBuildOnlyAfterPublish)' == '' and '$(DeployOnBuild)' == 'true'">true</WasmBuildOnlyAfterPublish>
-
- <!-- Temporarily `false`, till sdk gets a fix for supporting the new file -->
- <WasmEmitSymbolMap Condition="'$(WasmEmitSymbolMap)' == ''">false</WasmEmitSymbolMap>
</PropertyGroup>
<!-- PUBLISH -->
<WasmBuildAppDependsOn>PrepareForWasmBuildApp;$(WasmBuildAppDependsOn)</WasmBuildAppDependsOn>
<WasmGenerateAppBundle>true</WasmGenerateAppBundle>
<OutputType>library</OutputType>
+ <WasmEmitSymbolMap>true</WasmEmitSymbolMap>
</PropertyGroup>
<ItemGroup>
import { VoidPtr, CharPtr } from "./types/emscripten";
const commands_received : any = new Map<number, CommandResponse>();
+const wasm_func_map = new Map<number, string>();
commands_received.remove = function (key: number) : CommandResponse { const value = this.get(key); this.delete(key); return value;};
let _call_function_res_cache: any = {};
let _next_call_function_res_id = 0;
let _debugger_buffer_len = -1;
let _debugger_buffer: VoidPtr;
+const regexes:any[] = [];
+
+// V8
+// at <anonymous>:wasm-function[1900]:0x83f63
+// at dlfree (<anonymous>:wasm-function[18739]:0x2328ef)
+regexes.push(/at (?<replaceSection>[^:()]+:wasm-function\[(?<funcNum>\d+)\]:0x[a-fA-F\d]+)((?![^)a-fA-F\d])|$)/);
+
+//# 5: WASM [009712b2], function #111 (''), pc=0x7c16595c973 (+0x53), pos=38740 (+11)
+regexes.push(/(?:WASM \[[\da-zA-Z]+\], (?<replaceSection>function #(?<funcNum>[\d]+) \(''\)))/);
+
+//# chrome
+//# at http://127.0.0.1:63817/dotnet.wasm:wasm-function[8963]:0x1e23f4
+regexes.push(/(?<replaceSection>[a-z]+:\/\/[^ )]*:wasm-function\[(?<funcNum>\d+)\]:0x[a-fA-F\d]+)/);
+
+//# <?>.wasm-function[8962]
+regexes.push(/(?<replaceSection><[^ >]+>[.:]wasm-function\[(?<funcNum>[0-9]+)\])/);
+
export function mono_wasm_runtime_ready(): void {
runtimeHelpers.mono_wasm_runtime_is_ready = true;
debugger;
else
console.debug("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28");
+
+ _readSymbolMapFile("dotnet.js.symbols");
}
export function mono_wasm_fire_debugger_agent_message(): void {
console.debug(`Debugger.Debug: ${message}`);
}
+function _readSymbolMapFile(filename: string): void {
+ try {
+ const res = Module.FS_readFile(filename, {flags: "r", encoding: "utf8"});
+ res.split(/[\r\n]/).forEach((line: string) => {
+ const parts:string[] = line.split(/:/);
+ if (parts.length < 2)
+ return;
+
+ parts[1] = parts.splice(1).join(":");
+ wasm_func_map.set(Number(parts[0]), parts[1]);
+ });
+
+ console.debug(`Loaded ${wasm_func_map.size} symbols`);
+ } catch (error:any) {
+ if (error.errno == 44) // NOENT
+ console.debug(`Could not find symbols file ${filename}. Ignoring.`);
+ else
+ console.log(`Error loading symbol file ${filename}: ${JSON.stringify(error)}`);
+ return;
+ }
+}
+
+export function mono_wasm_symbolicate_string(message: string): string {
+ try {
+ if (wasm_func_map.size == 0)
+ return message;
+
+ const origMessage = message;
+
+ for (let i = 0; i < regexes.length; i ++)
+ {
+ const newRaw = message.replace(new RegExp(regexes[i], "g"), (substring, ...args) => {
+ const groups = args.find(arg => {
+ return typeof(arg) == "object" && arg.replaceSection !== undefined;
+ });
+
+ if (groups === undefined)
+ return substring;
+
+ const funcNum = groups.funcNum;
+ const replaceSection = groups.replaceSection;
+ const name = wasm_func_map.get(Number(funcNum));
+
+ if (name === undefined)
+ return substring;
+
+ return substring.replace(replaceSection, `${name} (${replaceSection})`);
+ });
+
+ if (newRaw !== origMessage)
+ return newRaw;
+ }
+
+ return origMessage;
+ } catch (error) {
+ console.debug(`failed to symbolicate: ${error}`);
+ return message;
+ }
+}
+
+export function mono_wasm_stringify_as_error_with_stack(err: Error | string): string {
+ let errObj: any = err;
+ if (!(err instanceof Error))
+ errObj = new Error(err);
+
+ // Error
+ return mono_wasm_symbolicate_string(errObj.stack);
+}
+
export function mono_wasm_trace_logger(log_domain_ptr: CharPtr, log_level_ptr: CharPtr, message_ptr: CharPtr, fatal: number, user_data: VoidPtr): void {
- const message = Module.UTF8ToString(message_ptr);
+ const origMessage = Module.UTF8ToString(message_ptr);
const isFatal = !!fatal;
- const domain = Module.UTF8ToString(log_domain_ptr); // is this always Mono?
+ const domain = Module.UTF8ToString(log_domain_ptr);
const dataPtr = user_data;
const log_level = Module.UTF8ToString(log_level_ptr);
+ const message = `[MONO] ${origMessage}`;
+
if (INTERNAL["logging"] && typeof INTERNAL.logging["trace"] === "function") {
INTERNAL.logging.trace(domain, log_level, message, isFatal, dataPtr);
return;
}
- if (isFatal)
- console.trace(message);
-
switch (log_level) {
case "critical":
case "error":
- console.error(message);
+ console.error(mono_wasm_stringify_as_error_with_stack(message));
break;
case "warning":
console.warn(message);
className?: string,
description?: string,
objectId?: string
-}
\ No newline at end of file
+}
UTF8ArrayToString(u8Array: Uint8Array, idx?: number, maxBytesToRead?: number): string;
FS_createPath(parent: string, path: string, canRead?: boolean, canWrite?: boolean): string;
FS_createDataFile(parent: string, name: string, data: TypedArray, canRead: boolean, canWrite: boolean, canOwn?: boolean): string;
+ FS_readFile(filename: string, opts: any): any;
removeRunDependency(id: string): void;
addRunDependency(id: string): void;
ready: Promise<unknown>;
mono_wasm_trace_logger,
mono_wasm_add_dbg_command_received,
mono_wasm_change_debugger_log_level,
+ mono_wasm_symbolicate_string,
+ mono_wasm_stringify_as_error_with_stack,
} from "./debug";
import { ENVIRONMENT_IS_WEB, ExitStatusError, runtimeHelpers, setImportsAndExports } from "./imports";
import { DotnetModuleConfigImports, DotnetModule } from "./types";
// with mono_wasm_debugger_log and mono_wasm_trace_logger
logging: undefined,
+ //
+ mono_wasm_symbolicate_string,
+ mono_wasm_stringify_as_error_with_stack,
+
// used in debugger DevToolsHelper.cs
mono_wasm_get_loaded_files,
mono_wasm_send_dbg_command_with_parms,
export let quit: Function;
export let ExitStatus: ExitStatusError;
export let requirePromise: Promise<Function>;
+export let readFile: Function;
export interface ExitStatusError {
new(status: number): any;
else
res += "\n" + stack;
}
+
+ res = INTERNAL.mono_wasm_symbolicate_string(res);
}
if (is_exception) {
Module.setValue(is_exception, 1, "i32");
-import { ExitStatus, Module, quit } from "./imports";
+import { ExitStatus, INTERNAL, Module, quit } 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<void> {
try {
const result = await mono_run_main(main_assembly_name, args);
function set_exit_code(exit_code: number, reason?: any) {
if (reason && !(reason instanceof ExitStatus)) {
- Module.printErr(reason.toString());
- if (reason.stack) {
- Module.printErr(reason.stack);
- }
+ if (reason instanceof Error)
+ Module.printErr(INTERNAL.mono_wasm_stringify_as_error_with_stack(reason));
+ else if (typeof reason == "string")
+ Module.printErr(reason);
+ else
+ Module.printErr(JSON.stringify(reason));
}
else {
reason = new ExitStatus(exit_code);
UTF8ArrayToString(u8Array: Uint8Array, idx?: number, maxBytesToRead?: number): string;
FS_createPath(parent: string, path: string, canRead?: boolean, canWrite?: boolean): string;
FS_createDataFile(parent: string, name: string, data: TypedArray, canRead: boolean, canWrite: boolean, canOwn?: boolean): string;
+ FS_readFile(filename: string, opts: any): any;
removeRunDependency(id: string): void;
addRunDependency(id: string): void;
console[m] = proxyConsoleMethod(`console.${m}`, send, true);
}
+function stringify_as_error_with_stack(err) {
+ if (!err)
+ return "";
+
+ if (App && App.INTERNAL)
+ return App.INTERNAL.mono_wasm_stringify_as_error_with_stack(err);
+
+ if (err.stack)
+ return err.stack;
+
+ if (typeof err == "string")
+ return err;
+
+ return JSON.stringify(err);
+}
+
if (typeof globalThis.crypto === 'undefined') {
// **NOTE** this is a simple insecure polyfill for testing purposes only
// /dev/random doesn't work on js shells, so define our own
App.init({ MONO, INTERNAL, BINDING, Module });
},
onAbort: (error) => {
- console.log("ABORT: " + error);
- const err = new Error();
- console.log("Stacktrace: \n");
- console.error(err.stack);
- set_exit_code(1, error);
+ set_exit_code(1, stringify_as_error_with_stack(new Error()));
},
}))
}).catch(function (err) {
- console.error(err);
- set_exit_code(1, "failed to load the dotnet.js file.\n" + err);
+ set_exit_code(1, "failed to load the dotnet.js file.\n" + stringify_as_error_with_stack(err));
});
const App = {
function set_exit_code(exit_code, reason) {
if (reason) {
- console.error(`${JSON.stringify(reason)}`);
- if (reason.stack) {
- console.error(reason.stack);
- }
+ if (reason instanceof Error)
+ console.error(stringify_as_error_with_stack(reason));
+ else if (typeof reason == "string")
+ console.error(reason);
+ else
+ console.error(JSON.stringify(reason));
}
if (is_browser) {
<_EmccLinkFlags Include="-s ALLOW_MEMORY_GROWTH=1" />
<_EmccLinkFlags Include="-s NO_EXIT_RUNTIME=1" />
<_EmccLinkFlags Include="-s FORCE_FILESYSTEM=1" />
- <_EmccLinkFlags Include="-s EXPORTED_RUNTIME_METHODS="['FS','print','ccall','cwrap','setValue','getValue','UTF8ToString','UTF8ArrayToString','FS_createPath','FS_createDataFile','removeRunDependency','addRunDependency']"" />
+ <_EmccLinkFlags Include="-s EXPORTED_RUNTIME_METHODS="['FS','print','ccall','cwrap','setValue','getValue','UTF8ToString','UTF8ArrayToString','FS_createPath','FS_createDataFile','removeRunDependency','addRunDependency', 'FS_readFile']"" />
<!-- _htons,_ntohs,__get_daylight,__get_timezone,__get_tzname are exported temporarily, until the issue is fixed in emscripten, https://github.com/dotnet/runtime/issues/64724 -->
<_EmccLinkFlags Include="-s EXPORTED_FUNCTIONS=_free,_malloc,_htons,_ntohs,__get_daylight,__get_timezone,__get_tzname,_memalign" />
<_EmccLinkFlags Include="--source-map-base http://example.com" />