diff options
Diffstat (limited to 'sdks/wasm')
-rw-r--r-- | sdks/wasm/BrowserDebugHost/BrowserDebugHost.csproj (renamed from sdks/wasm/ProxyDriver/ProxyDriver.csproj) | 2 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugHost/Program.cs (renamed from sdks/wasm/ProxyDriver/Program.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugHost/Properties/launchSettings.json (renamed from sdks/wasm/ProxyDriver/Properties/launchSettings.json) | 2 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugHost/Startup.cs (renamed from sdks/wasm/ProxyDriver/Startup.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugHost/TestHarnessStartup.cs (renamed from sdks/wasm/ProxyDriver/TestHarnessStartup.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/.editorconfig (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/.editorconfig) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/AssemblyInfo.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/AssemblyInfo.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/BrowserDebugProxy.csproj (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/Mono.WebAssembly.DebuggerProxy.csproj) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/BrowserDebugProxy.sln (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/Mono.WebAssembly.DebuggerProxy.sln) | 4 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/DebugStore.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/DebugStore.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/DebuggerProxy.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/DebuggerProxy.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/DevToolsHelper.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/DevToolsHelper.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/DevToolsProxy.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/DevToolsProxy.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/EvaluateExpression.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/EvaluateExpression.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/BrowserDebugProxy/MonoProxy.cs (renamed from sdks/wasm/Mono.WebAssembly.DebuggerProxy/MonoProxy.cs) | 0 | ||||
-rw-r--r-- | sdks/wasm/DebuggerTestSuite/DebuggerTestSuite.csproj | 4 | ||||
-rw-r--r-- | sdks/wasm/Makefile | 8 | ||||
-rw-r--r-- | sdks/wasm/README.md | 2 | ||||
-rw-r--r-- | sdks/wasm/package-update/download-packages.ps1 | 2 | ||||
-rw-r--r-- | sdks/wasm/package-update/download-packages.sh | 2 | ||||
-rw-r--r-- | sdks/wasm/src/library_mono.js | 1350 |
21 files changed, 974 insertions, 402 deletions
diff --git a/sdks/wasm/ProxyDriver/ProxyDriver.csproj b/sdks/wasm/BrowserDebugHost/BrowserDebugHost.csproj index c0551057f5a..f1765749813 100644 --- a/sdks/wasm/ProxyDriver/ProxyDriver.csproj +++ b/sdks/wasm/BrowserDebugHost/BrowserDebugHost.csproj @@ -9,7 +9,7 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\Mono.WebAssembly.DebuggerProxy\Mono.WebAssembly.DebuggerProxy.csproj" /> + <ProjectReference Include="..\BrowserDebugProxy\BrowserDebugProxy.csproj" /> </ItemGroup> </Project> diff --git a/sdks/wasm/ProxyDriver/Program.cs b/sdks/wasm/BrowserDebugHost/Program.cs index faaa4960c58..faaa4960c58 100644 --- a/sdks/wasm/ProxyDriver/Program.cs +++ b/sdks/wasm/BrowserDebugHost/Program.cs diff --git a/sdks/wasm/ProxyDriver/Properties/launchSettings.json b/sdks/wasm/BrowserDebugHost/Properties/launchSettings.json index 22258c4cb66..0acb48fc972 100644 --- a/sdks/wasm/ProxyDriver/Properties/launchSettings.json +++ b/sdks/wasm/BrowserDebugHost/Properties/launchSettings.json @@ -15,7 +15,7 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "ProxyDriver": { + "BrowserDebugHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { diff --git a/sdks/wasm/ProxyDriver/Startup.cs b/sdks/wasm/BrowserDebugHost/Startup.cs index e18df90c39c..e18df90c39c 100644 --- a/sdks/wasm/ProxyDriver/Startup.cs +++ b/sdks/wasm/BrowserDebugHost/Startup.cs diff --git a/sdks/wasm/ProxyDriver/TestHarnessStartup.cs b/sdks/wasm/BrowserDebugHost/TestHarnessStartup.cs index 6ae4ba32b76..6ae4ba32b76 100644 --- a/sdks/wasm/ProxyDriver/TestHarnessStartup.cs +++ b/sdks/wasm/BrowserDebugHost/TestHarnessStartup.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/.editorconfig b/sdks/wasm/BrowserDebugProxy/.editorconfig index ca41c33264a..ca41c33264a 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/.editorconfig +++ b/sdks/wasm/BrowserDebugProxy/.editorconfig diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/AssemblyInfo.cs b/sdks/wasm/BrowserDebugProxy/AssemblyInfo.cs index 8e935c7ed0d..8e935c7ed0d 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/AssemblyInfo.cs +++ b/sdks/wasm/BrowserDebugProxy/AssemblyInfo.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/Mono.WebAssembly.DebuggerProxy.csproj b/sdks/wasm/BrowserDebugProxy/BrowserDebugProxy.csproj index 4063416ceb0..4063416ceb0 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/Mono.WebAssembly.DebuggerProxy.csproj +++ b/sdks/wasm/BrowserDebugProxy/BrowserDebugProxy.csproj diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/Mono.WebAssembly.DebuggerProxy.sln b/sdks/wasm/BrowserDebugProxy/BrowserDebugProxy.sln index c99f5389610..cfb208d1562 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/Mono.WebAssembly.DebuggerProxy.sln +++ b/sdks/wasm/BrowserDebugProxy/BrowserDebugProxy.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.28407.52 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ProxyDriver", "..\ProxyDriver\ProxyDriver.csproj", "{954F768A-23E6-4B14-90E0-27EA6B41FBCC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserDebugHost", "..\BrowserDebugHost\BrowserDebugHost.csproj", "{954F768A-23E6-4B14-90E0-27EA6B41FBCC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.WebAssembly.DebuggerProxy", "Mono.WebAssembly.DebuggerProxy.csproj", "{490128B6-9F21-46CA-878A-F22BCF51EF3C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BrowserDebugProxy", "BrowserDebugProxy.csproj", "{490128B6-9F21-46CA-878A-F22BCF51EF3C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DebugStore.cs b/sdks/wasm/BrowserDebugProxy/DebugStore.cs index f633fe786d5..f633fe786d5 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DebugStore.cs +++ b/sdks/wasm/BrowserDebugProxy/DebugStore.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DebuggerProxy.cs b/sdks/wasm/BrowserDebugProxy/DebuggerProxy.cs index 74566c56024..74566c56024 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DebuggerProxy.cs +++ b/sdks/wasm/BrowserDebugProxy/DebuggerProxy.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DevToolsHelper.cs b/sdks/wasm/BrowserDebugProxy/DevToolsHelper.cs index a615540e450..a615540e450 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DevToolsHelper.cs +++ b/sdks/wasm/BrowserDebugProxy/DevToolsHelper.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DevToolsProxy.cs b/sdks/wasm/BrowserDebugProxy/DevToolsProxy.cs index 5eac86d124a..5eac86d124a 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/DevToolsProxy.cs +++ b/sdks/wasm/BrowserDebugProxy/DevToolsProxy.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/EvaluateExpression.cs b/sdks/wasm/BrowserDebugProxy/EvaluateExpression.cs index 7bcf6f81174..7bcf6f81174 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/EvaluateExpression.cs +++ b/sdks/wasm/BrowserDebugProxy/EvaluateExpression.cs diff --git a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/MonoProxy.cs b/sdks/wasm/BrowserDebugProxy/MonoProxy.cs index 5169d1ccfff..5169d1ccfff 100644 --- a/sdks/wasm/Mono.WebAssembly.DebuggerProxy/MonoProxy.cs +++ b/sdks/wasm/BrowserDebugProxy/MonoProxy.cs diff --git a/sdks/wasm/DebuggerTestSuite/DebuggerTestSuite.csproj b/sdks/wasm/DebuggerTestSuite/DebuggerTestSuite.csproj index c00fda67aaf..8cd99732b99 100644 --- a/sdks/wasm/DebuggerTestSuite/DebuggerTestSuite.csproj +++ b/sdks/wasm/DebuggerTestSuite/DebuggerTestSuite.csproj @@ -12,8 +12,8 @@ </ItemGroup> <ItemGroup> - <ProjectReference Include="..\ProxyDriver\ProxyDriver.csproj" /> - <ProjectReference Include="..\Mono.WebAssembly.DebuggerProxy\Mono.WebAssembly.DebuggerProxy.csproj" /> + <ProjectReference Include="..\BrowserDebugHost\BrowserDebugHost.csproj" /> + <ProjectReference Include="..\BrowserDebugProxy\BrowserDebugProxy.csproj" /> </ItemGroup> </Project> diff --git a/sdks/wasm/Makefile b/sdks/wasm/Makefile index 29553fcd7b2..1e0faa2400f 100644 --- a/sdks/wasm/Makefile +++ b/sdks/wasm/Makefile @@ -674,7 +674,7 @@ build-debugger-test-app: bin/debugger-test-suite/dotnet.wasm build-managed: build-debug-sample build-test-suite build-dbg-proxy: - $(DOTNET_BUILD) ProxyDriver + $(DOTNET_BUILD) BrowserDebugHost build-dbg-testsuite: $(DOTNET_BUILD) DebuggerTestSuite @@ -791,10 +791,10 @@ package: build build-sdk build-dbg-proxy cp $(OPTIONS_CS) tmp/ cp packager.exe tmp/ cp runtime.js tmp/ - cp Mono.WebAssembly.DebuggerProxy/bin/Debug/netstandard2.1/Mono.WebAssembly.DebuggerProxy.dll tmp/ - cp Mono.WebAssembly.DebuggerProxy/bin/Debug/netstandard2.1/Mono.WebAssembly.DebuggerProxy.pdb tmp/ + cp BrowserDebugProxy/bin/Debug/netstandard2.1/BrowserDebugProxy.dll tmp/ + cp BrowserDebugProxy/bin/Debug/netstandard2.1/BrowserDebugProxy.pdb tmp/ mkdir tmp/dbg-proxy - cp -r ProxyDriver/bin/Debug/netcoreapp3.0/ tmp/dbg-proxy/ + cp -r BrowserDebugHost/bin/Debug/netcoreapp3.0/ tmp/dbg-proxy/ mkdir tmp/docs cp -r ./docs/ tmp/docs/ mkdir tmp/packages diff --git a/sdks/wasm/README.md b/sdks/wasm/README.md index ff1bb31805b..95a3d133124 100644 --- a/sdks/wasm/README.md +++ b/sdks/wasm/README.md @@ -197,7 +197,7 @@ To experiment with the debugger, do the following steps: - When calling `packager.exe` pass the `-debug` argument to it. - Start Chrome with remote debugging enabled (IE `/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222`) -- Run the proxy: `dotnet run -p ProxyDriver/ProxyDriver.csproj` +- Run the proxy: `dotnet run -p BrowserDebugHost/BrowserDebugHost.csproj` - Connect to the proxy by visiting http://localhost:9300/ and select the tab you wish to debug from the list of tabs - Refresh the debugged page and you should be set diff --git a/sdks/wasm/package-update/download-packages.ps1 b/sdks/wasm/package-update/download-packages.ps1 index 2c12a12d082..1452bcd2f1b 100644 --- a/sdks/wasm/package-update/download-packages.ps1 +++ b/sdks/wasm/package-update/download-packages.ps1 @@ -40,7 +40,7 @@ $NUGET_HOME="~\\.nuget" $PACKAGE_PATH="$NUGET_HOME\\packages\\microsoft.aspnetcore.components.webassembly.runtime\\$runtime\\tools\\dotnetwasm" $PROXY_PACKAGE_PATH="$NUGET_HOME\\packages\\microsoft.aspnetcore.components.webassembly.devserver\\$runtime\\tools\\BlazorDebugProxy" $ASP_PROXY_PATH="$asp_working_dir\\src\\Components\\WebAssembly\\DebugProxy\\src" -$MONO_PROXY_PATH="$mono_working_dir\\sdks\\wasm\\Mono.WebAssembly.DebuggerProxy" +$MONO_PROXY_PATH="$mono_working_dir\\sdks\\wasm\\BrowserDebugProxy" $TMP_DIR=(mktemp -d) $TMP_PKG_DIR="$TMP_DIR\\wasm-package" mkdir $TMP_DIR diff --git a/sdks/wasm/package-update/download-packages.sh b/sdks/wasm/package-update/download-packages.sh index 22350c6913a..ad83c462f30 100644 --- a/sdks/wasm/package-update/download-packages.sh +++ b/sdks/wasm/package-update/download-packages.sh @@ -63,7 +63,7 @@ NUGET_HOME=${NUGET_HOME:-"$HOME/.nuget"} PACKAGE_PATH="$NUGET_HOME/packages/microsoft.aspnetcore.components.webassembly.runtime/$RUNTIME_VER/tools/dotnetwasm" PROXY_PACKAGE_PATH="$NUGET_HOME/packages/microsoft.aspnetcore.components.webassembly.devserver/$RUNTIME_VER/tools/BlazorDebugProxy" ASP_PROXY_PATH="$ASPNETCORE/src/Components/WebAssembly/DebugProxy/src" -MONO_PROXY_PATH="$MONO/sdks/wasm/Mono.WebAssembly.DebuggerProxy" +MONO_PROXY_PATH="$MONO/sdks/wasm/BrowserDebugProxy" TMP_DIR=`mktemp -d` TMP_PKG_DIR=$TMP_DIR/wasm-package mkdir $TMP_PKG_DIR diff --git a/sdks/wasm/src/library_mono.js b/sdks/wasm/src/library_mono.js index 8e8f9080be2..14e191e79cd 100644 --- a/sdks/wasm/src/library_mono.js +++ b/sdks/wasm/src/library_mono.js @@ -1,5 +1,15 @@ -/* jshint esversion: 6 */ -/* jshint evil: true */ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/** + * @typedef WasmId + * @type {object} + * @property {string} idStr - full object id string + * @property {string} scheme - eg, object, valuetype, array .. + * @property {string} value - string part after `dotnet:scheme:` of the id string + * @property {object} o - value parsed as JSON + */ + var MonoSupportLib = { $MONO__postset: 'MONO.export_functions (Module);', $MONO: { @@ -8,6 +18,10 @@ var MonoSupportLib = { _vt_stack: [], mono_wasm_runtime_is_ready : false, mono_wasm_ignore_pdb_load_errors: true, + + /** @type {object.<string, object>} */ + _id_table: {}, + pump_message: function () { if (!this.mono_background_exec) this.mono_background_exec = Module.cwrap ("mono_background_exec", null); @@ -24,6 +38,11 @@ var MonoSupportLib = { export_functions: function (module) { module ["pump_message"] = MONO.pump_message; module ["mono_load_runtime_and_bcl"] = MONO.mono_load_runtime_and_bcl; + module ["mono_load_runtime_and_bcl_args"] = MONO.mono_load_runtime_and_bcl_args; + module ["mono_wasm_load_bytes_into_heap"] = MONO.mono_wasm_load_bytes_into_heap; + module ["mono_wasm_load_icu_data"] = MONO.mono_wasm_load_icu_data; + module ["mono_wasm_globalization_init"] = MONO.mono_wasm_globalization_init; + module ["mono_wasm_get_loaded_files"] = MONO.mono_wasm_get_loaded_files; }, mono_text_decoder: undefined, @@ -43,11 +62,11 @@ var MonoSupportLib = { decode: function (start, end, save) { if (!MONO.mono_text_decoder) { MONO.mono_text_decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : undefined; - } + } var str = ""; if (MONO.mono_text_decoder) { - // When threading is enabled, TextDecoder does not accept a view of a + // When threading is enabled, TextDecoder does not accept a view of a // SharedArrayBuffer, we must make a copy of the array first. var subArray = typeof SharedArrayBuffer !== 'undefined' && Module.HEAPU8.buffer instanceof SharedArrayBuffer ? Module.HEAPU8.slice(start, end) @@ -67,6 +86,12 @@ var MonoSupportLib = { }, }, + mono_wasm_get_exception_object: function() { + var exception_obj = MONO.active_exception; + MONO.active_exception = null; + return exception_obj ; + }, + mono_wasm_get_call_stack: function() { if (!this.mono_wasm_current_bp_id) this.mono_wasm_current_bp_id = Module.cwrap ("mono_wasm_current_bp_id", 'number'); @@ -86,30 +111,19 @@ var MonoSupportLib = { }, _fixup_name_value_objects: function (var_list) { - var out_list = []; - - var _fixup_value = function (value) { - if (value != null && value != undefined) { - var descr = value.description; - if (descr == null || descr == undefined) - value.description = '' + value.value; - } - return value; - }; + let out_list = []; var i = 0; while (i < var_list.length) { - var o = var_list [i]; - var name = o.name; + let o = var_list [i]; + const name = o.name; if (name == null || name == undefined) { i ++; - o.value = _fixup_value(o.value); out_list.push (o); continue; } if (i + 1 < var_list.length) { - _fixup_value(var_list[i + 1].value); o = Object.assign (o, var_list [i + 1]); } @@ -121,8 +135,8 @@ var MonoSupportLib = { }, _filter_automatic_properties: function (props) { - var names_found = {}; - var final_var_list = []; + let names_found = {}; + let final_var_list = []; for (var i in props) { var p = props [i]; @@ -141,48 +155,116 @@ var MonoSupportLib = { return final_var_list; }, - // code from https://stackoverflow.com/a/5582308 - // - // Given `dotnet:object:foo:bar`, - // returns [ 'dotnet', 'object', 'foo:bar'] - _split_object_id: function (id, delim = ':', count = 3) { - if (id === undefined || id == "") - return []; + /** Given `dotnet:object:foo:bar`, + * returns { scheme:'object', value: 'foo:bar' } + * + * Given `dotnet:pointer:{ b: 3 }` + * returns { scheme:'object', value: '{b:3}`, o: {b:3} + * + * @param {string} idStr + * @param {boolean} [throwOnError=false] + * + * @returns {WasmId} + */ + _parse_object_id: function (idStr, throwOnError = false) { + if (idStr === undefined || idStr == "" || !idStr.startsWith ('dotnet:')) { + if (throwOnError) + throw new Error (`Invalid id: ${idStr}`); + + return undefined; + } + + const [, scheme, ...rest] = idStr.split(':'); + let res = { + scheme, + value: rest.join (':'), + idStr, + o: {} + }; - if (delim === undefined) delim = ':'; - if (count === undefined) count = 3; + try { + res.o = JSON.parse(res.value); + // eslint-disable-next-line no-empty + } catch (e) {} - var arr = id.split (delim); - var result = arr.splice (0, count - 1); + return res; + }, - if (arr.length > 0) - result.push (arr.join (delim)); - return result; + /** + * @param {WasmId} id + * @returns {object[]} + */ + _get_vt_properties: function (id) { + let entry = this._id_table [id.idStr]; + if (entry !== undefined && entry.members !== undefined) + return entry.members; + + if (!isNaN (id.o.containerId)) + this._get_object_properties (id.o.containerId, true); + else if (!isNaN (id.o.arrayId)) + this._get_array_values (id, Number (id.o.arrayIdx), 1, true); + else + throw new Error (`Invalid valuetype id (${id.idStr}). Can't get properties for it.`); + + entry = this._get_id_props (id.idStr); + if (entry !== undefined && entry.members !== undefined) + return entry.members; + + throw new Error (`Unknown valuetype id: ${id.idStr}`); + }, + + /** + * + * @callback GetIdArgsCallback + * @param {object} var + * @param {number} idx + * @returns {object} + */ + + /** + * @param {object[]} vars + * @param {GetIdArgsCallback} getIdArgs + * @returns {object} + */ + _assign_vt_ids: function (vars, getIdArgs) + { + vars.forEach ((v, i) => { + // we might not have a `.value`, like in case of getters which have a `.get` instead + const value = v.value; + if (value === undefined || !value.isValueType) + return; + + if (value.objectId !== undefined) + throw new Error (`Bug: Trying to assign valuetype id, but the var already has one: ${v}`); + + value.objectId = this._new_or_add_id_props ({ scheme: 'valuetype', idArgs: getIdArgs (v, i), props: value._props }); + delete value._props; + }); + + return vars; }, // // @var_list: [ { index: <var_id>, name: <var_name> }, .. ] mono_wasm_get_variables: function(scope, var_list) { - if (!this.mono_wasm_get_var_info) - this.mono_wasm_get_var_info = Module.cwrap ("mono_wasm_get_var_info", null, [ 'number', 'number', 'number']); - - this.var_info = []; - var numBytes = var_list.length * Int32Array.BYTES_PER_ELEMENT; - var ptr = Module._malloc(numBytes); - var heapBytes = new Int32Array(Module.HEAP32.buffer, ptr, numBytes); + const numBytes = var_list.length * Int32Array.BYTES_PER_ELEMENT; + const ptr = Module._malloc(numBytes); + let heapBytes = new Int32Array(Module.HEAP32.buffer, ptr, numBytes); for (let i=0; i<var_list.length; i++) { heapBytes[i] = var_list[i].index; } this._async_method_objectId = 0; - this.mono_wasm_get_var_info (scope, heapBytes.byteOffset, var_list.length); + let { res_ok, res } = this.mono_wasm_get_local_vars_info (scope, heapBytes.byteOffset, var_list.length); Module._free(heapBytes.byteOffset); - var res = MONO._fixup_name_value_objects (this.var_info); + if (!res_ok) + throw new Error (`Failed to get locals for scope ${scope}`); - for (let i in res) { - var res_name = res [i].name; + if (this._async_method_objectId != 0) + this._assign_vt_ids (res, v => ({ containerId: this._async_method_objectId, fieldOffset: v.fieldOffset })); - var value = res[i].value; + for (let i in res) { + const res_name = res [i].name; if (this._async_method_objectId != 0) { //Async methods are special in the way that local variables can be lifted to generated class fields //value of "this" comes here either @@ -191,9 +273,6 @@ var MonoSupportLib = { // ALTHOUGH, the name wouldn't have `<>` for method args res [i].name = res_name.substring (1, res_name.indexOf ('>')); } - - if (value.isValueType) - value.objectId = `dotnet:valuetype:${this._async_method_objectId}:${res [i].fieldOffset}`; } else if (res_name === undefined && var_list [i] !== undefined) { // For non-async methods, we just have the var id, but we have the name // from the caller @@ -202,69 +281,49 @@ var MonoSupportLib = { } this._post_process_details(res); - this.var_info = [] - return res; }, - mono_wasm_get_object_properties: function(objId, expandValueTypes) { - if (!this.mono_wasm_get_object_properties_info) - this.mono_wasm_get_object_properties_info = Module.cwrap ("mono_wasm_get_object_properties", null, [ 'number', 'bool' ]); - - this.var_info = []; - this.mono_wasm_get_object_properties_info (objId, expandValueTypes); - - var res = MONO._filter_automatic_properties (MONO._fixup_name_value_objects (this.var_info)); - for (var i = 0; i < res.length; i++) { - var res_val = res [i].value; - // we might not have a `.value`, like in case of getters which have a `.get` instead - if (res_val !== undefined && res_val.isValueType != undefined && res_val.isValueType) - res_val.objectId = `dotnet:valuetype:${objId}:${res [i].fieldOffset}`; - } - - this.var_info = []; + /** + * @param {number} idNum + * @param {boolean} expandValueTypes + * @returns {object} + */ + _get_object_properties: function(idNum, expandValueTypes) { + let { res_ok, res } = this.mono_wasm_get_object_properties_info (idNum, expandValueTypes); + if (!res_ok) + throw new Error (`Failed to get properties for ${idNum}`); + + res = MONO._filter_automatic_properties (res); + res = this._assign_vt_ids (res, v => ({ containerId: idNum, fieldOffset: v.fieldOffset })); + res = this._post_process_details (res); return res; }, - mono_wasm_get_array_values: function(objId) { - if (!this.mono_wasm_get_array_values_info) - this.mono_wasm_get_array_values_info = Module.cwrap ("mono_wasm_get_array_values", null, [ 'number' ]); - - this.var_info = []; - this.mono_wasm_get_array_values_info (objId); - - var res = MONO._fixup_name_value_objects (this.var_info); - for (var i = 0; i < res.length; i++) { - var prop_value = res [i].value; - if (prop_value.isValueType) { - res [i].value.objectId = `dotnet:array:${objId}:${i}`; - } else if (prop_value.objectId !== undefined && prop_value.objectId.startsWith("dotnet:pointer")) { - prop_value.objectId = this._get_updated_ptr_id (prop_value.objectId, { - varName: `[${i}]` - }); - } + /** + * @param {WasmId} id + * @param {number} [startIdx=0] + * @param {number} [count=-1] + * @param {boolean} [expandValueTypes=false] + * @returns {object[]} + */ + _get_array_values: function (id, startIdx = 0, count = -1, expandValueTypes = false) { + if (isNaN (id.o.arrayId) || isNaN (startIdx)) + throw new Error (`Invalid array id: ${id.idStr}`); + + let { res_ok, res } = this.mono_wasm_get_array_values_info (id.o.arrayId, startIdx, count, expandValueTypes); + if (!res_ok) + throw new Error (`Failed to get properties for array id ${id.idStr}`); + + res = this._assign_vt_ids (res, (_, i) => ({ arrayId: id.o.arrayId, arrayIdx: Number (startIdx) + i})); + + for (let i = 0; i < res.length; i ++) { + let value = res [i].value; + if (value.objectId !== undefined && value.objectId.startsWith("dotnet:pointer")) + this._new_or_add_id_props ({ objectId: value.objectId, props: { varName: `[${i}]` } }); } - - this.var_info = []; - - return res; - }, - - mono_wasm_get_array_value_expanded: function(objId, idx) { - if (!this.mono_wasm_get_array_value_expanded_info) - this.mono_wasm_get_array_value_expanded_info = Module.cwrap ("mono_wasm_get_array_value_expanded", null, [ 'number', 'number' ]); - - this.var_info = []; - this.mono_wasm_get_array_value_expanded_info (objId, idx); - - var res = MONO._fixup_name_value_objects (this.var_info); - // length should be exactly one! - if (res [0].value.isValueType != undefined && res [0].value.isValueType) - res [0].value.objectId = `dotnet:array:${objId}:${idx}`; - - this.var_info = []; - + res = this._post_process_details (res); return res; }, @@ -278,8 +337,13 @@ var MonoSupportLib = { return details; }, - _next_value_type_id: function () { - return ++this._next_value_type_id_var; + /** + * Gets the next id number to use for generating ids + * + * @returns {number} + */ + _next_id: function () { + return ++this._next_id_var; }, _extract_and_cache_value_types: function (var_list) { @@ -287,82 +351,46 @@ var MonoSupportLib = { return var_list; for (let i in var_list) { - var value = var_list [i].value; + let value = var_list [i].value; if (value === undefined) continue; if (value.objectId !== undefined && value.objectId.startsWith ("dotnet:pointer:")) { - var ptr_args = this._get_ptr_args (value.objectId); - if (ptr_args.varName === undefined) { - // It might have been already set in some cases, like arrays - // where the name would be `0`, but we want `[0]` for pointers, - // so the deref would look like `*[0]` - value.objectId = this._get_updated_ptr_id (value.objectId, { - varName: var_list [i].name - }); - } + let ptr_args = this._get_id_props (value.objectId); + if (ptr_args === undefined) + throw new Error (`Bug: Expected to find an entry for pointer id: ${value.objectId}`); + + // It might have been already set in some cases, like arrays + // where the name would be `0`, but we want `[0]` for pointers, + // so the deref would look like `*[0]` + ptr_args.varName = ptr_args.varName || var_list [i].name; } if (value.type != "object" || value.isValueType != true || value.expanded != true) // undefined would also give us false continue; // Generate objectId for expanded valuetypes - - var objectId = value.objectId; - if (objectId == undefined) - objectId = `dotnet:valuetype:${this._next_value_type_id ()}`; - value.objectId = objectId; + value.objectId = value.objectId || this._new_or_add_id_props ({ scheme: 'valuetype' }); this._extract_and_cache_value_types (value.members); - this._value_types_cache [objectId] = value.members; + const new_props = Object.assign ({ members: value.members }, value.__extra_vt_props); + + this._new_or_add_id_props ({ objectId: value.objectId, props: new_props }); delete value.members; + delete value.__extra_vt_props; } return var_list; }, - _get_details_for_value_type: function (objectId, fetchDetailsFn) { - if (objectId in this._value_types_cache) - return this._value_types_cache[objectId]; - - this._post_process_details (fetchDetailsFn()); - if (objectId in this._value_types_cache) - return this._value_types_cache[objectId]; - - // return error - throw new Error (`Could not get details for ${objectId}`); - }, - - _is_object_id_array: function (objectId) { - // Keep this in sync with `_get_array_details` - return (objectId.startsWith ('dotnet:array:') && objectId.split (':').length == 3); - }, - - _get_array_details: function (objectId, objectIdParts) { - // Keep this in sync with `_is_object_id_array` - switch (objectIdParts.length) { - case 3: - return this._post_process_details (this.mono_wasm_get_array_values(objectIdParts[2])); - - case 4: - var arrayObjectId = objectIdParts[2]; - var arrayIdx = objectIdParts[3]; - return this._get_details_for_value_type( - objectId, () => this.mono_wasm_get_array_value_expanded(arrayObjectId, arrayIdx)); - - default: - throw new Error (`object id format not supported : ${objectId}`); - } - }, - _get_cfo_res_details: function (objectId, args) { if (!(objectId in this._call_function_res_cache)) throw new Error(`Could not find any object with id ${objectId}`); - var real_obj = this._call_function_res_cache [objectId]; + const real_obj = this._call_function_res_cache [objectId]; - var descriptors = Object.getOwnPropertyDescriptors (real_obj); + const descriptors = Object.getOwnPropertyDescriptors (real_obj); if (args.accessorPropertiesOnly) { Object.keys (descriptors).forEach (k => { if (descriptors [k].get === undefined) @@ -370,10 +398,10 @@ var MonoSupportLib = { }); } - var res_details = []; + let res_details = []; Object.keys (descriptors).forEach (k => { - var new_obj; - var prop_desc = descriptors [k]; + let new_obj; + let prop_desc = descriptors [k]; if (typeof prop_desc.value == "object") { // convert `{value: { type='object', ... }}` // to `{ name: 'foo', value: { type='object', ... }} @@ -414,34 +442,77 @@ var MonoSupportLib = { return { __value_as_json_string__: JSON.stringify (res_details) }; }, - _get_ptr_args: function (objectId) { - var parts = this._split_object_id (objectId); - if (parts.length != 3) - throw new Error (`Bug: Unexpected objectId format for a pointer, expected 3 parts: ${objectId}`); - return JSON.parse (parts [2]); - }, + /** + * Generates a new id, and a corresponding entry for associated properties + * like `dotnet:pointer:{ a: 4 }` + * The third segment of that `{a:4}` is the idArgs parameter + * + * Only `scheme` or `objectId` can be set. + * if `scheme`, then a new id is generated, and it's properties set + * if `objectId`, then it's properties are updated + * + * @param {object} args + * @param {string} [args.scheme=undefined] scheme second part of `dotnet:pointer:..` + * @param {string} [args.objectId=undefined] objectId + * @param {object} [args.idArgs={}] The third segment of the objectId + * @param {object} [args.props={}] Properties for the generated id + * + * @returns {string} generated/updated id string + */ + _new_or_add_id_props: function ({ scheme = undefined, objectId = undefined, idArgs = {}, props = {} }) { + if (scheme === undefined && objectId === undefined) + throw new Error (`Either scheme or objectId must be given`); + + if (scheme !== undefined && objectId !== undefined) + throw new Error (`Both scheme, and objectId cannot be given`); + + if (objectId !== undefined && Object.entries (idArgs).length > 0) + throw new Error (`Both objectId, and idArgs cannot be given`); + + if (Object.entries (idArgs).length == 0) { + // We want to generate a new id, only if it doesn't have other + // attributes that it can use to uniquely identify. + // Eg, we don't do this for `dotnet:valuetype:{containerId:4, fieldOffset: 24}` + idArgs.num = this._next_id (); + } + + let idStr; + if (objectId !== undefined) { + idStr = objectId; + const old_props = this._id_table [idStr]; + if (old_props === undefined) + throw new Error (`ObjectId not found in the id table: ${idStr}`); + + this._id_table [idStr] = Object.assign (old_props, props); + } else { + idStr = `dotnet:${scheme}:${JSON.stringify (idArgs)}`; + this._id_table [idStr] = props; + } - _get_updated_ptr_id: function (objectId, new_args) { - var old_args = {}; - if (typeof (objectId) === 'string' && objectId.length) - old_args = this._get_ptr_args (objectId); + return idStr; + }, - return `dotnet:pointer:${JSON.stringify ( Object.assign (old_args, new_args) )}`; + /** + * @param {string} objectId + * @returns {object} + */ + _get_id_props: function (objectId) { + return this._id_table [objectId]; }, _get_deref_ptr_value: function (objectId) { - if (!this.mono_wasm_get_deref_ptr_value_info) - this.mono_wasm_get_deref_ptr_value_info = Module.cwrap("mono_wasm_get_deref_ptr_value", null, ['number', 'number']); + const ptr_args = this._get_id_props (objectId); + if (ptr_args === undefined) + throw new Error (`Unknown pointer id: ${objectId}`); - var ptr_args = this._get_ptr_args (objectId); if (ptr_args.ptr_addr == 0 || ptr_args.klass_addr == 0) throw new Error (`Both ptr_addr and klass_addr need to be non-zero, to dereference a pointer. objectId: ${objectId}`); - this.var_info = []; - var value_addr = new DataView (Module.HEAPU8.buffer).getUint32 (ptr_args.ptr_addr, /* littleEndian */ true); - this.mono_wasm_get_deref_ptr_value_info (value_addr, ptr_args.klass_addr); + const value_addr = new DataView (Module.HEAPU8.buffer).getUint32 (ptr_args.ptr_addr, /* littleEndian */ true); + let { res_ok, res } = this.mono_wasm_get_deref_ptr_value_info (value_addr, ptr_args.klass_addr); + if (!res_ok) + throw new Error (`Failed to dereference pointer ${objectId}`); - var res = MONO._fixup_name_value_objects(this.var_info); if (res.length > 0) { if (ptr_args.varName === undefined) throw new Error (`Bug: no varName found for the pointer. objectId: ${objectId}`); @@ -450,34 +521,25 @@ var MonoSupportLib = { } res = this._post_process_details (res); - this.var_info = []; return res; }, mono_wasm_get_details: function (objectId, args) { - var parts = objectId.split(":"); - if (parts[0] != "dotnet") - throw new Error ("Can't handle non-dotnet object ids. ObjectId: " + objectId); + let id = this._parse_object_id (objectId, true); - switch (parts[1]) { - case "object": - if (parts.length != 3) - throw new Error(`exception this time: Invalid object id format: ${objectId}`); + switch (id.scheme) { + case "object": { + if (isNaN (id.value)) + throw new Error (`Invalid objectId: ${objectId}. Expected a numeric id.`); - return this._post_process_details(this.mono_wasm_get_object_properties(parts[2], false)); + return this._get_object_properties(id.value, false); + } case "array": - return this._get_array_details(objectId, parts); + return this._get_array_values (id); case "valuetype": - if (parts.length != 3 && parts.length != 4) { - // dotnet:valuetype:vtid - // dotnet:valuetype:containerObjectId:vtId - throw new Error(`Invalid object id format: ${objectId}`); - } - - var containerObjectId = parts[2]; - return this._get_details_for_value_type(objectId, () => this.mono_wasm_get_object_properties(containerObjectId, true)); + return this._get_vt_properties(id); case "cfo_res": return this._get_cfo_res_details (objectId, args); @@ -492,7 +554,7 @@ var MonoSupportLib = { }, _cache_call_function_res: function (obj) { - var id = `dotnet:cfo_res:${this._next_call_function_res_id++}`; + const id = `dotnet:cfo_res:${this._next_call_function_res_id++}`; this._call_function_res_cache[id] = obj; return id; }, @@ -502,44 +564,69 @@ var MonoSupportLib = { delete this._cache_call_function_res[objectId]; }, - _invoke_getter_on_object: function (objectId, name) { - if (!this.mono_wasm_invoke_getter_on_object) - this.mono_wasm_invoke_getter_on_object = Module.cwrap ("mono_wasm_invoke_getter_on_object", 'void', [ 'number', 'string' ]); - - if (objectId < 0) { - // invalid id - return []; + /** + * @param {string} objectIdStr objectId + * @param {string} name property name + * @returns {object} return value + */ + _invoke_getter: function (objectIdStr, name) { + const id = this._parse_object_id (objectIdStr); + if (id === undefined) + throw new Error (`Invalid object id: ${objectIdStr}`); + + let getter_res; + if (id.scheme == 'object') { + if (isNaN (id.o) || id.o < 0) + throw new Error (`Invalid object id: ${objectIdStr}`); + + let { res_ok, res } = this.mono_wasm_invoke_getter_on_object_info (id.o, name); + if (!res_ok) + throw new Error (`Invoking getter on ${objectIdStr} failed`); + + getter_res = res; + } else if (id.scheme == 'valuetype') { + const id_props = this._get_id_props (objectIdStr); + if (id_props === undefined) + throw new Error (`Unknown valuetype id: ${objectIdStr}`); + + if (typeof id_props.value64 !== 'string' || isNaN (id_props.klass)) + throw new Error (`Bug: Cannot invoke getter on ${objectIdStr}, because of missing or invalid klass/value64 fields. idProps: ${JSON.stringify (id_props)}`); + + const dataPtr = Module._malloc (id_props.value64.length); + const dataHeap = new Uint8Array (Module.HEAPU8.buffer, dataPtr, id_props.value64.length); + dataHeap.set (new Uint8Array (this._base64_to_uint8 (id_props.value64))); + + let { res_ok, res } = this.mono_wasm_invoke_getter_on_value_info (dataHeap.byteOffset, id_props.klass, name); + Module._free (dataHeap.byteOffset); + + if (!res_ok) { + console.debug (`Invoking getter on valuetype ${objectIdStr}, with props: ${JSON.stringify (id_props)} failed`); + throw new Error (`Invoking getter on valuetype ${objectIdStr} failed`); + } + getter_res = res; + } else { + throw new Error (`Only object, and valuetypes supported for getters, id: ${objectIdStr}`); } - this.mono_wasm_invoke_getter_on_object (objectId, name); - var getter_res = MONO._post_process_details (MONO.var_info); - - MONO.var_info = []; - return getter_res [0]; + getter_res = MONO._post_process_details (getter_res); + return getter_res.length > 0 ? getter_res [0] : {}; }, _create_proxy_from_object_id: function (objectId) { - var details = this.mono_wasm_get_details(objectId); + const details = this.mono_wasm_get_details(objectId); - if (this._is_object_id_array (objectId)) + if (objectId.startsWith ('dotnet:array:')) return details.map (p => p.value); - var objIdParts = objectId.split (':'); - var objIdNum = -1; - if (objectId.startsWith ("dotnet:object:")) - objIdNum = objIdParts [2]; - - var proxy = {}; + let proxy = {}; Object.keys (details).forEach (p => { var prop = details [p]; if (prop.get !== undefined) { // TODO: `set` - // We don't add a `get` for non-object types right now, - // so, we shouldn't get here with objIdNum==-1 Object.defineProperty (proxy, prop.name, - { get () { return MONO._invoke_getter_on_object (objIdNum, prop.name); } } + { get () { return MONO._invoke_getter (objectId, prop.name); } } ); } else { proxy [prop.name] = prop.value; @@ -553,21 +640,27 @@ var MonoSupportLib = { if (request.arguments != undefined && !Array.isArray (request.arguments)) throw new Error (`"arguments" should be an array, but was ${request.arguments}`); - var objId = request.objectId; - var proxy; + const objId = request.objectId; + let proxy; - if (objId in this._call_function_res_cache) { - proxy = this._call_function_res_cache [objId]; - } else if (!objId.startsWith ('dotnet:cfo_res:')) { + if (objId.startsWith ('dotnet:cfo_res:')) { + if (objId in this._call_function_res_cache) + proxy = this._call_function_res_cache [objId]; + else + throw new Error (`Unknown object id ${objId}`); + } else { proxy = this._create_proxy_from_object_id (objId); } - var fn_args = request.arguments != undefined ? request.arguments.map(a => JSON.stringify(a.value)) : []; - var fn_eval_str = `var fn = ${request.functionDeclaration}; fn.call (proxy, ...[${fn_args}]);`; + const fn_args = request.arguments != undefined ? request.arguments.map(a => JSON.stringify(a.value)) : []; + const fn_eval_str = `var fn = ${request.functionDeclaration}; fn.call (proxy, ...[${fn_args}]);`; + + const fn_res = eval (fn_eval_str); + if (fn_res === undefined) + return { type: "undefined" }; - var fn_res = eval (fn_eval_str); - if (fn_res == undefined) // should we just return undefined? - throw Error ('Function returned undefined result'); + if (fn_res === null || (fn_res.subtype === 'null' && fn_res.value === undefined)) + return fn_res; // primitive type if (Object (fn_res) !== fn_res) @@ -580,7 +673,7 @@ var MonoSupportLib = { if (request.returnByValue) return {type: "object", value: fn_res}; - var fn_res_id = this._cache_call_function_res (fn_res); + const fn_res_id = this._cache_call_function_res (fn_res); if (Object.getPrototypeOf (fn_res) == Array.prototype) { return { type: "object", @@ -595,8 +688,8 @@ var MonoSupportLib = { }, _clear_per_step_state: function () { - this._next_value_type_id_var = 0; - this._value_types_cache = {}; + this._next_id_var = 0; + this._id_table = {}; }, mono_wasm_debugger_resume: function () { @@ -613,16 +706,75 @@ var MonoSupportLib = { return this.mono_wasm_setup_single_step (kind); }, + mono_wasm_set_pause_on_exceptions: function (state) { + if (!this.mono_wasm_pause_on_exceptions) + this.mono_wasm_pause_on_exceptions = Module.cwrap ("mono_wasm_pause_on_exceptions", 'number', [ 'number']); + var state_enum = 0; + switch (state) { + case 'uncaught': + state_enum = 1; //EXCEPTION_MODE_UNCAUGHT + break; + case 'all': + state_enum = 2; //EXCEPTION_MODE_ALL + break; + } + return this.mono_wasm_pause_on_exceptions (state_enum); + }, + + _register_c_fn: function (name, ...args) { + Object.defineProperty (this._c_fn_table, name + '_wrapper', { value: Module.cwrap (name, ...args) }); + }, + + /** + * Calls `Module.cwrap` for the function name, + * and creates a wrapper around it that returns + * `{ bool result, object var_info } + * + * @param {string} name C function name + * @param {string} ret_type + * @param {string[]} params + * + * @returns {void} + */ + _register_c_var_fn: function (name, ret_type, params) { + if (ret_type !== 'bool') + throw new Error (`Bug: Expected a C function signature that returns bool`); + + this._register_c_fn (name, ret_type, params); + Object.defineProperty (this, name + '_info', { + value: function (...args) { + MONO.var_info = []; + const res_ok = MONO._c_fn_table [name + '_wrapper'] (...args); + let res = MONO.var_info; + MONO.var_info = []; + if (res_ok) { + res = this._fixup_name_value_objects (res); + return { res_ok, res }; + } + + return { res_ok, res: undefined }; + } + }); + }, + mono_wasm_runtime_ready: function () { this.mono_wasm_runtime_is_ready = true; // DO NOT REMOVE - magic debugger init function - console.debug ("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28", JSON.stringify (this.loaded_files)); + console.debug ("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28"); this._clear_per_step_state (); // FIXME: where should this go? this._next_call_function_res_id = 0; this._call_function_res_cache = {}; + + this._c_fn_table = {}; + this._register_c_var_fn ('mono_wasm_get_object_properties', 'bool', [ 'number', 'bool' ]); + this._register_c_var_fn ('mono_wasm_get_array_values', 'bool', [ 'number', 'number', 'number', 'bool' ]); + this._register_c_var_fn ('mono_wasm_invoke_getter_on_object', 'bool', [ 'number', 'string' ]); + this._register_c_var_fn ('mono_wasm_invoke_getter_on_value', 'bool', [ 'number', 'number', 'string' ]); + this._register_c_var_fn ('mono_wasm_get_local_vars', 'bool', [ 'number', 'number', 'number']); + this._register_c_var_fn ('mono_wasm_get_deref_ptr_value', 'bool', [ 'number', 'number']); }, mono_wasm_set_breakpoint: function (assembly, method_token, il_offset) { @@ -640,7 +792,7 @@ var MonoSupportLib = { }, // Set environment variable NAME to VALUE - // Should be called before mono_load_runtime_and_bcl () in most cases + // Should be called before mono_load_runtime_and_bcl () in most cases mono_wasm_setenv: function (name, value) { if (!this.wasm_setenv) this.wasm_setenv = Module.cwrap ('mono_wasm_setenv', null, ['string', 'string']); @@ -652,7 +804,7 @@ var MonoSupportLib = { this.wasm_parse_runtime_options = Module.cwrap ('mono_wasm_parse_runtime_options', null, ['number', 'number']); var argv = Module._malloc (options.length * 4); var wasm_strdup = Module.cwrap ('mono_wasm_strdup', 'number', ['string']); - aindex = 0; + let aindex = 0; for (var i = 0; i < options.length; ++i) { Module.setValue (argv + (aindex * 4), wasm_strdup (options [i]), "i32"); aindex += 1; @@ -696,117 +848,440 @@ var MonoSupportLib = { Module.ccall ('mono_wasm_load_profiler_coverage', null, ['string'], [arg]); }, - mono_load_runtime_and_bcl: function (vfs_prefix, deploy_prefix, enable_debugging, file_list, loaded_cb, fetch_file_cb) { - var pending = file_list.length; - var loaded_files = []; - var loaded_files_with_debug_info = []; - var mono_wasm_add_assembly = Module.cwrap ('mono_wasm_add_assembly', 'number', ['string', 'number', 'number']); - - if (!fetch_file_cb) { - if (ENVIRONMENT_IS_NODE) { - var fs = require('fs'); - fetch_file_cb = function (asset) { - console.log("MONO_WASM: Loading... " + asset); - var binary = fs.readFileSync (asset); - var resolve_func2 = function(resolve, reject) { - resolve(new Uint8Array (binary)); - }; + _apply_configuration_from_args: function (args) { + for (var k in (args.environment_variables || {})) + MONO.mono_wasm_setenv (k, args.environment_variables[k]); - var resolve_func1 = function(resolve, reject) { - var response = { - ok: true, - url: asset, - arrayBuffer: function() { - return new Promise(resolve_func2); - } - }; - resolve(response); - }; + if (args.runtime_options) + MONO.mono_wasm_set_runtime_options (args.runtime_options); - return new Promise(resolve_func1); + if (args.aot_profiler_options) + MONO.mono_wasm_init_aot_profiler (args.aot_profiler_options); + + if (args.coverage_profiler_options) + MONO.mono_wasm_init_coverage_profiler (args.coverage_profiler_options); + }, + + _get_fetch_file_cb_from_args: function (args) { + if (typeof (args.fetch_file_cb) === "function") + return args.fetch_file_cb; + + if (ENVIRONMENT_IS_NODE) { + var fs = require('fs'); + return function (asset) { + console.log ("MONO_WASM: Loading... " + asset); + var binary = fs.readFileSync (asset); + var resolve_func2 = function (resolve, reject) { + resolve (new Uint8Array (binary)); }; - } else { - fetch_file_cb = function (asset) { - return fetch (asset, { credentials: 'same-origin' }); + + var resolve_func1 = function (resolve, reject) { + var response = { + ok: true, + url: asset, + arrayBuffer: function () { + return new Promise (resolve_func2); + } + }; + resolve (response); + }; + + return new Promise (resolve_func1); + }; + } else if (typeof (fetch) === "function") { + return function (asset) { + return fetch (asset, { credentials: 'same-origin' }); + }; + } else { + throw new Error ("No fetch_file_cb was provided and this environment does not expose 'fetch'."); + } + }, + + _handle_loaded_asset: function (ctx, asset, url, blob) { + var bytes = new Uint8Array (blob); + if (ctx.tracing) + console.log ("MONO_WASM: Loaded:", asset.name, "size", bytes.length, "from", url); + else + console.log ("MONO_WASM: Loaded:", asset.name); + + var virtualName = asset.virtual_path || asset.name; + var offset = null; + + switch (asset.behavior) { + case "assembly": + ctx.loaded_files.push ({ url: url, file: virtualName}); + case "heap": + case "icu": + offset = this.mono_wasm_load_bytes_into_heap (bytes); + ctx.loaded_assets[virtualName] = [offset, bytes.length]; + break; + + case "vfs": + // FIXME + var lastSlash = virtualName.lastIndexOf("/"); + var parentDirectory = (lastSlash > 0) + ? virtualName.substr(0, lastSlash) + : null; + var fileName = (lastSlash > 0) + ? virtualName.substr(lastSlash + 1) + : virtualName; + if (fileName.startsWith("/")) + fileName = fileName.substr(1); + if (parentDirectory) { + if (ctx.tracing) + console.log ("MONO_WASM: Creating directory '" + parentDirectory + "'"); + + var pathRet = ctx.createPath( + "/", parentDirectory, true, true // fixme: should canWrite be false? + ); + } else { + parentDirectory = "/"; + } + + if (ctx.tracing) + console.log ("MONO_WASM: Creating file '" + fileName + "' in directory '" + parentDirectory + "'"); + + if (!this.mono_wasm_load_data_archive (bytes, parentDirectory)) { + var fileRet = ctx.createDataFile ( + parentDirectory, fileName, + bytes, true /* canRead */, true /* canWrite */, true /* canOwn */ + ); } + break; + + default: + throw new Error ("Unrecognized asset behavior:", asset.behavior, "for asset", asset.name); + } + + if (asset.behavior === "assembly") { + var hasPpdb = ctx.mono_wasm_add_assembly (virtualName, offset, bytes.length); + + if (!hasPpdb) { + var index = ctx.loaded_files.findIndex(element => element.file == virtualName); + ctx.loaded_files.splice(index, 1); } } + else if (asset.behavior === "icu") { + if (this.mono_wasm_load_icu_data (offset)) + ctx.num_icu_assets_loaded_successfully += 1; + else + console.error ("Error loading ICU asset", asset.name); + } + }, + + // deprecated + mono_load_runtime_and_bcl: function ( + unused_vfs_prefix, deploy_prefix, debug_level, file_list, loaded_cb, fetch_file_cb + ) { + var args = { + fetch_file_cb: fetch_file_cb, + loaded_cb: loaded_cb, + debug_level: debug_level, + assembly_root: deploy_prefix, + assets: [] + }; + + for (var i = 0; i < file_list.length; i++) { + var file_name = file_list[i]; + var behavior; + if (file_name === "icudt.dat") + behavior = "icu"; + else // if (file_name.endsWith (".pdb") || file_name.endsWith (".dll")) + behavior = "assembly"; + + args.assets.push ({ + name: file_name, + behavior: behavior + }); + } + + return this.mono_load_runtime_and_bcl_args (args); + }, + + // Initializes the runtime and loads assemblies, debug information, and other files. + // @args is a dictionary-style Object with the following properties: + // assembly_root: (required) the subfolder containing managed assemblies and pdbs + // debug_level or enable_debugging: (required) + // assets: (required) a list of assets to load along with the runtime. each asset + // is a dictionary-style Object with the following properties: + // name: (required) the name of the asset, including extension. + // behavior: (required) determines how the asset will be handled once loaded: + // "heap": store asset into the native heap + // "assembly": load asset as a managed assembly (or debugging information) + // "icu": load asset as an ICU data archive + // "vfs": load asset into the virtual filesystem (for fopen, File.Open, etc) + // load_remote: (optional) if true, an attempt will be made to load the asset + // from each location in @args.remote_sources. + // virtual_path: (optional) if specified, overrides the path of the asset in + // the virtual filesystem and similar data structures once loaded. + // is_optional: (optional) if true, any failure to load this asset will be ignored. + // loaded_cb: (required) a function () invoked when loading has completed. + // fetch_file_cb: (optional) a function (string) invoked to fetch a given file. + // If no callback is provided a default implementation appropriate for the current + // environment will be selected (readFileSync in node, fetch elsewhere). + // If no default implementation is available this call will fail. + // remote_sources: (optional) additional search locations for assets. + // sources will be checked in sequential order until the asset is found. + // the string "./" indicates to load from the application directory (as with the + // files in assembly_list), and a fully-qualified URL like "https://example.com/" indicates + // that asset loads can be attempted from a remote server. Sources must end with a "/". + // environment_variables: (optional) dictionary-style Object containing environment variables + // runtime_options: (optional) array of runtime options as strings + // aot_profiler_options: (optional) dictionary-style Object. see the comments for + // mono_wasm_init_aot_profiler. If omitted, aot profiler will not be initialized. + // coverage_profiler_options: (optional) dictionary-style Object. see the comments for + // mono_wasm_init_coverage_profiler. If omitted, coverage profiler will not be initialized. + // globalization_mode: (optional) configures the runtime's globalization mode: + // "icu": load ICU globalization data from any runtime assets with behavior "icu". + // "invariant": operate in invariant globalization mode. + // "auto" (default): if "icu" behavior assets are present, use ICU, otherwise invariant. + // diagnostic_tracing: (optional) enables diagnostic log messages during startup + mono_load_runtime_and_bcl_args: function (args) { + try { + return this._load_assets_and_runtime (args); + } catch (exc) { + console.error ("error in mono_load_runtime_and_bcl_args:", exc); + throw exc; + } + }, - file_list.forEach (function(file_name) { - - var fetch_promise = fetch_file_cb (locateFile(deploy_prefix + "/" + file_name)); + // @bytes must be a typed array. space is allocated for it in the native heap + // and it is copied to that location. returns the address of the allocation. + mono_wasm_load_bytes_into_heap: function (bytes) { + var memoryOffset = Module._malloc (bytes.length); + var heapBytes = new Uint8Array (Module.HEAPU8.buffer, memoryOffset, bytes.length); + heapBytes.set (bytes); + return memoryOffset; + }, - fetch_promise.then (function (response) { + num_icu_assets_loaded_successfully: 0, + + // @offset must be the address of an ICU data archive in the native heap. + // returns true on success. + mono_wasm_load_icu_data: function (offset) { + var fn = Module.cwrap ('mono_wasm_load_icu_data', 'number', ['number']); + var ok = (fn (offset)) === 1; + if (ok) + this.num_icu_assets_loaded_successfully++; + return ok; + }, + + _finalize_startup: function (args, ctx) { + var loaded_files_with_debug_info = []; + + MONO.loaded_assets = ctx.loaded_assets; + ctx.loaded_files.forEach(value => loaded_files_with_debug_info.push(value.url)); + MONO.loaded_files = loaded_files_with_debug_info; + if (ctx.tracing) { + console.log ("MONO_WASM: loaded_assets: " + JSON.stringify(ctx.loaded_assets)); + console.log ("MONO_WASM: loaded_files: " + JSON.stringify(ctx.loaded_files)); + } + + var load_runtime = Module.cwrap ('mono_wasm_load_runtime', null, ['string', 'number']); + + console.log ("MONO_WASM: Initializing mono runtime"); + + this.mono_wasm_globalization_init (args.globalization_mode); + + if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { + try { + load_runtime ("unused", args.debug_level); + } catch (ex) { + print ("MONO_WASM: load_runtime () failed: " + ex); + print ("MONO_WASM: Stacktrace: \n"); + print (ex.stack); + + var wasm_exit = Module.cwrap ('mono_wasm_exit', null, ['number']); + wasm_exit (1); + } + } else { + load_runtime ("unused", args.debug_level); + } + + MONO.mono_wasm_runtime_ready (); + args.loaded_cb (); + }, + + _load_assets_and_runtime: function (args) { + if (args.enable_debugging) + args.debug_level = args.enable_debugging; + if (args.assembly_list) + throw new Error ("Invalid args (assembly_list was replaced by assets)"); + if (args.runtime_assets) + throw new Error ("Invalid args (runtime_assets was replaced by assets)"); + if (args.runtime_asset_sources) + throw new Error ("Invalid args (runtime_asset_sources was replaced by remote_sources)"); + if (!args.loaded_cb) + throw new Error ("loaded_cb not provided"); + + var ctx = { + tracing: args.diagnostic_tracing || false, + pending_count: args.assets.length, + mono_wasm_add_assembly: Module.cwrap ('mono_wasm_add_assembly', 'number', ['string', 'number', 'number']), + loaded_assets: Object.create (null), + // dlls and pdbs, used by blazor and the debugger + loaded_files: [], + createPath: Module['FS_createPath'], + createDataFile: Module['FS_createDataFile'] + }; + + if (ctx.tracing) + console.log ("mono_wasm_load_runtime_with_args", JSON.stringify(args)); + + this._apply_configuration_from_args (args); + + var fetch_file_cb = this._get_fetch_file_cb_from_args (args); + + var onPendingRequestComplete = function () { + --ctx.pending_count; + + if (ctx.pending_count === 0) { + try { + MONO._finalize_startup (args, ctx); + } catch (exc) { + console.error ("Unhandled exception in _finalize_startup", exc); + throw exc; + } + } + }; + + var processFetchResponseBuffer = function (asset, url, blob) { + try { + MONO._handle_loaded_asset (ctx, asset, url, blob); + } catch (exc) { + console.error ("Unhandled exception in processFetchResponseBuffer", exc); + throw exc; + } finally { + onPendingRequestComplete (); + } + }; + + args.assets.forEach (function (asset) { + var attemptNextSource; + var sourceIndex = 0; + var sourcesList = asset.load_remote ? args.remote_sources : [""]; + + var handleFetchResponse = function (response) { if (!response.ok) { - // If it's a 404 on a .pdb, we don't want to block the app from starting up. - // We'll just skip that file and continue (though the 404 is logged in the console). - if (response.status === 404 && file_name.match(/\.pdb$/) && MONO.mono_wasm_ignore_pdb_load_errors) { - --pending; - throw "MONO-WASM: Skipping failed load for .pdb file: '" + file_name + "'"; + try { + attemptNextSource (); + return; + } catch (exc) { + console.error ("MONO_WASM: Unhandled exception in handleFetchResponse attemptNextSource for asset", asset.name, exc); + throw exc; } - else { - throw "MONO_WASM: Failed to load file: '" + file_name + "'"; - } - } - else { - loaded_files.push ({ url: response.url, file: file_name}); - return response ['arrayBuffer'] (); } - }).then (function (blob) { - var asm = new Uint8Array (blob); - var memory = Module._malloc(asm.length); - var heapBytes = new Uint8Array(Module.HEAPU8.buffer, memory, asm.length); - heapBytes.set (asm); - var hasPpdb = mono_wasm_add_assembly (file_name, memory, asm.length); - - if (!hasPpdb) { - var index = loaded_files.findIndex(element => element.file == file_name); - loaded_files.splice(index, 1); + + try { + var bufferPromise = response ['arrayBuffer'] (); + bufferPromise.then (processFetchResponseBuffer.bind (this, asset, response.url)); + } catch (exc) { + console.error ("MONO_WASM: Unhandled exception in handleFetchResponse for asset", asset.name, exc); + attemptNextSource (); } - //console.log ("MONO_WASM: Loaded: " + file_name); - --pending; - if (pending == 0) { - loaded_files.forEach(value => loaded_files_with_debug_info.push(value.url)); - MONO.loaded_files = loaded_files_with_debug_info; - var load_runtime = Module.cwrap ('mono_wasm_load_runtime', null, ['string', 'number']); - - console.log ("MONO_WASM: Initializing mono runtime"); - if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { - try { - load_runtime (vfs_prefix, enable_debugging); - } catch (ex) { - print ("MONO_WASM: load_runtime () failed: " + ex); - print ("MONO_WASM: Stacktrace: \n"); - print (ex.stack); - - var wasm_exit = Module.cwrap ('mono_wasm_exit', null, ['number']); - wasm_exit (1); + }; + + attemptNextSource = function () { + if (sourceIndex >= sourcesList.length) { + var msg = "MONO_WASM: Failed to load " + asset.name; + try { + var isOk = asset.is_optional || + (asset.name.match (/\.pdb$/) && MONO.mono_wasm_ignore_pdb_load_errors); + + if (isOk) + console.log (msg); + else { + console.error (msg); + throw new Error (msg); } + } finally { + onPendingRequestComplete (); + } + } + + var sourcePrefix = sourcesList[sourceIndex]; + sourceIndex++; + + // HACK: Special-case because MSBuild doesn't allow "" as an attribute + if (sourcePrefix === "./") + sourcePrefix = ""; + + var attemptUrl; + if (sourcePrefix.trim() === "") { + if (asset.behavior === "assembly") + attemptUrl = locateFile (args.assembly_root + "/" + asset.name); + else + attemptUrl = asset.name; + } else { + attemptUrl = sourcePrefix + asset.name; + } + + try { + if (asset.name === attemptUrl) { + if (ctx.tracing) + console.log ("Attempting to fetch '" + attemptUrl + "'"); } else { - load_runtime (vfs_prefix, enable_debugging); + if (ctx.tracing) + console.log ("Attempting to fetch '" + attemptUrl + "' for", asset.name); } - MONO.mono_wasm_runtime_ready (); - loaded_cb (); + var fetch_promise = fetch_file_cb (attemptUrl); + fetch_promise.then (handleFetchResponse); + } catch (exc) { + console.error ("MONO_WASM: Error fetching " + attemptUrl, exc); + attemptNextSource (); } - }); + }; + + attemptNextSource (); }); }, + // Performs setup for globalization. + // @globalization_mode is one of "icu", "invariant", or "auto". + // "auto" will use "icu" if any ICU data archives have been loaded, + // otherwise "invariant". + mono_wasm_globalization_init: function (globalization_mode) { + var invariantMode = false; + + if (globalization_mode === "invariant") + invariantMode = true; + + if (!invariantMode) { + if (this.num_icu_assets_loaded_successfully > 0) { + console.log ("MONO_WASM: ICU data archive(s) loaded, disabling invariant mode"); + } else if (globalization_mode !== "icu") { + console.log ("MONO_WASM: ICU data archive(s) not loaded, using invariant globalization mode"); + invariantMode = true; + } else { + var msg = "invariant globalization mode is inactive and no ICU data archives were loaded"; + console.error ("MONO_WASM: ERROR: " + msg); + throw new Error (msg); + } + } + + if (invariantMode) + this.mono_wasm_setenv ("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1"); + }, + + // Used by the debugger to enumerate loaded dlls and pdbs mono_wasm_get_loaded_files: function() { - console.log(">>>mono_wasm_get_loaded_files"); - return this.loaded_files; + return MONO.loaded_files; + }, + + mono_wasm_get_loaded_asset_table: function() { + return MONO.loaded_assets; }, - + mono_wasm_clear_all_breakpoints: function() { if (!this.mono_clear_bps) this.mono_clear_bps = Module.cwrap ('mono_wasm_clear_all_breakpoints', null); this.mono_clear_bps (); }, - + mono_wasm_add_null_var: function(className) { - fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); + let fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); if (!fixed_class_name) { // Eg, when a @className is passed from js itself, like // mono_wasm_add_null_var ("string") @@ -830,12 +1305,13 @@ var MonoSupportLib = { value: { type: "string", value: var_value, + description: var_value } }); }, _mono_wasm_add_getter_var: function(className, invokable) { - fixed_class_name = MONO._mono_csharp_fixup_class_name (className); + const fixed_class_name = MONO._mono_csharp_fixup_class_name (className); if (invokable != 0) { var name; if (MONO.var_info.length > 0) @@ -862,7 +1338,7 @@ var MonoSupportLib = { }, _mono_wasm_add_array_var: function(className, objectId, length) { - fixed_class_name = MONO._mono_csharp_fixup_class_name(className); + const fixed_class_name = MONO._mono_csharp_fixup_class_name(className); if (objectId == 0) { MONO.mono_wasm_add_null_var (fixed_class_name); return; @@ -874,42 +1350,121 @@ var MonoSupportLib = { subtype: "array", className: fixed_class_name, description: `${fixed_class_name}(${length})`, - objectId: "dotnet:array:"+ objectId, + objectId: this._new_or_add_id_props ({ scheme: 'array', idArgs: { arrayId: objectId } }) } }); }, + // FIXME: improve + _base64_to_uint8: function (base64String) { + const byteCharacters = atob (base64String); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + + return new Uint8Array (byteNumbers); + }, + + _begin_value_type_var: function(className, args) { + if (args === undefined || (typeof args !== 'object')) { + console.debug (`_begin_value_type_var: Expected an args object`); + return; + } + + const fixed_class_name = MONO._mono_csharp_fixup_class_name(className); + const toString = args.toString; + const base64String = btoa (String.fromCharCode (...new Uint8Array (Module.HEAPU8.buffer, args.value_addr, args.value_size))); + const vt_obj = { + value: { + type : "object", + className : fixed_class_name, + description : (toString == 0 ? fixed_class_name: Module.UTF8ToString (toString)), + expanded : true, + isValueType : true, + __extra_vt_props: { klass: args.klass, value64: base64String }, + members : [] + } + }; + if (MONO._vt_stack.length == 0) + MONO._old_var_info = MONO.var_info; + + MONO.var_info = vt_obj.value.members; + MONO._vt_stack.push (vt_obj); + }, + + _end_value_type_var: function() { + let top_vt_obj_popped = MONO._vt_stack.pop (); + top_vt_obj_popped.value.members = MONO._filter_automatic_properties ( + MONO._fixup_name_value_objects (top_vt_obj_popped.value.members)); + + if (MONO._vt_stack.length == 0) { + MONO.var_info = MONO._old_var_info; + MONO.var_info.push(top_vt_obj_popped); + } else { + var top_obj = MONO._vt_stack [MONO._vt_stack.length - 1]; + top_obj.value.members.push (top_vt_obj_popped); + MONO.var_info = top_obj.value.members; + } + }, + + _add_valuetype_unexpanded_var: function(className, args) { + if (args === undefined || (typeof args !== 'object')) { + console.debug (`_add_valuetype_unexpanded_var: Expected an args object`); + return; + } + + const fixed_class_name = MONO._mono_csharp_fixup_class_name (className); + const toString = args.toString; + + MONO.var_info.push ({ + value: { + type: "object", + className: fixed_class_name, + description: (toString == 0 ? fixed_class_name : Module.UTF8ToString (toString)), + isValueType: true + } + }); + }, + + mono_wasm_add_typed_value: function (type, str_value, value) { - var type_str = type; + let type_str = type; if (typeof type != 'string') type_str = Module.UTF8ToString (type); - if (typeof str_value != 'string') str_value = Module.UTF8ToString (str_value); switch (type_str) { - case "bool": + case "bool": { + const v = value != 0; MONO.var_info.push ({ value: { type: "boolean", - value: value != 0 + value: v, + description: v.toString () } }); break; + } - case "char": + case "char": { + const v = `${value} '${String.fromCharCode (value)}'`; MONO.var_info.push ({ value: { type: "symbol", - value: `${value} '${String.fromCharCode (value)}'` + value: v, + description: v } }); break; + } case "number": MONO.var_info.push ({ value: { type: "number", - value: value + value: value, + description: '' + value } }); break; @@ -926,8 +1481,20 @@ var MonoSupportLib = { MONO._mono_wasm_add_array_var (str_value, value.objectId, value.length); break; + case "begin_vt": + MONO._begin_value_type_var (str_value, value); + break; + + case "end_vt": + MONO._end_value_type_var (); + break; + + case "unexpanded_vt": + MONO._add_valuetype_unexpanded_var (str_value, value); + break; + case "pointer": { - var fixed_value_str = MONO._mono_csharp_fixup_class_name (str_value); + const fixed_value_str = MONO._mono_csharp_fixup_class_name (str_value); if (value.klass_addr == 0 || value.ptr_addr == 0 || fixed_value_str.startsWith ('(void*')) { // null or void*, which we can't deref MONO.var_info.push({ @@ -943,7 +1510,7 @@ var MonoSupportLib = { type: "object", className: fixed_value_str, description: fixed_value_str, - objectId: this._get_updated_ptr_id ('', value) + objectId: this._new_or_add_id_props ({ scheme: 'pointer', props: value }) } }); } @@ -951,7 +1518,7 @@ var MonoSupportLib = { break; default: { - var msg = `'${str_value}' ${value}`; + const msg = `'${str_value}' ${value}`; MONO.var_info.push ({ value: { @@ -971,6 +1538,59 @@ var MonoSupportLib = { // and nested class names like Foo/Bar to Foo.Bar return className.replace(/\//g, '.').replace(/`\d+/g, ''); }, + + mono_wasm_load_data_archive: function (data, prefix) { + if (data.length < 8) + return false; + + var dataview = new DataView(data.buffer); + var magic = dataview.getUint32(0, true); + // get magic number + if (magic != 0x626c6174) { + return false; + } + var manifestSize = dataview.getUint32(4, true); + if (manifestSize == 0 || data.length < manifestSize + 8) + return false; + + var manifest; + try { + manifestContent = Module.UTF8ArrayToString(data, 8, manifestSize); + manifest = JSON.parse(manifestContent); + if (!(manifest instanceof Array)) + return false; + } catch (exc) { + return false; + } + + data = data.slice(manifestSize+8); + + // Create the folder structure + // /usr/share/zoneinfo + // /usr/share/zoneinfo/Africa + // /usr/share/zoneinfo/Asia + // .. + + var folders = new Set() + manifest.filter(m => { + var file = m[0]; + var last = file.lastIndexOf ("/"); + var directory = file.slice (0, last); + folders.add(directory); + }); + folders.forEach(folder => { + Module['FS_createPath'](prefix, folder, true, true); + }); + + for (row of manifest) { + var name = row[0]; + var length = row[1]; + var bytes = data.slice(0, length); + Module['FS_createDataFile'](prefix, name, bytes, true, true); + data = data.slice(length); + } + return true; + } }, mono_wasm_add_typed_value: function (type, str_value, value) { @@ -988,55 +1608,6 @@ var MonoSupportLib = { MONO._async_method_objectId = objectId; }, - mono_wasm_begin_value_type_var: function(className, toString) { - fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - var vt_obj = { - value: { - type: "object", - className: fixed_class_name, - description: (toString == 0 ? fixed_class_name : Module.UTF8ToString (toString)), - // objectId will be generated by MonoProxy - expanded: true, - isValueType: true, - members: [] - } - }; - if (MONO._vt_stack.length == 0) - MONO._old_var_info = MONO.var_info; - - MONO.var_info = vt_obj.value.members; - MONO._vt_stack.push (vt_obj); - }, - - mono_wasm_end_value_type_var: function() { - var top_vt_obj_popped = MONO._vt_stack.pop (); - top_vt_obj_popped.value.members = MONO._filter_automatic_properties ( - MONO._fixup_name_value_objects (top_vt_obj_popped.value.members)); - - if (MONO._vt_stack.length == 0) { - MONO.var_info = MONO._old_var_info; - MONO.var_info.push(top_vt_obj_popped); - } else { - var top_obj = MONO._vt_stack [MONO._vt_stack.length - 1]; - top_obj.value.members.push (top_vt_obj_popped); - MONO.var_info = top_obj.value.members; - } - }, - - mono_wasm_add_value_type_unexpanded_var: function (className, toString) { - fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); - MONO.var_info.push({ - value: { - type: "object", - className: fixed_class_name, - description: (toString == 0 ? fixed_class_name : Module.UTF8ToString (toString)), - // objectId added when enumerating object's properties - expanded: false, - isValueType: true - } - }); - }, - mono_wasm_add_enum_var: function(className, members, value) { // FIXME: flags // @@ -1044,13 +1615,13 @@ var MonoSupportLib = { // group0: Monday:0 // group1: Monday // group2: 0 - var re = new RegExp (`[,]?([^,:]+):(${value}(?=,)|${value}$)`, 'g') - var members_str = Module.UTF8ToString (members); + const re = new RegExp (`[,]?([^,:]+):(${value}(?=,)|${value}$)`, 'g') + const members_str = Module.UTF8ToString (members); - var match = re.exec(members_str); - var member_name = match == null ? ('' + value) : match [1]; + const match = re.exec(members_str); + const member_name = match == null ? ('' + value) : match [1]; - fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); + const fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); MONO.var_info.push({ value: { type: "object", @@ -1073,7 +1644,7 @@ var MonoSupportLib = { return; } - fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); + const fixed_class_name = MONO._mono_csharp_fixup_class_name(Module.UTF8ToString (className)); MONO.var_info.push({ value: { type: "object", @@ -1110,11 +1681,11 @@ var MonoSupportLib = { return `${ret_sig} ${method_name} (${args_sig})`; } - var tgt_sig; + let tgt_sig; if (targetName != 0) tgt_sig = args_to_sig (Module.UTF8ToString (targetName)); - var type_name = MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className)); + const type_name = MONO._mono_csharp_fixup_class_name (Module.UTF8ToString (className)); if (objectId == -1) { // Target property @@ -1184,6 +1755,7 @@ var MonoSupportLib = { mono_wasm_fire_bp: function () { console.log ("mono_wasm_fire_bp"); + // eslint-disable-next-line no-debugger debugger; }, |