From 9e8eb7eb133e6726fd3c9f0151c5636c6b348e6a Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sat, 25 Sep 2021 13:37:56 +0200 Subject: [PATCH 1/4] - refactor wasm javascript as typescript - use nodeJs from EMSDK - use rollup.js for packing code as ES6 IFFE for emcc consumption - incremental build Co-authored-by: Radek Doulik - types Co-authored-by: Daniel Genkin --- .../Directory.Build.props | 1 + src/mono/wasm/Makefile | 2 +- src/mono/wasm/build/WasmApp.Native.targets | 5 +- src/mono/wasm/runtime-test.js | 1 + src/mono/wasm/runtime/CMakeLists.txt | 4 +- src/mono/wasm/runtime/library_mono.js | 1556 +---------------- src/mono/wasm/runtime/package-lock.json | 328 ++++ src/mono/wasm/runtime/package.json | 26 + src/mono/wasm/runtime/rollup.config.js | 69 + src/mono/wasm/runtime/src/binding/types.ts | 14 + src/mono/wasm/runtime/src/mono/base64.ts | 121 ++ src/mono/wasm/runtime/src/mono/cwraps.ts | 57 + src/mono/wasm/runtime/src/mono/debug.ts | 291 +++ src/mono/wasm/runtime/src/mono/icu.ts | 60 + src/mono/wasm/runtime/src/mono/init.ts | 490 ++++++ src/mono/wasm/runtime/src/mono/profiler.ts | 48 + src/mono/wasm/runtime/src/mono/roots.ts | 304 ++++ src/mono/wasm/runtime/src/mono/scheduling.ts | 78 + .../wasm/runtime/src/mono/string-decoder.ts | 81 + src/mono/wasm/runtime/src/mono/types.ts | 157 ++ src/mono/wasm/runtime/src/runtime.ts | 14 + src/mono/wasm/runtime/src/startup.ts | 70 + .../wasm/runtime/src/types/emscripten.d.ts | 56 + src/mono/wasm/runtime/src/types/v8.d.ts | 5 + src/mono/wasm/runtime/tsconfig.json | 20 + src/mono/wasm/wasm.proj | 13 +- 26 files changed, 2319 insertions(+), 1552 deletions(-) create mode 100644 src/mono/wasm/runtime/package-lock.json create mode 100644 src/mono/wasm/runtime/package.json create mode 100644 src/mono/wasm/runtime/rollup.config.js create mode 100644 src/mono/wasm/runtime/src/binding/types.ts create mode 100644 src/mono/wasm/runtime/src/mono/base64.ts create mode 100644 src/mono/wasm/runtime/src/mono/cwraps.ts create mode 100644 src/mono/wasm/runtime/src/mono/debug.ts create mode 100644 src/mono/wasm/runtime/src/mono/icu.ts create mode 100644 src/mono/wasm/runtime/src/mono/init.ts create mode 100644 src/mono/wasm/runtime/src/mono/profiler.ts create mode 100644 src/mono/wasm/runtime/src/mono/roots.ts create mode 100644 src/mono/wasm/runtime/src/mono/scheduling.ts create mode 100644 src/mono/wasm/runtime/src/mono/string-decoder.ts create mode 100644 src/mono/wasm/runtime/src/mono/types.ts create mode 100644 src/mono/wasm/runtime/src/runtime.ts create mode 100644 src/mono/wasm/runtime/src/startup.ts create mode 100644 src/mono/wasm/runtime/src/types/emscripten.d.ts create mode 100644 src/mono/wasm/runtime/src/types/v8.d.ts create mode 100644 src/mono/wasm/runtime/tsconfig.json diff --git a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props index 15d09343b0503..f6cdff8b24e98 100644 --- a/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props +++ b/src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props @@ -217,6 +217,7 @@ + diff --git a/src/mono/wasm/Makefile b/src/mono/wasm/Makefile index 7d48a7e6f98aa..c4aebc3327c48 100644 --- a/src/mono/wasm/Makefile +++ b/src/mono/wasm/Makefile @@ -101,7 +101,7 @@ $(NATIVE_BIN_DIR)/include/wasm: $(BUILDS_OBJ_DIR): mkdir -p $$@ -$(NATIVE_BIN_DIR)/dotnet.js: runtime/library_mono.js runtime/binding_support.js runtime/dotnet_support.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(2) $(EMCC_DEFAULT_RSP) | $(NATIVE_BIN_DIR) +$(NATIVE_BIN_DIR)/dotnet.js: $(NATIVE_BIN_DIR)/src/runtime.iffe.js runtime/library_mono.js runtime/binding_support.js runtime/dotnet_support.js $(SYSTEM_NATIVE_LIBDIR)/pal_random.js $(2) $(EMCC_DEFAULT_RSP) | $(NATIVE_BIN_DIR) $(DOTNET) build $(CURDIR)/wasm.proj _MSBUILD_WASM_BUILD_ARGS /t:BuildWasmRuntimes $(EMCC_DEFAULT_RSP): $(CURDIR)/wasm.proj | $(NATIVE_BIN_DIR)/src Makefile diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 19737849bdaee..5a08fee4e2839 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -217,7 +217,8 @@ <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> - <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" /> + <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" Exclude="$(_WasmRuntimePackSrcDir)\*.iffe.js"/> + <_DotnetJSPreFile Include="$(_WasmRuntimePackSrcDir)\*.iffe.js"/> <_WasmNativeFileForLinking Include="@(NativeFileReference)" /> @@ -337,7 +338,9 @@ <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> + <_EmccLinkStepArgs Include="--pre-js "%(_DotnetJSPreFile.Identity)"" /> <_WasmLinkDependencies Include="@(_DotnetJSSrcFile)" /> + <_WasmLinkDependencies Include="@(_DotnetJSPreFile)" /> <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> <_EmccLinkStepArgs Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" /> diff --git a/src/mono/wasm/runtime-test.js b/src/mono/wasm/runtime-test.js index 76c02dfe63f85..8d8b89bd0bd5a 100644 --- a/src/mono/wasm/runtime-test.js +++ b/src/mono/wasm/runtime-test.js @@ -274,6 +274,7 @@ var Module = { try { bytes = read (asset, 'binary'); } catch (exc) { + console.log('v8 file read failed ' + asset + ' ' + exc) error = exc; } var response = { ok: (bytes && !error), url: asset, diff --git a/src/mono/wasm/runtime/CMakeLists.txt b/src/mono/wasm/runtime/CMakeLists.txt index ee2cf5f608308..8e20ff644a19d 100644 --- a/src/mono/wasm/runtime/CMakeLists.txt +++ b/src/mono/wasm/runtime/CMakeLists.txt @@ -21,8 +21,8 @@ target_link_libraries(dotnet set_target_properties(dotnet PROPERTIES CMAKE_EXECUTABLE_SUFFIX ".js" - LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${SOURCE_DIR}/library_mono.js;${SOURCE_DIR}/binding_support.js;${SOURCE_DIR}/dotnet_support.js;${SYSTEM_NATIVE_DIR}/pal_random.js" - LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp ${CONFIGURATION_EMCC_FLAGS} -DENABLE_NETCORE=1 --js-library ${SOURCE_DIR}/library_mono.js --js-library ${SOURCE_DIR}/binding_support.js --js-library ${SOURCE_DIR}/dotnet_support.js --js-library ${SYSTEM_NATIVE_DIR}/pal_random.js" + LINK_DEPENDS "${NATIVE_BIN_DIR}/src/emcc-default.rsp;${NATIVE_BIN_DIR}/src/runtime.iffe.js;${SOURCE_DIR}/library_mono.js;${SOURCE_DIR}/binding_support.js;${SOURCE_DIR}/dotnet_support.js;${SYSTEM_NATIVE_DIR}/pal_random.js" + LINK_FLAGS "@${NATIVE_BIN_DIR}/src/emcc-default.rsp ${CONFIGURATION_EMCC_FLAGS} -DENABLE_NETCORE=1 --pre-js ${NATIVE_BIN_DIR}/src/runtime.iffe.js --js-library ${SOURCE_DIR}/library_mono.js --js-library ${SOURCE_DIR}/binding_support.js --js-library ${SOURCE_DIR}/dotnet_support.js --js-library ${SYSTEM_NATIVE_DIR}/pal_random.js" RUNTIME_OUTPUT_DIRECTORY "${NATIVE_BIN_DIR}") add_custom_command(TARGET dotnet POST_BUILD COMMAND ${EMSDK_PATH}/upstream/bin/wasm-opt --strip-dwarf ${NATIVE_BIN_DIR}/dotnet.wasm -o ${NATIVE_BIN_DIR}/dotnet.wasm) diff --git a/src/mono/wasm/runtime/library_mono.js b/src/mono/wasm/runtime/library_mono.js index 2f9fe8a5e19c5..462b042fbe711 100644 --- a/src/mono/wasm/runtime/library_mono.js +++ b/src/mono/wasm/runtime/library_mono.js @@ -3,1552 +3,16 @@ "use strict"; -/** - * @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 - */ - -/** - * @typedef WasmRoot - a single address in the managed heap, visible to the GC - * @type {object} - * @property {ManagedPointer} value - pointer into the managed heap, stored in the root - * @property {function} get_address - retrieves address of the root in wasm memory - * @property {function} get - retrieves pointer value - * @property {function} set - updates the pointer - * @property {function} release - releases the root storage for future use - */ - -/** - * @typedef WasmRootBuffer - a collection of addresses in the managed heap, visible to the GC - * @type {object} - * @property {number} length - number of elements the root buffer can hold - * @property {function} get_address - retrieves address of an element in wasm memory, by index - * @property {function} get - retrieves an element by index - * @property {function} set - sets an element's value by index - * @property {function} release - releases the root storage for future use - */ - -/** - * @typedef ManagedPointer - * @type {number} - address in the managed heap - */ - -/** - * @typedef NativePointer - * @type {number} - address in wasm memory - */ - -/** - * @typedef Event - * @type {object} - * @property {string} eventName - name of the event being raised - * @property {object} eventArgs - arguments for the event itself - */ - -var MonoSupportLib = { - $MONO__postset: 'MONO.export_functions (Module);', - $MONO: { - active_frames: [], - pump_count: 0, - timeout_queue: [], - spread_timers_maximum: 0, - isChromium: false, - _vt_stack: [], - mono_wasm_runtime_is_ready : false, - mono_wasm_ignore_pdb_load_errors: true, - num_icu_assets_loaded_successfully: 0, - _async_method_objectId: 0, - _next_id_var: 0, - _next_call_function_res_id: 0, - _scratch_root_buffer: null, - _scratch_root_free_indices: null, - _scratch_root_free_indices_count: 0, - _scratch_root_free_instances: [], - _vt_stack: [], - - /** @type {object.} */ - _id_table: {}, - - pump_message: function () { - if (!MONO.mono_background_exec) - MONO.mono_background_exec = Module.cwrap ("mono_background_exec", null); - while (MONO.timeout_queue.length > 0) { - --MONO.pump_count; - MONO.timeout_queue.shift()(); - } - while (MONO.pump_count > 0) { - --MONO.pump_count; - MONO.mono_background_exec (); - } - }, - - export_functions: function (module) { - module ["pump_message"] = MONO.pump_message.bind(MONO); - module ["prevent_timer_throttling"] = MONO.prevent_timer_throttling.bind(MONO); - module ["mono_wasm_set_timeout_exec"] = MONO.mono_wasm_set_timeout_exec.bind(MONO); - module ["mono_load_runtime_and_bcl"] = MONO.mono_load_runtime_and_bcl.bind(MONO); - module ["mono_load_runtime_and_bcl_args"] = MONO.mono_load_runtime_and_bcl_args.bind(MONO); - module ["mono_wasm_load_bytes_into_heap"] = MONO.mono_wasm_load_bytes_into_heap.bind(MONO); - module ["mono_wasm_load_icu_data"] = MONO.mono_wasm_load_icu_data.bind(MONO); - module ["mono_wasm_get_icudt_name"] = MONO.mono_wasm_get_icudt_name.bind(MONO); - module ["mono_wasm_globalization_init"] = MONO.mono_wasm_globalization_init.bind(MONO); - module ["mono_wasm_get_loaded_files"] = MONO.mono_wasm_get_loaded_files.bind(MONO); - module ["mono_wasm_new_root_buffer"] = MONO.mono_wasm_new_root_buffer.bind(MONO); - module ["mono_wasm_new_root_buffer_from_pointer"] = MONO.mono_wasm_new_root_buffer_from_pointer.bind(MONO); - module ["mono_wasm_new_root"] = MONO.mono_wasm_new_root.bind(MONO); - module ["mono_wasm_new_roots"] = MONO.mono_wasm_new_roots.bind(MONO); - module ["mono_wasm_release_roots"] = MONO.mono_wasm_release_roots.bind(MONO); - module ["mono_wasm_load_config"] = MONO.mono_wasm_load_config.bind(MONO); - - if (globalThis.navigator) { - const nav = globalThis.navigator; - if (nav.userAgentData && nav.userAgentData.brands) { - MONO.isChromium = nav.userAgentData.brands.some((i) => i.brand == 'Chromium'); - } - else if (nav.userAgent) { - MONO.isChromium = nav.userAgent.includes("Chrome"); - } - } - }, - - _base64Converter: { - // Code from JSIL: - // https://github.com/sq/JSIL/blob/1d57d5427c87ab92ffa3ca4b82429cd7509796ba/JSIL.Libraries/Includes/Bootstrap/Core/Classes/System.Convert.js#L149 - // Thanks to Katelyn Gadd @kg - - _base64Table: [ - 'A', 'B', 'C', 'D', - 'E', 'F', 'G', 'H', - 'I', 'J', 'K', 'L', - 'M', 'N', 'O', 'P', - 'Q', 'R', 'S', 'T', - 'U', 'V', 'W', 'X', - 'Y', 'Z', - 'a', 'b', 'c', 'd', - 'e', 'f', 'g', 'h', - 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', - 'q', 'r', 's', 't', - 'u', 'v', 'w', 'x', - 'y', 'z', - '0', '1', '2', '3', - '4', '5', '6', '7', - '8', '9', - '+', '/' - ], - - _makeByteReader: function (bytes, index, count) { - var position = (typeof (index) === "number") ? index : 0; - var endpoint; - - if (typeof (count) === "number") - endpoint = (position + count); - else - endpoint = (bytes.length - position); - - var result = { - read: function () { - if (position >= endpoint) - return false; - - var nextByte = bytes[position]; - position += 1; - return nextByte; - } - }; - - Object.defineProperty(result, "eof", { - get: function () { - return (position >= endpoint); - }, - configurable: true, - enumerable: true - }); - - return result; - }, - - toBase64StringImpl: function (inArray, offset, length) { - var reader = this._makeByteReader(inArray, offset, length); - var result = ""; - var ch1 = 0, ch2 = 0, ch3 = 0, bits = 0, equalsCount = 0, sum = 0; - var mask1 = (1 << 24) - 1, mask2 = (1 << 18) - 1, mask3 = (1 << 12) - 1, mask4 = (1 << 6) - 1; - var shift1 = 18, shift2 = 12, shift3 = 6, shift4 = 0; - - while (true) { - ch1 = reader.read(); - ch2 = reader.read(); - ch3 = reader.read(); - - if (ch1 === false) - break; - if (ch2 === false) { - ch2 = 0; - equalsCount += 1; - } - if (ch3 === false) { - ch3 = 0; - equalsCount += 1; - } - - // Seems backwards, but is right! - sum = (ch1 << 16) | (ch2 << 8) | (ch3 << 0); - - bits = (sum & mask1) >> shift1; - result += this._base64Table[bits]; - bits = (sum & mask2) >> shift2; - result += this._base64Table[bits]; - - if (equalsCount < 2) { - bits = (sum & mask3) >> shift3; - result += this._base64Table[bits]; - } - - if (equalsCount === 2) { - result += "=="; - } else if (equalsCount === 1) { - result += "="; - } else { - bits = (sum & mask4) >> shift4; - result += this._base64Table[bits]; - } - } - - return result; - }, - }, - - _mono_wasm_root_buffer_prototype: { - _throw_index_out_of_range: function () { - throw new Error ("index out of range"); - }, - _check_in_range: function (index) { - if ((index >= this.__count) || (index < 0)) - this._throw_index_out_of_range(); - }, - /** @returns {NativePointer} */ - get_address: function (index) { - this._check_in_range (index); - return this.__offset + (index * 4); - }, - /** @returns {number} */ - get_address_32: function (index) { - this._check_in_range (index); - return this.__offset32 + index; - }, - /** @returns {ManagedPointer} */ - get: function (index) { - this._check_in_range (index); - return Module.HEAP32[this.get_address_32 (index)]; - }, - set: function (index, value) { - Module.HEAP32[this.get_address_32 (index)] = value; - return value; - }, - _unsafe_get: function (index) { - return Module.HEAP32[this.__offset32 + index]; - }, - _unsafe_set: function (index, value) { - Module.HEAP32[this.__offset32 + index] = value; - }, - clear: function () { - if (this.__offset) - MONO._zero_region (this.__offset, this.__count * 4); - }, - release: function () { - if (this.__offset && this.__ownsAllocation) { - MONO.mono_wasm_deregister_root (this.__offset); - MONO._zero_region (this.__offset, this.__count * 4); - Module._free (this.__offset); - } - - this.__handle = this.__offset = this.__count = this.__offset32 = 0; - }, - toString: function () { - return "[root buffer @" + this.get_address (0) + ", size " + this.__count + "]"; - } - }, - - _mono_wasm_root_prototype: { - /** @returns {NativePointer} */ - get_address: function () { - return this.__buffer.get_address (this.__index); - }, - /** @returns {number} */ - get_address_32: function () { - return this.__buffer.get_address_32 (this.__index); - }, - /** @returns {ManagedPointer} */ - get: function () { - var result = this.__buffer._unsafe_get (this.__index); - return result; - }, - set: function (value) { - this.__buffer._unsafe_set (this.__index, value); - return value; - }, - /** @returns {ManagedPointer} */ - valueOf: function () { - return this.get (); - }, - clear: function () { - this.set (0); - }, - release: function () { - const maxPooledInstances = 128; - if (MONO._scratch_root_free_instances.length > maxPooledInstances) { - MONO._mono_wasm_release_scratch_index (this.__index); - this.__buffer = 0; - this.__index = 0; - } else { - this.set (0); - MONO._scratch_root_free_instances.push (this); - } - }, - toString: function () { - return "[root @" + this.get_address () + "]"; - } - }, - - _mono_wasm_release_scratch_index: function (index) { - if (index === undefined) - return; - - MONO._scratch_root_buffer.set (index, 0); - MONO._scratch_root_free_indices[MONO._scratch_root_free_indices_count] = index; - MONO._scratch_root_free_indices_count++; - }, - - _mono_wasm_claim_scratch_index: function () { - if (!MONO._scratch_root_buffer) { - const maxScratchRoots = 8192; - MONO._scratch_root_buffer = this.mono_wasm_new_root_buffer (maxScratchRoots, "js roots"); - - MONO._scratch_root_free_indices = new Int32Array (maxScratchRoots); - MONO._scratch_root_free_indices_count = maxScratchRoots; - for (var i = 0; i < maxScratchRoots; i++) - MONO._scratch_root_free_indices[i] = maxScratchRoots - i - 1; - - Object.defineProperty (this._mono_wasm_root_prototype, "value", { - get: this._mono_wasm_root_prototype.get, - set: this._mono_wasm_root_prototype.set, - configurable: false - }); - } - - if (MONO._scratch_root_free_indices_count < 1) - throw new Error ("Out of scratch root space"); - - var result = MONO._scratch_root_free_indices[MONO._scratch_root_free_indices_count - 1]; - MONO._scratch_root_free_indices_count--; - return result; - }, - - _zero_region: function (byteOffset, sizeBytes) { - if (((byteOffset % 4) === 0) && ((sizeBytes % 4) === 0)) - Module.HEAP32.fill(0, byteOffset / 4, sizeBytes / 4); - else - Module.HEAP8.fill(0, byteOffset, sizeBytes); - }, - - /** - * Allocates a block of memory that can safely contain pointers into the managed heap. - * The result object has get(index) and set(index, value) methods that can be used to retrieve and store managed pointers. - * Once you are done using the root buffer, you must call its release() method. - * For small numbers of roots, it is preferable to use the mono_wasm_new_root and mono_wasm_new_roots APIs instead. - * @param {number} capacity - the maximum number of elements the buffer can hold. - * @param {string} [msg] - a description of the root buffer (for debugging) - * @returns {WasmRootBuffer} - */ - mono_wasm_new_root_buffer: function (capacity, msg) { - if (!this.mono_wasm_register_root || !this.mono_wasm_deregister_root) { - this.mono_wasm_register_root = Module.cwrap ("mono_wasm_register_root", "number", ["number", "number", "string"]); - this.mono_wasm_deregister_root = Module.cwrap ("mono_wasm_deregister_root", null, ["number"]); - } - - if (capacity <= 0) - throw new Error ("capacity >= 1"); - - capacity = capacity | 0; - - var capacityBytes = capacity * 4; - var offset = Module._malloc (capacityBytes); - if ((offset % 4) !== 0) - throw new Error ("Malloc returned an unaligned offset"); - - this._zero_region (offset, capacityBytes); - - var result = Object.create (this._mono_wasm_root_buffer_prototype); - result.__offset = offset; - result.__offset32 = (offset / 4) | 0; - result.__count = capacity; - result.length = capacity; - result.__handle = this.mono_wasm_register_root (offset, capacityBytes, msg || 0); - result.__ownsAllocation = true; - - return result; - }, - - /** - * Creates a root buffer object representing an existing allocation in the native heap and registers - * the allocation with the GC. The caller is responsible for managing the lifetime of the allocation. - * @param {NativePointer} offset - the offset of the root buffer in the native heap. - * @param {number} capacity - the maximum number of elements the buffer can hold. - * @param {string} [msg] - a description of the root buffer (for debugging) - * @returns {WasmRootBuffer} - */ - mono_wasm_new_root_buffer_from_pointer: function (offset, capacity, msg) { - if (!this.mono_wasm_register_root || !this.mono_wasm_deregister_root) { - this.mono_wasm_register_root = Module.cwrap ("mono_wasm_register_root", "number", ["number", "number", "string"]); - this.mono_wasm_deregister_root = Module.cwrap ("mono_wasm_deregister_root", null, ["number"]); - } - - if (capacity <= 0) - throw new Error ("capacity >= 1"); - - capacity = capacity | 0; - - var capacityBytes = capacity * 4; - if ((offset % 4) !== 0) - throw new Error ("Unaligned offset"); - - this._zero_region (offset, capacityBytes); - - var result = Object.create (this._mono_wasm_root_buffer_prototype); - result.__offset = offset; - result.__offset32 = (offset / 4) | 0; - result.__count = capacity; - result.length = capacity; - result.__handle = this.mono_wasm_register_root (offset, capacityBytes, msg || 0); - result.__ownsAllocation = false; - - return result; - }, - - /** - * Allocates temporary storage for a pointer into the managed heap. - * Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected. - * If you already have a managed pointer you can pass it as an argument to initialize the temporary storage. - * The result object has get() and set(value) methods, along with a .value property. - * When you are done using the root you must call its .release() method. - * @param {ManagedPointer} [value] - an address in the managed heap to initialize the root with (or 0) - * @returns {WasmRoot} - */ - mono_wasm_new_root: function (value) { - var result; - - if (MONO._scratch_root_free_instances.length > 0) { - result = MONO._scratch_root_free_instances.pop (); - } else { - var index = this._mono_wasm_claim_scratch_index (); - var buffer = MONO._scratch_root_buffer; - - result = Object.create (this._mono_wasm_root_prototype); - result.__buffer = buffer; - result.__index = index; - } - - if (value !== undefined) { - if (typeof (value) !== "number") - throw new Error ("value must be an address in the managed heap"); - - result.set (value); - } else { - result.set (0); - } - - return result; - }, - - /** - * Allocates 1 or more temporary roots, accepting either a number of roots or an array of pointers. - * mono_wasm_new_roots(n): returns an array of N zero-initialized roots. - * mono_wasm_new_roots([a, b, ...]) returns an array of new roots initialized with each element. - * Each root must be released with its release method, or using the mono_wasm_release_roots API. - * @param {(number | ManagedPointer[])} count_or_values - either a number of roots or an array of pointers - * @returns {WasmRoot[]} - */ - mono_wasm_new_roots: function (count_or_values) { - var result; - - if (Array.isArray (count_or_values)) { - result = new Array (count_or_values.length); - for (var i = 0; i < result.length; i++) - result[i] = this.mono_wasm_new_root (count_or_values[i]); - } else if ((count_or_values | 0) > 0) { - result = new Array (count_or_values); - for (var i = 0; i < result.length; i++) - result[i] = this.mono_wasm_new_root (); - } else { - throw new Error ("count_or_values must be either an array or a number greater than 0"); - } - - return result; - }, - - /** - * Releases 1 or more root or root buffer objects. - * Multiple objects may be passed on the argument list. - * 'undefined' may be passed as an argument so it is safe to call this method from finally blocks - * even if you are not sure all of your roots have been created yet. - * @param {... WasmRoot} roots - */ - mono_wasm_release_roots: function () { - for (var i = 0; i < arguments.length; i++) { - if (!arguments[i]) - continue; - - arguments[i].release (); - } - }, - - string_decoder: { - copy: function (mono_string) { - if (mono_string === 0) - return null; - - if (!this.mono_wasm_string_root) - this.mono_wasm_string_root = MONO.mono_wasm_new_root (); - this.mono_wasm_string_root.value = mono_string; - - if (!this.mono_wasm_string_get_data) - this.mono_wasm_string_get_data = Module.cwrap ("mono_wasm_string_get_data", null, ['number', 'number', 'number', 'number']); - - if (!this.mono_wasm_string_decoder_buffer) - this.mono_wasm_string_decoder_buffer = Module._malloc(12); - - let ppChars = this.mono_wasm_string_decoder_buffer + 0, - pLengthBytes = this.mono_wasm_string_decoder_buffer + 4, - pIsInterned = this.mono_wasm_string_decoder_buffer + 8; - - this.mono_wasm_string_get_data (mono_string, ppChars, pLengthBytes, pIsInterned); - - // TODO: Is this necessary? - if (!this.mono_wasm_empty_string) - this.mono_wasm_empty_string = ""; - - let result = this.mono_wasm_empty_string; - let lengthBytes = Module.HEAP32[pLengthBytes / 4], - pChars = Module.HEAP32[ppChars / 4], - isInterned = Module.HEAP32[pIsInterned / 4]; - - if (pLengthBytes && pChars) { - if ( - isInterned && - MONO.interned_string_table && - MONO.interned_string_table.has(mono_string) - ) { - result = MONO.interned_string_table.get(mono_string); - // console.log("intern table cache hit", mono_string, result.length); - } else { - result = this.decode(pChars, pChars + lengthBytes, false); - if (isInterned) { - if (!MONO.interned_string_table) - MONO.interned_string_table = new Map(); - // console.log("interned", mono_string, result.length); - MONO.interned_string_table.set(mono_string, result); - } - } - } - - this.mono_wasm_string_root.value = 0; - return result; - }, - decode: function (start, end, save) { - if (MONO.mono_text_decoder === undefined) { - MONO.mono_text_decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : null; - } - - var str = ""; - if (MONO.mono_text_decoder) { - // When threading is enabled, TextDecoder does not accept a view of a - // SharedArrayBuffer, we must make a copy of the array first. - // See https://github.com/whatwg/encoding/issues/172 - var subArray = typeof SharedArrayBuffer !== 'undefined' && Module.HEAPU8.buffer instanceof SharedArrayBuffer - ? Module.HEAPU8.slice(start, end) - : Module.HEAPU8.subarray(start, end); - - str = MONO.mono_text_decoder.decode(subArray); - } else { - for (var i = 0; i < end - start; i+=2) { - var char = Module.getValue (start + i, 'i16'); - str += String.fromCharCode (char); - } - } - if (save) - this.result = str; - - return str; - }, - }, - - mono_wasm_add_dbg_command_received: function(res_ok, id, buffer, buffer_len) { - const assembly_data = new Uint8Array(Module.HEAPU8.buffer, buffer, buffer_len); - const base64String = MONO._base64Converter.toBase64StringImpl(assembly_data); - const buffer_obj = { - res_ok, - res: { - id, - value: base64String - } - } - MONO.commands_received = buffer_obj; - }, - - mono_wasm_send_dbg_command_with_parms: function (id, command_set, command, command_parameters, length, valtype, newvalue) - { - var dataHeap = this.mono_wasm_load_bytes_into_heap (this._base64_to_uint8 (command_parameters)); - this._c_fn_table.mono_wasm_send_dbg_command_with_parms_wrapper (id, command_set, command, dataHeap, length, valtype, newvalue.toString()); - let { res_ok, res } = MONO.commands_received; - if (!res_ok) - throw new Error (`Failed on mono_wasm_invoke_method_debugger_agent_with_parms`); - return res; - }, - - mono_wasm_send_dbg_command: function (id, command_set, command, command_parameters) - { - var dataHeap = this.mono_wasm_load_bytes_into_heap (this._base64_to_uint8 (command_parameters)); - this._c_fn_table.mono_wasm_send_dbg_command_wrapper (id, command_set, command, dataHeap, command_parameters.length); - let { res_ok, res } = MONO.commands_received; - if (!res_ok) - throw new Error (`Failed on mono_wasm_send_dbg_command`); - return res; - - }, - - mono_wasm_get_dbg_command_info: function () - { - let { res_ok, res } = MONO.commands_received; - if (!res_ok) - throw new Error (`Failed on mono_wasm_get_dbg_command_info`); - return res; - }, - - _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}`); - - const real_obj = this._call_function_res_cache [objectId]; - - const descriptors = Object.getOwnPropertyDescriptors (real_obj); - if (args.accessorPropertiesOnly) { - Object.keys (descriptors).forEach (k => { - if (descriptors [k].get === undefined) - Reflect.deleteProperty (descriptors, k); - }); - } - - let res_details = []; - Object.keys (descriptors).forEach (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', ... }} - new_obj = Object.assign ({ name: k }, prop_desc); - } else if (prop_desc.value !== undefined) { - // This is needed for values that were not added by us, - // thus are like { value: 5 } - // instead of { value: { type = 'number', value: 5 }} - // - // This can happen, for eg., when `length` gets added for arrays - // or `__proto__`. - new_obj = { - name: k, - // merge/add `type` and `description` to `d.value` - value: Object.assign ({ type: (typeof prop_desc.value), description: '' + prop_desc.value }, - prop_desc) - }; - } else if (prop_desc.get !== undefined) { - // The real_obj has the actual getter. We are just returning a placeholder - // If the caller tries to run function on the cfo_res object, - // that accesses this property, then it would be run on `real_obj`, - // which *has* the original getter - new_obj = { - name: k, - get: { - className: "Function", - description: `get ${k} () {}`, - type: "function" - } - }; - } else { - new_obj = { name: k, value: { type: "symbol", value: "", description: ""} }; - } - - res_details.push (new_obj); - }); - - return { __value_as_json_string__: JSON.stringify (res_details) }; - }, - - mono_wasm_get_details: function (objectId, args={}) { - return this._get_cfo_res_details (`dotnet:cfo_res:${objectId}`, args); - }, - - _cache_call_function_res: function (obj) { - const id = `dotnet:cfo_res:${MONO._next_call_function_res_id++}`; - this._call_function_res_cache[id] = obj; - return id; - }, - - mono_wasm_release_object: function (objectId) { - if (objectId in this._cache_call_function_res) - delete this._cache_call_function_res[objectId]; - }, - - _create_proxy_from_object_id: function (objectId, details) { - if (objectId.startsWith ('dotnet:array:')) - { - let ret = details.map (p => p.value); - return ret; - } - - let proxy = {}; - Object.keys (details).forEach (p => { - var prop = details [p]; - if (prop.get !== undefined) { - Object.defineProperty (proxy, - prop.name, - { get () { return MONO.mono_wasm_send_dbg_command(-1, prop.get.commandSet, prop.get.command, prop.get.buffer, prop.get.length); }, - set: function (newValue) { MONO.mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return MONO.commands_received.res_ok;}} - ); - } else if (prop.set !== undefined ){ - Object.defineProperty (proxy, - prop.name, - { get () { return prop.value; }, - set: function (newValue) { MONO.mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return MONO.commands_received.res_ok;}} - ); - } else { - proxy [prop.name] = prop.value; - } - }); - return proxy; - }, - - mono_wasm_call_function_on: function (request) { - if (request.arguments != undefined && !Array.isArray (request.arguments)) - throw new Error (`"arguments" should be an array, but was ${request.arguments}`); - - const objId = request.objectId; - const details = request.details; - let proxy; - - 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, details); - } - - 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" }; - - if (Object (fn_res) !== fn_res) - { - if (typeof(fn_res) == "object" && fn_res == null) - return { type: typeof(fn_res), subtype: `${fn_res}`, value: null }; - return { type: typeof(fn_res), description: `${fn_res}`, value: `${fn_res}`}; - } - - if (request.returnByValue && fn_res.subtype == undefined) - return {type: "object", value: fn_res}; - if (Object.getPrototypeOf (fn_res) == Array.prototype) { - - const fn_res_id = this._cache_call_function_res (fn_res); - - return { - type: "object", - subtype: "array", - className: "Array", - description: `Array(${fn_res.length})`, - objectId: fn_res_id - }; - } - if (fn_res.value !== undefined || fn_res.subtype !== undefined) { - return fn_res; - } - - if (fn_res == proxy) - return { type: "object", className: "Object", description: "Object", objectId: objId }; - const fn_res_id = this._cache_call_function_res (fn_res); - return { type: "object", className: "Object", description: "Object", objectId: fn_res_id }; - }, - - _clear_per_step_state: function () { - MONO._next_id_var = 0; - MONO._id_table = {}; - }, - - mono_wasm_debugger_resume: function () { - this._clear_per_step_state (); - }, - - mono_wasm_detach_debugger: function () { - if (!this.mono_wasm_set_is_debugger_attached) - this.mono_wasm_set_is_debugger_attached = Module.cwrap ('mono_wasm_set_is_debugger_attached', 'void', ['bool']); - this.mono_wasm_set_is_debugger_attached(false); - }, - - _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 () { - MONO.mono_wasm_runtime_is_ready = true; - this._clear_per_step_state (); - - // FIXME: where should this go? - MONO._next_call_function_res_id = 0; - this._call_function_res_cache = {}; - - this._c_fn_table = {}; - this._register_c_fn ('mono_wasm_send_dbg_command', 'bool', [ 'number', 'number', 'number', 'number', 'number' ]); - this._register_c_fn ('mono_wasm_send_dbg_command_with_parms', 'bool', [ 'number', 'number', 'number', 'number', 'number', 'number', 'string' ]); - - // DO NOT REMOVE - magic debugger init function - if (globalThis.dotnetDebugger) - debugger; - else - console.debug ("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28"); - }, - - // Set environment variable NAME to VALUE - // 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']); - this.wasm_setenv (name, value); - }, - - mono_wasm_set_runtime_options: function (options) { - if (!this.wasm_parse_runtime_options) - 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']); - let aindex = 0; - for (var i = 0; i < options.length; ++i) { - Module.setValue (argv + (aindex * 4), wasm_strdup (options [i]), "i32"); - aindex += 1; - } - this.wasm_parse_runtime_options (options.length, argv); - }, - - // - // Initialize the AOT profiler with OPTIONS. - // Requires the AOT profiler to be linked into the app. - // options = { write_at: "", send_to: "" } - // should be in the format ::. - // write_at defaults to 'WebAssembly.Runtime::StopProfile'. - // send_to defaults to 'WebAssembly.Runtime::DumpAotProfileData'. - // DumpAotProfileData stores the data into Module.aot_profile_data. - // - mono_wasm_init_aot_profiler: function (options) { - if (options == null) - options = {} - if (!('write_at' in options)) - options.write_at = 'Interop/Runtime::StopProfile'; - if (!('send_to' in options)) - options.send_to = 'Interop/Runtime::DumpAotProfileData'; - var arg = "aot:write-at-method=" + options.write_at + ",send-to-method=" + options.send_to; - Module.ccall ('mono_wasm_load_profiler_aot', null, ['string'], [arg]); - }, - - // options = { write_at: "", send_to: "" } - // should be in the format ::. - // write_at defaults to 'WebAssembly.Runtime::StopProfile'. - // send_to defaults to 'WebAssembly.Runtime::DumpCoverageProfileData'. - // DumpCoverageProfileData stores the data into Module.coverage_profile_data. - mono_wasm_init_coverage_profiler: function (options) { - if (options == null) - options = {} - if (!('write_at' in options)) - options.write_at = 'WebAssembly.Runtime::StopProfile'; - if (!('send_to' in options)) - options.send_to = 'WebAssembly.Runtime::DumpCoverageProfileData'; - var arg = "coverage:write-at-method=" + options.write_at + ",send-to-method=" + options.send_to; - Module.ccall ('mono_wasm_load_profiler_coverage', null, ['string'], [arg]); - }, - - _apply_configuration_from_args: function (args) { - for (var k in (args.environment_variables || {})) - MONO.mono_wasm_setenv (k, args.environment_variables[k]); - - if (args.runtime_options) - MONO.mono_wasm_set_runtime_options (args.runtime_options); - - 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.debug ("MONO_WASM: Loading... " + asset); - var binary = fs.readFileSync (asset); - var resolve_func2 = function (resolve, reject) { - resolve (new Uint8Array (binary)); - }; - - 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); - - var virtualName = asset.virtual_path || asset.name; - var offset = null; - - switch (asset.behavior) { - case "resource": - 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); - } - else if (asset.behavior === "resource") { - ctx.mono_wasm_add_satellite_assembly (virtualName, asset.culture, offset, bytes.length); - } - }, - - // 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.startsWith ("icudt") && file_name.endsWith (".dat")) { - // ICU data files are expected to be "icudt%FilterName%.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) - // "resource": load asset as a managed resource assembly - // "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; - } - }, - - // @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; - }, - - // @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) - MONO.num_icu_assets_loaded_successfully++; - return ok; - }, - - // Get icudt.dat exact filename that matches given culture, examples: - // "ja" -> "icudt_CJK.dat" - // "en_US" (or "en-US" or just "en") -> "icudt_EFIGS.dat" - // etc, see "mono_wasm_get_icudt_name" implementation in pal_icushim_static.c - mono_wasm_get_icudt_name: function (culture) { - return Module.ccall ('mono_wasm_get_icudt_name', 'string', ['string'], [culture]); - }, - - _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.debug ("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); - } - - let tz; - try { - tz = Intl.DateTimeFormat().resolvedOptions().timeZone; - } catch {} - MONO.mono_wasm_setenv ("TZ", tz || "UTC"); - 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']), - mono_wasm_add_satellite_assembly: Module.cwrap ('mono_wasm_add_satellite_assembly', 'void', ['string', '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) { - try { - attemptNextSource (); - return; - } catch (exc) { - console.error ("MONO_WASM: Unhandled exception in handleFetchResponse attemptNextSource for asset", asset.name, exc); - throw exc; - } - } - - 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 (); - } - }; - - 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.debug (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 if (asset.behavior === "resource") { - var path = asset.culture !== '' ? `${asset.culture}/${asset.name}` : asset.name; - attemptUrl = locateFile (args.assembly_root + "/" + path); - } - else - attemptUrl = asset.name; - } else { - attemptUrl = sourcePrefix + asset.name; - } - - try { - if (asset.name === attemptUrl) { - if (ctx.tracing) - console.log ("Attempting to fetch '" + attemptUrl + "'"); - } else { - if (ctx.tracing) - console.log ("Attempting to fetch '" + attemptUrl + "' for", asset.name); - } - 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 (MONO.num_icu_assets_loaded_successfully > 0) { - console.debug ("MONO_WASM: ICU data archive(s) loaded, disabling invariant mode"); - } else if (globalization_mode !== "icu") { - console.debug ("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"); - - // Set globalization mode to PredefinedCulturesOnly - this.mono_wasm_setenv ("DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", "1"); - }, - - // Used by the debugger to enumerate loaded dlls and pdbs - mono_wasm_get_loaded_files: function() { - if (!this.mono_wasm_set_is_debugger_attached) - this.mono_wasm_set_is_debugger_attached = Module.cwrap ('mono_wasm_set_is_debugger_attached', 'void', ['bool']); - this.mono_wasm_set_is_debugger_attached (true); - return MONO.loaded_files; - }, - - mono_wasm_get_loaded_asset_table: function() { - return MONO.loaded_assets; - }, - - // 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); - }, - - 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 { - var 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+1); - folders.add(directory); - }); - folders.forEach(folder => { - Module['FS_createPath'](prefix, folder, true, true); - }); - - for (var 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; - }, - - /** - * Raises an event for the debug proxy - * - * @param {Event} event - event to be raised - * @param {object} args - arguments for raising this event, eg. `{trace: true}` - */ - mono_wasm_raise_debug_event: function(event, args={}) { - if (typeof event !== 'object') - throw new Error(`event must be an object, but got ${JSON.stringify(event)}`); - - if (event.eventName === undefined) - throw new Error(`event.eventName is a required parameter, in event: ${JSON.stringify(event)}`); - - if (typeof args !== 'object') - throw new Error(`args must be an object, but got ${JSON.stringify(args)}`); - - console.debug('mono_wasm_debug_event_raised:aef14bca-5519-4dfe-b35a-f867abc123ae', JSON.stringify(event), JSON.stringify(args)); - }, - - /** - * Loads the mono config file (typically called mono-config.json) asynchroniously - * Note: the run dependencies are so emsdk actually awaits it in order. - * - * @param {string} configFilePath - relative path to the config file - * @throws Will throw an error if the config file loading fails - */ - mono_wasm_load_config: async function (configFilePath) { - Module.addRunDependency(configFilePath); - try { - let config = null; - // NOTE: when we add nodejs make sure to include the nodejs fetch package - if (ENVIRONMENT_IS_WEB) { - const configRaw = await fetch(configFilePath); - config = await configRaw.json(); - }else if (ENVIRONMENT_IS_NODE) { - config = require(configFilePath); - } else { // shell or worker - config = JSON.parse(read(configFilePath)); // read is a v8 debugger command - } - Module.config = config; - } catch(e) { - Module.config = {message: "failed to load config file", error: e}; - } finally { - Module.removeRunDependency(configFilePath); - } - }, - mono_wasm_set_timeout_exec: function(id){ - if (!this.mono_set_timeout_exec) - this.mono_set_timeout_exec = Module.cwrap ("mono_set_timeout_exec", null, [ 'number' ]); - this.mono_set_timeout_exec (id); - }, - prevent_timer_throttling: function () { - if (!MONO.isChromium) { - return; - } - - // this will schedule timers every second for next 6 minutes, it should be called from WebSocket event, to make it work - // on next call, it would only extend the timers to cover yet uncovered future - let now = new Date().valueOf(); - const desired_reach_time = now + (1000 * 60 * 6); - const next_reach_time = Math.max(now + 1000, this.spread_timers_maximum); - const light_throttling_frequency = 1000; - for (var schedule = next_reach_time; schedule < desired_reach_time; schedule += light_throttling_frequency) { - const delay = schedule - now; - setTimeout(() => { - this.mono_wasm_set_timeout_exec(0); - MONO.pump_count++; - MONO.pump_message(); - }, delay); - } - this.spread_timers_maximum = desired_reach_time; - } - }, - schedule_background_exec: function () { - ++MONO.pump_count; - if (typeof globalThis.setTimeout === 'function') { - globalThis.setTimeout (MONO.pump_message, 0); - } - }, - - mono_set_timeout: function (timeout, id) { - - if (typeof globalThis.setTimeout === 'function') { - globalThis.setTimeout (function () { - MONO.mono_wasm_set_timeout_exec (id); - }, timeout); - } else { - ++MONO.pump_count; - MONO.timeout_queue.push(function() { - MONO.mono_wasm_set_timeout_exec (id); - }) - } - }, - - mono_wasm_fire_debugger_agent_message: function () { - // eslint-disable-next-line no-debugger - debugger; - }, - - mono_wasm_asm_loaded: function (assembly_name, assembly_ptr, assembly_len, pdb_ptr, pdb_len) { - // Only trigger this codepath for assemblies loaded after app is ready - if (MONO.mono_wasm_runtime_is_ready !== true) - return; - - const assembly_name_str = assembly_name !== 0 ? Module.UTF8ToString(assembly_name).concat('.dll') : ''; - - const assembly_data = new Uint8Array(Module.HEAPU8.buffer, assembly_ptr, assembly_len); - const assembly_b64 = MONO._base64Converter.toBase64StringImpl(assembly_data); - - let pdb_b64; - if (pdb_ptr) { - const pdb_data = new Uint8Array(Module.HEAPU8.buffer, pdb_ptr, pdb_len); - pdb_b64 = MONO._base64Converter.toBase64StringImpl(pdb_data); - } - - MONO.mono_wasm_raise_debug_event({ - eventName: 'AssemblyLoaded', - assembly_name: assembly_name_str, - assembly_b64, - pdb_b64 - }); - } +const MonoSupportLib = { + // this line will be executed in mergeInto below + $MONO__postset: '__dotnet_runtime.export_functions(MONO, Module);', + // this will become globalThis.MONO + $MONO: {}, + // the methods would be visible to EMCC linker + mono_set_timeout: function () { return MONO.mono_set_timeout.call(MONO, arguments) }, + mono_wasm_asm_loaded: function () { return MONO.mono_wasm_asm_loaded.call(MONO, arguments) }, + mono_wasm_fire_debugger_agent_message: function () { return MONO.mono_wasm_fire_debugger_agent_message.call(MONO, arguments) }, + schedule_background_exec: function () { return MONO.schedule_background_exec.call(MONO, arguments) }, }; autoAddDeps(MonoSupportLib, '$MONO') diff --git a/src/mono/wasm/runtime/package-lock.json b/src/mono/wasm/runtime/package-lock.json new file mode 100644 index 0000000000000..60dd9a6b6f885 --- /dev/null +++ b/src/mono/wasm/runtime/package-lock.json @@ -0,0 +1,328 @@ +{ + "name": "dotnet-runtime", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/code-frame": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.14.5.tgz", + "integrity": "sha512-9pzDqyc6OLDaqe+zbACgFkb6fKMNG6CObKpnYXChRsvYGyEdc7CA2BaqeOM+vOtCS5ndmJicPJhKAwYRI6UfFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.14.5" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", + "dev": true + }, + "@babel/highlight": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", + "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.14.5", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@rollup/plugin-typescript": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz", + "integrity": "sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "resolve": "^1.17.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + } + } + }, + "@types/node": { + "version": "16.9.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz", + "integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "is-core-module": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", + "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "jest-worker": { + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", + "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picomatch": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", + "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "dev": true, + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, + "rollup": { + "version": "2.56.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz", + "integrity": "sha512-Au92NuznFklgQCUcV96iXlxUbHuB1vQMaH76DHl5M11TotjOHwqk9CwcrT78+Tnv4FN9uTBxq6p4EJoYkpyekg==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", + "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", + "dev": true + }, + "source-map-support": { + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "terser": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.8.0.tgz", + "integrity": "sha512-f0JH+6yMpneYcRJN314lZrSwu9eKkUFEHLN/kNy8ceh8gaRiLgFPJqrB9HsXjhEGdv4e/ekjTOFxIlL6xlma8A==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.7.2", + "source-map-support": "~0.5.20" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", + "dev": true + }, + "typescript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", + "dev": true + } + } +} diff --git a/src/mono/wasm/runtime/package.json b/src/mono/wasm/runtime/package.json new file mode 100644 index 0000000000000..16199c17c40e8 --- /dev/null +++ b/src/mono/wasm/runtime/package.json @@ -0,0 +1,26 @@ +{ + "name": "dotnet-runtime", + "description": ".NET is a developer platform with tools and libraries for building any type of app, including web, mobile, desktop, games, IoT, cloud, and microservices.", + "repository": { + "type": "git", + "url": "git@github.com:dotnet/runtime.git" + }, + "version": "1.0.0", + "scripts": { + "rollup": "rollup -c" + }, + "keywords": [ + "dotnet", + "runtime", + "wasm" + ], + "author": "Microsoft", + "license": "MIT", + "devDependencies": { + "@rollup/plugin-typescript": "8.2.5", + "rollup": "2.56.3", + "rollup-plugin-terser": "7.0.2", + "tslib": "2.3.1", + "typescript": "4.4.3" + } +} diff --git a/src/mono/wasm/runtime/rollup.config.js b/src/mono/wasm/runtime/rollup.config.js new file mode 100644 index 0000000000000..7b2777118ae96 --- /dev/null +++ b/src/mono/wasm/runtime/rollup.config.js @@ -0,0 +1,69 @@ +import { defineConfig } from 'rollup'; +import typescript from '@rollup/plugin-typescript'; +import { terser } from 'rollup-plugin-terser'; +import { readFile, writeFile, mkdir } from 'fs/promises'; +import * as fs from 'fs'; +import { createHash } from 'crypto'; + +const outputFileName = 'runtime.iffe.js' +const isDebug = process.env.Configuration !== 'Release'; +const nativeBinDir = process.env.NativeBinDir ? process.env.NativeBinDir.replace(/\"/g, '') : 'bin'; +const plugins = isDebug ? [writeOnChangePlugin()] : [terser(), writeOnChangePlugin()] + +export default defineConfig({ + treeshake: !isDebug, + input: 'src/startup.ts', + output: [{ + banner: '//! Licensed to the .NET Foundation under one or more agreements.\n//! The .NET Foundation licenses this file to you under the MIT license.\n', + name: '__dotnet_runtime', + file: nativeBinDir + '/src/' + outputFileName, + + // emcc doesn't know how to load ES6 module, that's why we need the whole rollup.js + format: 'iife', + plugins: plugins + }], + plugins: [typescript()] +}); + +// this would create .md5 file next to the output file, so that we do not touch datetime of the file if it's same -> faster incremental build. +function writeOnChangePlugin() { + return { + name: 'writeOnChange', + generateBundle: writeWhenChanged + } +} + +async function writeWhenChanged(options, bundle) { + try { + const asset = bundle[outputFileName]; + const code = asset.code; + const hashFileName = options.file + '.sha256'; + const oldHashExists = await checkFileExists(hashFileName); + const oldFileExists = await checkFileExists(options.file) + + var newHash = createHash('sha256').update(code).digest('hex'); + + let isOutputChanged = true; + if (oldHashExists && oldFileExists) { + const oldHash = await readFile(hashFileName, { encoding: 'ascii' }); + isOutputChanged = oldHash !== newHash + } + if (isOutputChanged) { + if (!await checkFileExists(hashFileName)) { + await mkdir(nativeBinDir + '/src', { recursive: true }); + } + await writeFile(hashFileName, newHash); + } else { + // this.warn('No change in ' + options.file) + delete bundle[outputFileName] + } + } catch (ex) { + this.warn(ex.toString()); + } +} + +function checkFileExists(file) { + return fs.promises.access(file, fs.constants.F_OK) + .then(() => true) + .catch(() => false) +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/binding/types.ts b/src/mono/wasm/runtime/src/binding/types.ts new file mode 100644 index 0000000000000..c8c38ce13487b --- /dev/null +++ b/src/mono/wasm/runtime/src/binding/types.ts @@ -0,0 +1,14 @@ +/* TODO this is from ASP +declare interface BINDING { + mono_obj_array_new(length: number): System_Array; + mono_obj_array_set(array: System_Array, index: Number, value: System_Object): void; + js_string_to_mono_string(jsString: string): System_String; + js_typed_array_to_array(array: Uint8Array): System_Object; + js_to_mono_obj(jsObject: any) : System_Object; + mono_array_to_js_array(array: System_Array) : Array; + conv_string(dotnetString: System_String | null): string | null; + bind_static_method(fqn: string, signature?: string): Function; + call_assembly_entry_point(assemblyName: string, args: any[], signature: any): Promise; + unbox_mono_obj(object: System_Object): any; + } + */ \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/mono/base64.ts b/src/mono/wasm/runtime/src/mono/base64.ts new file mode 100644 index 0000000000000..322b0f72700b7 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/base64.ts @@ -0,0 +1,121 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// Code from JSIL: +// https://github.com/sq/JSIL/blob/1d57d5427c87ab92ffa3ca4b82429cd7509796ba/JSIL.Libraries/Includes/Bootstrap/Core/Classes/System.Convert.js#L149 +// Thanks to Katelyn Gadd @kg + +export function toBase64StringImpl(inArray: Uint8Array, offset?: number, length?: number) { + var reader = _makeByteReader(inArray, offset, length); + var result = ""; + var ch1: number | null = 0, ch2: number | null = 0, ch3: number | null = 0 + var bits = 0, equalsCount = 0, sum = 0; + var mask1 = (1 << 24) - 1, mask2 = (1 << 18) - 1, mask3 = (1 << 12) - 1, mask4 = (1 << 6) - 1; + var shift1 = 18, shift2 = 12, shift3 = 6, shift4 = 0; + + while (true) { + ch1 = reader.read(); + ch2 = reader.read(); + ch3 = reader.read(); + + if (ch1 === null) + break; + if (ch2 === null) { + ch2 = 0; + equalsCount += 1; + } + if (ch3 === null) { + ch3 = 0; + equalsCount += 1; + } + + // Seems backwards, but is right! + sum = (ch1 << 16) | (ch2 << 8) | (ch3 << 0); + + bits = (sum & mask1) >> shift1; + result += _base64Table[bits]; + bits = (sum & mask2) >> shift2; + result += _base64Table[bits]; + + if (equalsCount < 2) { + bits = (sum & mask3) >> shift3; + result += _base64Table[bits]; + } + + if (equalsCount === 2) { + result += "=="; + } else if (equalsCount === 1) { + result += "="; + } else { + bits = (sum & mask4) >> shift4; + result += _base64Table[bits]; + } + } + + return result; +} + +const _base64Table = [ + 'A', 'B', 'C', 'D', + 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', + 'U', 'V', 'W', 'X', + 'Y', 'Z', + 'a', 'b', 'c', 'd', + 'e', 'f', 'g', 'h', + 'i', 'j', 'k', 'l', + 'm', 'n', 'o', 'p', + 'q', 'r', 's', 't', + 'u', 'v', 'w', 'x', + 'y', 'z', + '0', '1', '2', '3', + '4', '5', '6', '7', + '8', '9', + '+', '/' +] + +function _makeByteReader(bytes: Uint8Array, index?: number, count?: number): { + read: () => number | null +} { + var position = (typeof (index) === "number") ? index : 0; + var endpoint: number; + + if (typeof (count) === "number") + endpoint = (position + count); + else + endpoint = (bytes.length - position); + + var result = { + read: function () { + if (position >= endpoint) + return null; + + var nextByte = bytes[position]; + position += 1; + return nextByte; + } + }; + + Object.defineProperty(result, "eof", { + get: function () { + return (position >= endpoint); + }, + configurable: true, + enumerable: true + }); + + return result; +} + +// FIXME: improve +export function _base64_to_uint8(base64String: string) { + 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); +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/mono/cwraps.ts b/src/mono/wasm/runtime/src/mono/cwraps.ts new file mode 100644 index 0000000000000..c55a59b2d30be --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/cwraps.ts @@ -0,0 +1,57 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { Module } from '../runtime' + +const fn_signatures: [ident: string, returnType: string | null, argTypes?: string[], opts?: any][] = [ + ["mono_wasm_register_root", "number", ["number", "number", "string"]], + ["mono_wasm_deregister_root", null, ["number"]], + ["mono_wasm_string_get_data", null, ['number', 'number', 'number', 'number']], + ['mono_wasm_set_is_debugger_attached', 'void', ['bool']], + ['mono_wasm_send_dbg_command', 'bool', ['number', 'number', 'number', 'number', 'number']], + ['mono_wasm_send_dbg_command_with_parms', 'bool', ['number', 'number', 'number', 'number', 'number', 'number', 'string']], + ['mono_wasm_setenv', null, ['string', 'string']], + ['mono_wasm_parse_runtime_options', null, ['number', 'number']], + ['mono_wasm_strdup', 'number', ['string']], + ['mono_background_exec', null, []], + ["mono_set_timeout_exec", null, ['number']], + ['mono_wasm_load_icu_data', 'number', ['number']], + ['mono_wasm_get_icudt_name', 'string', ['string']], + ['mono_wasm_add_assembly', 'number', ['string', 'number', 'number']], + ['mono_wasm_add_satellite_assembly', 'void', ['string', 'string', 'number', 'number']], + ['mono_wasm_load_runtime', null, ['string', 'number']], + ['mono_wasm_exit', null, ['number']], +] + +export interface t_Cwraps { + mono_wasm_register_root(start: CharPtr, size: number, name: CharPtr): number; + mono_wasm_deregister_root(addr: CharPtr): void; + mono_wasm_string_get_data(string: MonoString, outChars: CharPtrPtr, outLengthBytes: Int32Ptr, outIsInterned: Int32Ptr): void; + mono_wasm_set_is_debugger_attached(value: boolean): void; + mono_wasm_send_dbg_command(id: number, command_set: number, command: number, data: VoidPtr, size: number): boolean; + mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, data: VoidPtr, size: number, valtype: number, newvalue: CharPtr): boolean; + mono_wasm_setenv(name: string, value: string): void; + mono_wasm_strdup(value: string): number; + mono_wasm_parse_runtime_options(length: number, argv: VoidPtr): void; + mono_background_exec(): void; + mono_set_timeout_exec(id: number): void; + mono_wasm_load_icu_data(offset: VoidPtr): number; + mono_wasm_get_icudt_name(name: string): string; + mono_wasm_add_assembly(name: CharPtr, data: VoidPtr, size: number): number; + mono_wasm_add_satellite_assembly(name: CharPtr, culture: CharPtr, data: VoidPtr, size: number): void; + mono_wasm_load_runtime(unused: CharPtr, debug_level: number): void; + mono_wasm_exit(exit_code: number): number; +} + +const wrapped_c_functions: t_Cwraps = {} +for (let sig of fn_signatures) { + const wf: any = wrapped_c_functions; + // lazy init on first run + wf[sig[0]] = function () { + const fce = Module.cwrap(sig[0], sig[1], sig[2], sig[3]) + wf[sig[0]] = fce; + return fce.apply(undefined, arguments); + }; +} + +export default wrapped_c_functions; \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/mono/debug.ts b/src/mono/wasm/runtime/src/mono/debug.ts new file mode 100644 index 0000000000000..75c15d4b87ba8 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/debug.ts @@ -0,0 +1,291 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { Module, MONO } from '../runtime' +import { toBase64StringImpl, _base64_to_uint8 } from './base64' +import cwraps from './cwraps' +import { mono_wasm_load_bytes_into_heap } from './roots' + +var commands_received: any; +var _call_function_res_cache: any = {} +var _next_call_function_res_id = 0; + +export function mono_wasm_runtime_ready() { + MONO.mono_wasm_runtime_is_ready = true; + + // FIXME: where should this go? + _next_call_function_res_id = 0; + _call_function_res_cache = {}; + + // DO NOT REMOVE - magic debugger init function + if ((globalThis).dotnetDebugger) + debugger; + else + console.debug("mono_wasm_runtime_ready", "fe00e07a-5519-4dfe-b35a-f867dbaf2e28"); +} + +export function mono_wasm_fire_debugger_agent_message() { + // eslint-disable-next-line no-debugger + debugger; +} + +export function mono_wasm_add_dbg_command_received(res_ok: boolean, id: number, buffer: number, buffer_len: number) { + const assembly_data = new Uint8Array(Module.HEAPU8.buffer, buffer, buffer_len); + const base64String = toBase64StringImpl(assembly_data); + const buffer_obj = { + res_ok, + res: { + id, + value: base64String + } + } + commands_received = buffer_obj; +} + +export function mono_wasm_send_dbg_command_with_parms(id: number, command_set: number, command: number, command_parameters: any, length: number, valtype: number, newvalue: number) { + const dataHeap = mono_wasm_load_bytes_into_heap(_base64_to_uint8(command_parameters)); + cwraps.mono_wasm_send_dbg_command_with_parms(id, command_set, command, dataHeap, length, valtype, newvalue.toString()); + + const { res_ok, res } = commands_received; + if (!res_ok) + throw new Error(`Failed on mono_wasm_invoke_method_debugger_agent_with_parms`); + return res; +} + +export function mono_wasm_send_dbg_command(id: number, command_set: number, command: number, command_parameters: any) { + var dataHeap = mono_wasm_load_bytes_into_heap(_base64_to_uint8(command_parameters)); + cwraps.mono_wasm_send_dbg_command(id, command_set, command, dataHeap, command_parameters.length); + + const { res_ok, res } = commands_received; + if (!res_ok) + throw new Error(`Failed on mono_wasm_send_dbg_command`); + return res; + +} + +export function mono_wasm_get_dbg_command_info() { + const { res_ok, res } = commands_received; + if (!res_ok) + throw new Error(`Failed on mono_wasm_get_dbg_command_info`); + return res; +} + +export function mono_wasm_debugger_resume() { +} + +export function mono_wasm_detach_debugger() { + cwraps.mono_wasm_set_is_debugger_attached(false); +} + +/** + * Raises an event for the debug proxy + */ +export function mono_wasm_raise_debug_event(event: WasmEvent, args = {}) { + if (typeof event !== 'object') + throw new Error(`event must be an object, but got ${JSON.stringify(event)}`); + + if (event.eventName === undefined) + throw new Error(`event.eventName is a required parameter, in event: ${JSON.stringify(event)}`); + + if (typeof args !== 'object') + throw new Error(`args must be an object, but got ${JSON.stringify(args)}`); + + console.debug('mono_wasm_debug_event_raised:aef14bca-5519-4dfe-b35a-f867abc123ae', JSON.stringify(event), JSON.stringify(args)); +} + +// Used by the debugger to enumerate loaded dlls and pdbs +export function mono_wasm_get_loaded_files() { + cwraps.mono_wasm_set_is_debugger_attached(true); + return MONO.loaded_files; +} + +function _create_proxy_from_object_id(objectId: string, details: any) { + if (objectId.startsWith('dotnet:array:')) { + const ret = details.map((p: any) => p.value); + return ret; + } + + const proxy: any = {}; + Object.keys(details).forEach(p => { + var prop = details[p]; + if (prop.get !== undefined) { + Object.defineProperty(proxy, + prop.name, + { + get() { + return mono_wasm_send_dbg_command(-1, prop.get.commandSet, prop.get.command, prop.get.buffer); + }, + set: function (newValue) { + mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return commands_received.res_ok; + } + } + ); + } else if (prop.set !== undefined) { + Object.defineProperty(proxy, + prop.name, + { + get() { + return prop.value; + }, + set: function (newValue) { + mono_wasm_send_dbg_command_with_parms(-1, prop.set.commandSet, prop.set.command, prop.set.buffer, prop.set.length, prop.set.valtype, newValue); return commands_received.res_ok; + } + } + ); + } else { + proxy[prop.name] = prop.value; + } + }); + return proxy; +} + +export function mono_wasm_call_function_on(request: CallRequest) { + if (request.arguments != undefined && !Array.isArray(request.arguments)) + throw new Error(`"arguments" should be an array, but was ${request.arguments}`); + + const objId = request.objectId; + const details = request.details; + let proxy; + + if (objId.startsWith('dotnet:cfo_res:')) { + if (objId in _call_function_res_cache) + proxy = _call_function_res_cache[objId]; + else + throw new Error(`Unknown object id ${objId}`); + } else { + proxy = _create_proxy_from_object_id(objId, details); + } + + 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 local_eval = eval; // https://rollupjs.org/guide/en/#avoiding-eval + const fn_res = local_eval(fn_eval_str); + if (fn_res === undefined) + return { type: "undefined" }; + + if (Object(fn_res) !== fn_res) { + if (typeof (fn_res) == "object" && fn_res == null) + return { type: typeof (fn_res), subtype: `${fn_res}`, value: null }; + return { type: typeof (fn_res), description: `${fn_res}`, value: `${fn_res}` }; + } + + if (request.returnByValue && fn_res.subtype == undefined) + return { type: "object", value: fn_res }; + + if (Object.getPrototypeOf(fn_res) == Array.prototype) { + + const fn_res_id = _cache_call_function_res(fn_res); + + return { + type: "object", + subtype: "array", + className: "Array", + description: `Array(${fn_res.length})`, + objectId: fn_res_id + }; + } + if (fn_res.value !== undefined || fn_res.subtype !== undefined) { + return fn_res; + } + + if (fn_res == proxy) + return { type: "object", className: "Object", description: "Object", objectId: objId }; + const fn_res_id = _cache_call_function_res(fn_res); + return { type: "object", className: "Object", description: "Object", objectId: fn_res_id }; +} + + +function _get_cfo_res_details(objectId: string, args: any) { + if (!(objectId in _call_function_res_cache)) + throw new Error(`Could not find any object with id ${objectId}`); + + const real_obj = _call_function_res_cache[objectId]; + + const descriptors = Object.getOwnPropertyDescriptors(real_obj); + if (args.accessorPropertiesOnly) { + Object.keys(descriptors).forEach(k => { + if (descriptors[k].get === undefined) + Reflect.deleteProperty(descriptors, k); + }); + } + + let res_details: any[] = []; + Object.keys(descriptors).forEach(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', ... }} + new_obj = Object.assign({ name: k }, prop_desc); + } else if (prop_desc.value !== undefined) { + // This is needed for values that were not added by us, + // thus are like { value: 5 } + // instead of { value: { type = 'number', value: 5 }} + // + // This can happen, for eg., when `length` gets added for arrays + // or `__proto__`. + new_obj = { + name: k, + // merge/add `type` and `description` to `d.value` + value: Object.assign({ type: (typeof prop_desc.value), description: '' + prop_desc.value }, + prop_desc) + }; + } else if (prop_desc.get !== undefined) { + // The real_obj has the actual getter. We are just returning a placeholder + // If the caller tries to run function on the cfo_res object, + // that accesses this property, then it would be run on `real_obj`, + // which *has* the original getter + new_obj = { + name: k, + get: { + className: "Function", + description: `get ${k} () {}`, + type: "function" + } + }; + } else { + new_obj = { name: k, value: { type: "symbol", value: "", description: "" } }; + } + + res_details.push(new_obj); + }); + + return { __value_as_json_string__: JSON.stringify(res_details) }; +} + +export function mono_wasm_get_details(objectId: string, args = {}) { + return _get_cfo_res_details(`dotnet:cfo_res:${objectId}`, args); +} + +function _cache_call_function_res(obj: any) { + const id = `dotnet:cfo_res:${_next_call_function_res_id++}`; + _call_function_res_cache[id] = obj; + return id; +} + +export function mono_wasm_release_object(objectId: string) { + if (objectId in _call_function_res_cache) + delete _call_function_res_cache[objectId]; +} + +type CallDetails = { + value: string +} + +type CallArgs = { + value: string +} + +type CallRequest = { + arguments: undefined | Array, + objectId: string, + details: CallDetails[], + functionDeclaration: string + returnByValue: boolean, +} + +type WasmEvent = { + eventName: string, // - name of the event being raised + [i: string]: any, // - arguments for the event itself +} diff --git a/src/mono/wasm/runtime/src/mono/icu.ts b/src/mono/wasm/runtime/src/mono/icu.ts new file mode 100644 index 0000000000000..041ba2cc5908e --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/icu.ts @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import cwraps from './cwraps' + +let num_icu_assets_loaded_successfully = 0; + +// @offset must be the address of an ICU data archive in the native heap. +// returns true on success. +export function mono_wasm_load_icu_data(offset: VoidPtr): boolean { + var ok = (cwraps.mono_wasm_load_icu_data(offset)) === 1; + if (ok) + num_icu_assets_loaded_successfully++; + return ok; +} + +// Get icudt.dat exact filename that matches given culture, examples: +// "ja" -> "icudt_CJK.dat" +// "en_US" (or "en-US" or just "en") -> "icudt_EFIGS.dat" +// etc, see "mono_wasm_get_icudt_name" implementation in pal_icushim_static.c +export function mono_wasm_get_icudt_name(culture: string): string { + return cwraps.mono_wasm_get_icudt_name(culture); +} + + +// 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". +export function mono_wasm_globalization_init(globalization_mode: GlobalizationMode) { + var invariantMode = false; + + if (globalization_mode === "invariant") + invariantMode = true; + + if (!invariantMode) { + if (num_icu_assets_loaded_successfully > 0) { + console.debug("MONO_WASM: ICU data archive(s) loaded, disabling invariant mode"); + } else if (globalization_mode !== "icu") { + console.debug("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) + cwraps.mono_wasm_setenv("DOTNET_SYSTEM_GLOBALIZATION_INVARIANT", "1"); + + // Set globalization mode to PredefinedCulturesOnly + cwraps.mono_wasm_setenv("DOTNET_SYSTEM_GLOBALIZATION_PREDEFINED_CULTURES_ONLY", "1"); +} + +export const enum GlobalizationMode { + ICU = "icu", // load ICU globalization data from any runtime assets with behavior "icu". + INVARIANT = "invariant", // operate in invariant globalization mode. + AUTO = "auto" // (default): if "icu" behavior assets are present, use ICU, otherwise invariant. +} diff --git a/src/mono/wasm/runtime/src/mono/init.ts b/src/mono/wasm/runtime/src/mono/init.ts new file mode 100644 index 0000000000000..bcb9819c58e39 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/init.ts @@ -0,0 +1,490 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { Module, MONO } from '../runtime' +import { AssetEntry, MonoConfig } from './types' +import cwraps from './cwraps' +import { mono_wasm_runtime_ready } from './debug'; +import { mono_wasm_load_bytes_into_heap } from './roots' +import { mono_wasm_globalization_init, mono_wasm_load_icu_data } from './icu'; +import { toBase64StringImpl } from './base64'; +import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from './profiler'; + +// Set environment variable NAME to VALUE +// Should be called before mono_load_runtime_and_bcl () in most cases +export function mono_wasm_setenv(name: string, value: string) { + cwraps.mono_wasm_setenv(name, value); +} + +export function mono_wasm_set_runtime_options(options: string[]) { + var argv = Module._malloc(options.length * 4); + let aindex = 0; + for (var i = 0; i < options.length; ++i) { + Module.setValue(argv + (aindex * 4), cwraps.mono_wasm_strdup(options[i]), "i32"); + aindex += 1; + } + cwraps.mono_wasm_parse_runtime_options(options.length, argv); +} + +function _handle_loaded_asset(ctx: MonoInitContext, asset: AssetEntry, url: string, blob: ArrayBuffer) { + var bytes = new Uint8Array(blob); + if (ctx.tracing) + console.log(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`); + + var virtualName: string = asset.virtual_path || asset.name; + var offset: VoidPtr | null = null; + + switch (asset.behavior) { + case "resource": + case "assembly": + ctx.loaded_files.push({ url: url, file: virtualName }); + case "heap": + case "icu": + offset = 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 (!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 = cwraps.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 (!mono_wasm_load_icu_data(offset!)) + console.error("Error loading ICU asset", asset.name); + } + else if (asset.behavior === "resource") { + cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length); + } +} + +// 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) +// "resource": load asset as a managed resource assembly +// "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 +export function mono_load_runtime_and_bcl_args(args: MonoConfig) { + try { + return _load_assets_and_runtime(args); + } catch (exc: any) { + console.error("error in mono_load_runtime_and_bcl_args:", exc.toString()); + throw exc; + } +} + +function _apply_configuration_from_args(args: MonoConfig) { + for (var k in (args.environment_variables || {})) + mono_wasm_setenv(k, args.environment_variables![k]); + + if (args.runtime_options) + mono_wasm_set_runtime_options(args.runtime_options); + + if (args.aot_profiler_options) + mono_wasm_init_aot_profiler(args.aot_profiler_options); + + if (args.coverage_profiler_options) + mono_wasm_init_coverage_profiler(args.coverage_profiler_options); +} + +function _get_fetch_file_cb_from_args(args: MonoConfig): (asset: string) => Promise { + if (typeof (args.fetch_file_cb) === "function") + return args.fetch_file_cb; + + if (ENVIRONMENT_IS_NODE) { + var fs = require('fs'); + return function (asset) { + console.debug("MONO_WASM: Loading... " + asset); + var binary = fs.readFileSync(asset); + var resolve_func2 = function (resolve: Function, reject: Function) { + resolve(new Uint8Array(binary)); + }; + + var resolve_func1 = function (resolve: Function, reject: Function) { + 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'."); + } +} + +function _finalize_startup(args: MonoConfig, ctx: MonoInitContext) { + var loaded_files_with_debug_info: string[] = []; + ctx.loaded_files.forEach(value => loaded_files_with_debug_info.push(value.url)); + MONO.loaded_assets = ctx.loaded_assets; + 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 = cwraps.mono_wasm_load_runtime; + + console.debug("MONO_WASM: Initializing mono runtime"); + + mono_wasm_globalization_init(args.globalization_mode!); + + if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { + try { + load_runtime("unused", args.debug_level || 0); + } catch (ex: any) { + Module.print("MONO_WASM: load_runtime () failed: " + ex); + Module.print("MONO_WASM: Stacktrace: \n"); + Module.print(ex.stack); + + var wasm_exit = cwraps.mono_wasm_exit; + wasm_exit(1); + } + } else { + load_runtime("unused", args.debug_level || 0); + } + + let tz; + try { + tz = Intl.DateTimeFormat().resolvedOptions().timeZone; + } catch { } + mono_wasm_setenv("TZ", tz || "UTC"); + mono_wasm_runtime_ready(); + args.loaded_cb(); +} + +function _load_assets_and_runtime(args: MonoConfig) { + 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: MonoInitContext = { + tracing: args.diagnostic_tracing || false, + pending_count: args.assets.length, + 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)); + + _apply_configuration_from_args(args); + + var fetch_file_cb = _get_fetch_file_cb_from_args(args); + + var onPendingRequestComplete = function () { + --ctx.pending_count; + + if (ctx.pending_count === 0) { + try { + _finalize_startup(args, ctx); + } catch (exc) { + console.error("Unhandled exception in _finalize_startup", exc); + throw exc; + } + } + }; + + var processFetchResponseBuffer = function (asset: AssetEntry, url: string, buffer: ArrayBuffer) { + try { + _handle_loaded_asset(ctx, asset, url, buffer); + } catch (exc) { + console.error(`Unhandled exception in processFetchResponseBuffer ${url}`, exc); + throw exc; + } finally { + onPendingRequestComplete(); + } + }; + + args.assets.forEach(function (asset: AssetEntry) { + var sourceIndex = 0; + var sourcesList = asset.load_remote ? args.remote_sources! : [""]; + + var handleFetchResponse = function (response: Response) { + if (!response.ok) { + try { + attemptNextSource(); + return; + } catch (exc) { + console.error("MONO_WASM: Unhandled exception in handleFetchResponse attemptNextSource for asset", asset.name, exc); + throw exc; + } + } + + try { + var bufferPromise = response.arrayBuffer(); + bufferPromise.then((data) => processFetchResponseBuffer(asset, response.url, data)); + } catch (exc) { + console.error("MONO_WASM: Unhandled exception in handleFetchResponse for asset", asset.name, exc); + attemptNextSource(); + } + }; + + const 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$/) && args.ignore_pdb_load_errors); + + if (isOk) + console.debug(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 if (asset.behavior === "resource") { + var path = asset.culture !== '' ? `${asset.culture}/${asset.name}` : asset.name; + attemptUrl = locateFile(args.assembly_root + "/" + path); + } + else + attemptUrl = asset.name; + } else { + attemptUrl = sourcePrefix + asset.name; + } + + try { + if (asset.name === attemptUrl) { + if (ctx.tracing) + console.log("Attempting to fetch '" + attemptUrl + "'"); + } else { + if (ctx.tracing) + console.log("Attempting to fetch '" + attemptUrl + "' for", asset.name); + } + var fetch_promise = fetch_file_cb(attemptUrl); + fetch_promise.then(handleFetchResponse); + } catch (exc) { + console.error("MONO_WASM: Error fetching " + attemptUrl, exc); + attemptNextSource(); + } + }; + + attemptNextSource(); + }); +} + +// used from ASP.NET +export function mono_wasm_load_data_archive(data: TypedArray, prefix: string) { + 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 { + var 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 + 1); + folders.add(directory); + }); + folders.forEach(folder => { + Module['FS_createPath'](prefix, folder, true, true); + }); + + for (var 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; +} + +/** + * Loads the mono config file (typically called mono-config.json) asynchroniously + * Note: the run dependencies are so emsdk actually awaits it in order. + * + * @param {string} configFilePath - relative path to the config file + * @throws Will throw an error if the config file loading fails + */ +export async function mono_wasm_load_config(configFilePath: string) { + Module.addRunDependency(configFilePath); + try { + let config = null; + // NOTE: when we add nodejs make sure to include the nodejs fetch package + if (ENVIRONMENT_IS_WEB) { + const configRaw = await fetch(configFilePath); + config = await configRaw.json(); + } else if (ENVIRONMENT_IS_NODE) { + config = require(configFilePath); + } else { // shell or worker + config = JSON.parse(read(configFilePath)); // read is a v8 debugger command + } + MONO.config = config; + Module.config = MONO.config; + } catch (e) { + const errMessage = "failed to load config file " + configFilePath; + console.error(errMessage) + MONO.config = { message: errMessage, error: e }; + Module.config = MONO.config; + } finally { + Module.removeRunDependency(configFilePath); + } +} + +export function mono_wasm_asm_loaded(assembly_name: number, assembly_ptr: number, assembly_len: number, pdb_ptr: number, pdb_len: number) { + // Only trigger this codepath for assemblies loaded after app is ready + if (MONO.mono_wasm_runtime_is_ready !== true) + return; + + const assembly_name_str = assembly_name !== 0 ? Module.UTF8ToString(assembly_name).concat('.dll') : ''; + const assembly_data = new Uint8Array(Module.HEAPU8.buffer, assembly_ptr, assembly_len); + const assembly_b64 = toBase64StringImpl(assembly_data); + + let pdb_b64; + if (pdb_ptr) { + const pdb_data = new Uint8Array(Module.HEAPU8.buffer, pdb_ptr, pdb_len); + pdb_b64 = toBase64StringImpl(pdb_data); + } + + MONO.mono_wasm_raise_debug_event({ + eventName: 'AssemblyLoaded', + assembly_name: assembly_name_str, + assembly_b64, + pdb_b64 + }); +} + +type MonoInitContext = { + tracing: boolean, + pending_count: number, + loaded_files: { url: string, file: string }[], + loaded_assets: { [id: string]: [VoidPtr, number] }, + createPath: Function, + createDataFile: Function +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/mono/profiler.ts b/src/mono/wasm/runtime/src/mono/profiler.ts new file mode 100644 index 0000000000000..270ef69061345 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/profiler.ts @@ -0,0 +1,48 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { Module } from '../runtime' + +// Initialize the AOT profiler with OPTIONS. +// Requires the AOT profiler to be linked into the app. +// options = { write_at: "", send_to: "" } +// should be in the format ::. +// write_at defaults to 'WebAssembly.Runtime::StopProfile'. +// send_to defaults to 'WebAssembly.Runtime::DumpAotProfileData'. +// DumpAotProfileData stores the data into Module.aot_profile_data. +// +export function mono_wasm_init_aot_profiler(options: AOTProfilerOptions) { + if (options == null) + options = {} + if (!('write_at' in options)) + options.write_at = 'Interop/Runtime::StopProfile'; + if (!('send_to' in options)) + options.send_to = 'Interop/Runtime::DumpAotProfileData'; + var arg = "aot:write-at-method=" + options.write_at + ",send-to-method=" + options.send_to; + Module.ccall('mono_wasm_load_profiler_aot', null, ['string'], [arg]); +} + +// options = { write_at: "", send_to: "" } +// should be in the format ::. +// write_at defaults to 'WebAssembly.Runtime::StopProfile'. +// send_to defaults to 'WebAssembly.Runtime::DumpCoverageProfileData'. +// DumpCoverageProfileData stores the data into Module.coverage_profile_data. +export function mono_wasm_init_coverage_profiler(options: CoverageProfilerOptions) { + if (options == null) + options = {} + if (!('write_at' in options)) + options.write_at = 'WebAssembly.Runtime::StopProfile'; + if (!('send_to' in options)) + options.send_to = 'WebAssembly.Runtime::DumpCoverageProfileData'; + var arg = "coverage:write-at-method=" + options.write_at + ",send-to-method=" + options.send_to; + Module.ccall('mono_wasm_load_profiler_coverage', null, ['string'], [arg]); +} + +export type AOTProfilerOptions = { + write_at?: string, // should be in the format ::, default: 'WebAssembly.Runtime::StopProfile' + send_to?: string // should be in the format ::, default: 'WebAssembly.Runtime::DumpAotProfileData' (DumpAotProfileData stores the data into Module.aot_profile_data.) +} +export type CoverageProfilerOptions = { + write_at?: string, // should be in the format ::, default: 'WebAssembly.Runtime::StopProfile' + send_to?: string // should be in the format ::, default: 'WebAssembly.Runtime::DumpCoverageProfileData' (DumpCoverageProfileData stores the data into Module.coverage_profile_data.) +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/mono/roots.ts b/src/mono/wasm/runtime/src/mono/roots.ts new file mode 100644 index 0000000000000..220a6e4927f40 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/roots.ts @@ -0,0 +1,304 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import cwraps from './cwraps' +import { Module } from '../runtime' + +const maxScratchRoots = 8192; +let _scratch_root_buffer: WasmRootBuffer | null = null; +let _scratch_root_free_indices: Int32Array | null = null; +let _scratch_root_free_indices_count = 0; +const _scratch_root_free_instances: WasmRoot[] = []; + +// @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. +export function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr { + var memoryOffset = Module._malloc(bytes.length); + var heapBytes = new Uint8Array(Module.HEAPU8.buffer, memoryOffset, bytes.length); + heapBytes.set(bytes); + return memoryOffset; +} + +/** + * Allocates a block of memory that can safely contain pointers into the managed heap. + * The result object has get(index) and set(index, value) methods that can be used to retrieve and store managed pointers. + * Once you are done using the root buffer, you must call its release() method. + * For small numbers of roots, it is preferable to use the mono_wasm_new_root and mono_wasm_new_roots APIs instead. + */ +export function mono_wasm_new_root_buffer(capacity: number, msg: string): WasmRootBuffer { + if (capacity <= 0) + throw new Error("capacity >= 1"); + + capacity = capacity | 0; + + var capacityBytes = capacity * 4; + var offset = Module._malloc(capacityBytes); + if ((offset % 4) !== 0) + throw new Error("Malloc returned an unaligned offset"); + + _zero_region(offset, capacityBytes); + + return new WasmRootBuffer(offset, capacity, true, msg); +} + +/** + * Creates a root buffer object representing an existing allocation in the native heap and registers + * the allocation with the GC. The caller is responsible for managing the lifetime of the allocation. + */ +export function mono_wasm_new_root_buffer_from_pointer(offset: number, capacity: number, msg: string): WasmRootBuffer { + if (capacity <= 0) + throw new Error("capacity >= 1"); + + capacity = capacity | 0; + + var capacityBytes = capacity * 4; + if ((offset % 4) !== 0) + throw new Error("Unaligned offset"); + + _zero_region(offset, capacityBytes); + + return new WasmRootBuffer(offset, capacity, false, msg); +} + +/** + * Allocates temporary storage for a pointer into the managed heap. + * Pointers stored here will be visible to the GC, ensuring that the object they point to aren't moved or collected. + * If you already have a managed pointer you can pass it as an argument to initialize the temporary storage. + * The result object has get() and set(value) methods, along with a .value property. + * When you are done using the root you must call its .release() method. + */ +export function mono_wasm_new_root(value: ManagedPointer | undefined = undefined): WasmRoot { + var result: WasmRoot; + + if (_scratch_root_free_instances.length > 0) { + result = _scratch_root_free_instances.pop()!; + } else { + var index = _mono_wasm_claim_scratch_index(); + var buffer = _scratch_root_buffer; + + result = new WasmRoot(buffer!, index); + } + + if (value !== undefined) { + if (typeof (value) !== "number") + throw new Error("value must be an address in the managed heap"); + + result.set(value); + } else { + result.set(0); + } + + return result; +} + +/** + * Allocates 1 or more temporary roots, accepting either a number of roots or an array of pointers. + * mono_wasm_new_roots(n): returns an array of N zero-initialized roots. + * mono_wasm_new_roots([a, b, ...]) returns an array of new roots initialized with each element. + * Each root must be released with its release method, or using the mono_wasm_release_roots API. + */ +export function mono_wasm_new_roots(count_or_values: number | ManagedPointer[]): WasmRoot[] { + var result; + + if (Array.isArray(count_or_values)) { + result = new Array(count_or_values.length); + for (var i = 0; i < result.length; i++) + result[i] = mono_wasm_new_root(count_or_values[i]); + } else if ((count_or_values | 0) > 0) { + result = new Array(count_or_values); + for (var i = 0; i < result.length; i++) + result[i] = mono_wasm_new_root(); + } else { + throw new Error("count_or_values must be either an array or a number greater than 0"); + } + + return result; +} + +/** + * Releases 1 or more root or root buffer objects. + * Multiple objects may be passed on the argument list. + * 'undefined' may be passed as an argument so it is safe to call this method from finally blocks + * even if you are not sure all of your roots have been created yet. + * @param {... WasmRoot} roots + */ +export function mono_wasm_release_roots() { + for (var i = 0; i < arguments.length; i++) { + if (!arguments[i]) + continue; + + arguments[i].release(); + } +} + +function _zero_region(byteOffset: number, sizeBytes: number) { + if (((byteOffset % 4) === 0) && ((sizeBytes % 4) === 0)) + Module.HEAP32.fill(0, byteOffset / 4, sizeBytes / 4); + else + Module.HEAP8.fill(0, byteOffset, sizeBytes); +} + +function _mono_wasm_release_scratch_index(index: number) { + if (index === undefined) + return; + + _scratch_root_buffer!.set(index, 0); + _scratch_root_free_indices![_scratch_root_free_indices_count] = index; + _scratch_root_free_indices_count++; +} + +function _mono_wasm_claim_scratch_index() { + if (!_scratch_root_buffer || !_scratch_root_free_indices) { + _scratch_root_buffer = mono_wasm_new_root_buffer(maxScratchRoots, "js roots"); + + _scratch_root_free_indices = new Int32Array(maxScratchRoots); + _scratch_root_free_indices_count = maxScratchRoots; + for (var i = 0; i < maxScratchRoots; i++) + _scratch_root_free_indices[i] = maxScratchRoots - i - 1; + } + + if (_scratch_root_free_indices_count < 1) + throw new Error("Out of scratch root space"); + + var result = _scratch_root_free_indices[_scratch_root_free_indices_count - 1]; + _scratch_root_free_indices_count--; + return result; +} + + +export class WasmRootBuffer { + private __count: number; + private length: number; + private __offset: number; + private __offset32: number; + private __handle: number; + private __ownsAllocation: boolean; + + constructor(offset: number, capacity: number, ownsAllocation: boolean, msg: string) { + const capacityBytes = capacity * 4; + + this.__offset = offset; + this.__offset32 = (offset / 4) | 0; + this.__count = capacity; + this.length = capacity; + this.__handle = cwraps.mono_wasm_register_root(offset, capacityBytes, msg || 0); + this.__ownsAllocation = ownsAllocation; + } + + _throw_index_out_of_range() { + throw new Error("index out of range"); + } + + _check_in_range(index: number) { + if ((index >= this.__count) || (index < 0)) + this._throw_index_out_of_range(); + } + + get_address(index: number): NativePointer { + this._check_in_range(index); + return this.__offset + (index * 4); + } + + get_address_32(index: number) { + this._check_in_range(index); + return this.__offset32 + index; + } + + get(index: number): ManagedPointer { + this._check_in_range(index); + return Module.HEAP32[this.get_address_32(index)]; + } + + set(index: number, value: ManagedPointer) { + Module.HEAP32[this.get_address_32(index)] = value; + return value; + } + + _unsafe_get(index: number) { + return Module.HEAP32[this.__offset32 + index]; + } + + _unsafe_set(index: number, value: ManagedPointer) { + Module.HEAP32[this.__offset32 + index] = value; + } + + clear() { + if (this.__offset) + _zero_region(this.__offset, this.__count * 4); + } + + release() { + if (this.__offset && this.__ownsAllocation) { + cwraps.mono_wasm_deregister_root(this.__offset); + _zero_region(this.__offset, this.__count * 4); + Module._free(this.__offset); + } + + this.__handle = this.__offset = this.__count = this.__offset32 = 0; + } + + toString() { + return "[root buffer @" + this.get_address(0) + ", size " + this.__count + "]"; + } +} + +export class WasmRoot { + private __buffer: WasmRootBuffer; + private __index: number; + + constructor(buffer: WasmRootBuffer, index: number) { + this.__buffer = buffer;//TODO + this.__index = index; + } + + get_address(): NativePointer { + return this.__buffer.get_address(this.__index); + } + + get_address_32(): number { + return this.__buffer.get_address_32(this.__index); + } + + get(): ManagedPointer { + var result = this.__buffer._unsafe_get(this.__index); + return result; + } + + set(value: ManagedPointer) { + this.__buffer._unsafe_set(this.__index, value); + return value; + } + + get value(): ManagedPointer { + return this.get(); + } + + set value(value: ManagedPointer) { + this.set(value); + } + + /** @returns {ManagedPointer} */ + valueOf(): ManagedPointer { + return this.get(); + } + + clear() { + this.set(0); + } + + release() { + const maxPooledInstances = 128; + if (_scratch_root_free_instances.length > maxPooledInstances) { + _mono_wasm_release_scratch_index(this.__index); + (this).__buffer = null; + this.__index = 0; + } else { + this.set(0); + _scratch_root_free_instances.push(this); + } + } + + toString() { + return "[root @" + this.get_address() + "]"; + } +} diff --git a/src/mono/wasm/runtime/src/mono/scheduling.ts b/src/mono/wasm/runtime/src/mono/scheduling.ts new file mode 100644 index 0000000000000..060fc9df99ff5 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/scheduling.ts @@ -0,0 +1,78 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import cwraps from './cwraps' + +const timeout_queue: Function[] = []; +let spread_timers_maximum = 0; +let isChromium = false; +let pump_count = 0; + +if (globalThis.navigator) { + const nav: any = globalThis.navigator; + if (nav.userAgentData && nav.userAgentData.brands) { + isChromium = nav.userAgentData.brands.some((i: any) => i.brand == 'Chromium'); + } + else if (nav.userAgent) { + isChromium = nav.userAgent.includes("Chrome"); + } +} + +function pump_message() { + while (timeout_queue.length > 0) { + --pump_count; + const cb: Function = timeout_queue.shift()!; + cb(); + } + while (pump_count > 0) { + --pump_count; + cwraps.mono_background_exec(); + } +} + +function mono_wasm_set_timeout_exec(id: number) { + cwraps.mono_set_timeout_exec(id); +} + +export function prevent_timer_throttling() { + if (isChromium) { + return; + } + + // this will schedule timers every second for next 6 minutes, it should be called from WebSocket event, to make it work + // on next call, it would only extend the timers to cover yet uncovered future + let now = new Date().valueOf(); + const desired_reach_time = now + (1000 * 60 * 6); + const next_reach_time = Math.max(now + 1000, spread_timers_maximum); + const light_throttling_frequency = 1000; + for (var schedule = next_reach_time; schedule < desired_reach_time; schedule += light_throttling_frequency) { + const delay = schedule - now; + setTimeout(() => { + mono_wasm_set_timeout_exec(0); + pump_count++; + pump_message(); + }, delay); + } + spread_timers_maximum = desired_reach_time; +} + +export function schedule_background_exec() { + ++pump_count; + if (typeof globalThis.setTimeout === 'function') { + globalThis.setTimeout(pump_message, 0); + } +} + +export function mono_set_timeout(timeout: number, id: number) { + + if (typeof globalThis.setTimeout === 'function') { + globalThis.setTimeout(function () { + mono_wasm_set_timeout_exec(id); + }, timeout); + } else { + ++pump_count; + timeout_queue.push(function () { + mono_wasm_set_timeout_exec(id); + }) + } +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/mono/string-decoder.ts b/src/mono/wasm/runtime/src/mono/string-decoder.ts new file mode 100644 index 0000000000000..cca7fc8f15472 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/string-decoder.ts @@ -0,0 +1,81 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { mono_wasm_new_root } from './roots' +import cwraps from './cwraps' +import { Module } from '../runtime' + +const mono_wasm_empty_string = ""; + +export class StringDecoder { + + private mono_wasm_string_root: any; + private mono_text_decoder: TextDecoder | undefined | null; + private mono_wasm_string_decoder_buffer: number | undefined; + private interned_string_table: Map | undefined; + + copy(mono_string: MonoString) { + if (!this.interned_string_table || !this.mono_wasm_string_decoder_buffer) { + this.mono_text_decoder = typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-16le') : null; + this.mono_wasm_string_root = mono_wasm_new_root(); + this.mono_wasm_string_decoder_buffer = Module._malloc(12); + this.interned_string_table = new Map(); + } + if (mono_string === 0) + return null; + + this.mono_wasm_string_root.value = mono_string; + + let ppChars = this.mono_wasm_string_decoder_buffer + 0, + pLengthBytes = this.mono_wasm_string_decoder_buffer + 4, + pIsInterned = this.mono_wasm_string_decoder_buffer + 8; + + cwraps.mono_wasm_string_get_data(mono_string, ppChars, pLengthBytes, pIsInterned); + + let result = mono_wasm_empty_string; + let lengthBytes = Module.HEAP32[pLengthBytes / 4], + pChars = Module.HEAP32[ppChars / 4], + isInterned = Module.HEAP32[pIsInterned / 4]; + + if (pLengthBytes && pChars) { + if ( + isInterned && + this.interned_string_table && + this.interned_string_table.has(mono_string) //TODO remove 2x lookup + ) { + result = this.interned_string_table.get(mono_string)!; + // console.log("intern table cache hit", mono_string, result.length); + } else { + result = this.decode(pChars, pChars + lengthBytes); + if (isInterned) { + // console.log("interned", mono_string, result.length); + this.interned_string_table.set(mono_string, result); + } + } + } + + this.mono_wasm_string_root.value = 0; + return result; + } + + private decode(start: number, end: number) { + var str = ""; + if (this.mono_text_decoder) { + // When threading is enabled, TextDecoder does not accept a view of a + // SharedArrayBuffer, we must make a copy of the array first. + // See https://github.com/whatwg/encoding/issues/172 + var subArray = typeof SharedArrayBuffer !== 'undefined' && Module.HEAPU8.buffer instanceof SharedArrayBuffer + ? Module.HEAPU8.slice(start, end) + : Module.HEAPU8.subarray(start, end); + + str = this.mono_text_decoder.decode(subArray); + } else { + for (var i = 0; i < end - start; i += 2) { + var char = Module.getValue(start + i, 'i16'); + str += String.fromCharCode(char); + } + } + + return str; + } +} diff --git a/src/mono/wasm/runtime/src/mono/types.ts b/src/mono/wasm/runtime/src/mono/types.ts new file mode 100644 index 0000000000000..597850e14b8f2 --- /dev/null +++ b/src/mono/wasm/runtime/src/mono/types.ts @@ -0,0 +1,157 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { + mono_wasm_new_root, mono_wasm_new_roots, mono_wasm_release_roots, + mono_wasm_new_root_buffer, mono_wasm_new_root_buffer_from_pointer +} from './roots' +import { + mono_wasm_add_dbg_command_received, + mono_wasm_send_dbg_command_with_parms, + mono_wasm_send_dbg_command, + mono_wasm_get_dbg_command_info, + mono_wasm_get_details, + mono_wasm_release_object, + mono_wasm_call_function_on, + mono_wasm_debugger_resume, + mono_wasm_detach_debugger, + mono_wasm_runtime_ready, + mono_wasm_get_loaded_files, + mono_wasm_raise_debug_event, + mono_wasm_fire_debugger_agent_message +} from './debug' +import { StringDecoder } from './string-decoder' +import { mono_wasm_load_bytes_into_heap } from './roots' +import { + mono_load_runtime_and_bcl_args, mono_wasm_load_config, + mono_wasm_setenv, mono_wasm_set_runtime_options, mono_wasm_load_data_archive, mono_wasm_asm_loaded +} from './init' +import { prevent_timer_throttling, mono_set_timeout, schedule_background_exec } from './scheduling' +import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name, GlobalizationMode } from './icu' +import { AOTProfilerOptions, CoverageProfilerOptions } from './profiler' + +// this represents visibility for the C code +export interface t_MonoSupportLib { + mono_set_timeout: typeof mono_set_timeout + mono_wasm_asm_loaded: typeof mono_wasm_asm_loaded + mono_wasm_fire_debugger_agent_message: typeof mono_wasm_fire_debugger_agent_message, + schedule_background_exec: typeof schedule_background_exec + mono_wasm_setenv: typeof mono_wasm_setenv + mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer; + mono_wasm_new_root_buffer_from_pointer: typeof mono_wasm_new_root_buffer_from_pointer; + mono_wasm_new_root: typeof mono_wasm_new_root; + mono_wasm_new_roots: typeof mono_wasm_new_roots; + mono_wasm_release_roots: typeof mono_wasm_release_roots; + mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap; + mono_wasm_get_loaded_files: typeof mono_wasm_get_loaded_files; + mono_wasm_load_icu_data: typeof mono_wasm_load_icu_data; + mono_wasm_get_icudt_name: typeof mono_wasm_get_icudt_name; +} + +// this represents visibility in the javascript +// TODO limit this only to public API methods +export interface t_MONO { + loaded_files: string[]; + loaded_assets: { [id: string]: [VoidPtr, number] }, + mono_wasm_runtime_is_ready: boolean; + config?: MonoConfig, + + string_decoder: StringDecoder, + mono_set_timeout: typeof mono_set_timeout + mono_wasm_asm_loaded: typeof mono_wasm_asm_loaded + mono_wasm_fire_debugger_agent_message: typeof mono_wasm_fire_debugger_agent_message, + schedule_background_exec: typeof schedule_background_exec + mono_wasm_new_root: typeof mono_wasm_new_root, + mono_wasm_new_roots: typeof mono_wasm_new_roots, + mono_wasm_release_roots: typeof mono_wasm_release_roots, + mono_wasm_new_root_buffer: typeof mono_wasm_new_root_buffer, + mono_wasm_new_root_buffer_from_pointer: typeof mono_wasm_new_root_buffer_from_pointer + mono_wasm_load_bytes_into_heap: typeof mono_wasm_load_bytes_into_heap + mono_wasm_add_dbg_command_received: typeof mono_wasm_add_dbg_command_received + mono_wasm_send_dbg_command_with_parms: typeof mono_wasm_send_dbg_command_with_parms + mono_wasm_send_dbg_command: typeof mono_wasm_send_dbg_command + mono_wasm_get_dbg_command_info: typeof mono_wasm_get_dbg_command_info + mono_wasm_get_details: typeof mono_wasm_get_details + mono_wasm_release_object: typeof mono_wasm_release_object + mono_wasm_call_function_on: typeof mono_wasm_call_function_on + mono_wasm_debugger_resume: typeof mono_wasm_debugger_resume + mono_wasm_detach_debugger: typeof mono_wasm_detach_debugger + mono_wasm_runtime_ready: typeof mono_wasm_runtime_ready + mono_wasm_get_loaded_files: typeof mono_wasm_get_loaded_files + mono_wasm_raise_debug_event: typeof mono_wasm_raise_debug_event + mono_load_runtime_and_bcl_args: typeof mono_load_runtime_and_bcl_args + prevent_timer_throttling: typeof prevent_timer_throttling + mono_wasm_load_icu_data: typeof mono_wasm_load_icu_data + mono_wasm_get_icudt_name: typeof mono_wasm_get_icudt_name + mono_wasm_load_config: typeof mono_wasm_load_config + mono_wasm_setenv: typeof mono_wasm_setenv + mono_wasm_set_runtime_options: typeof mono_wasm_set_runtime_options + mono_wasm_load_data_archive: typeof mono_wasm_load_data_archive +} + +// how we extended wasm Module +export interface t_ModuleExtension { + config?: MonoConfig, +} + +export type MonoConfig = { + assembly_root: string, // the subfolder containing managed assemblies and pdbs + assets: (AssetEntry | AssetEntry | SatelliteAssemblyEntry | VfsEntry | IcuData)[], // a list of assets to load along with the runtime. each asset is a dictionary-style Object with the following properties: + loaded_cb: Function, // a function invoked when loading has completed + debug_level?: number, // Either this or the next one needs to be set + enable_debugging?: number, // Either this or the previous one needs to be set + fetch_file_cb?: Request, // 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. + globalization_mode: GlobalizationMode, // configures the runtime's globalization mode + assembly_list?: any, // obsolete but necessary for the check + runtime_assets?: any, // obsolete but necessary for the check + runtime_asset_sources?: any, // obsolete but necessary for the check + diagnostic_tracing?: boolean // enables diagnostic log messages during startup + remote_sources?: string[], // 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?: { + [i: string]: string; + }, // dictionary-style Object containing environment variables + runtime_options?: string[], // array of runtime options as strings + aot_profiler_options?: AOTProfilerOptions, // dictionary-style Object. If omitted, aot profiler will not be initialized. + coverage_profiler_options?: CoverageProfilerOptions, // dictionary-style Object. If omitted, coverage profiler will not be initialized. + ignore_pdb_load_errors?: boolean +}; + +export type MonoConfigError = { message: string, error: any } + +// Types of assets that can be in the mono-config.js/mono-config.json file (taken from /src/tasks/WasmAppBuilder/WasmAppBuilder.cs) +export type AssetEntry = { + name: string, // the name of the asset, including extension. + behavior: AssetBehaviours, // determines how the asset will be handled once loaded + virtual_path?: string, // if specified, overrides the path of the asset in the virtual filesystem and similar data structures once loaded. + culture?: string, + load_remote?: boolean, // if true, an attempt will be made to load the asset from each location in @args.remote_sources. + is_optional?: boolean // if true, any failure to load this asset will be ignored. +} + +export interface AssemblyEntry extends AssetEntry { + name: "assembly" +} + +export interface SatelliteAssemblyEntry extends AssetEntry { + name: "resource", + culture: string +} + +export interface VfsEntry extends AssetEntry { + name: "vfs", + virtual_path: string +} + +export interface IcuData extends AssetEntry { + name: "icu", + load_remote: boolean +} + +// Note that since these are annoated as `declare const enum` they are replaces by tsc with their raw value during compilation +export const enum AssetBehaviours { + Resource = "resource", // load asset as a managed resource assembly + Assembly = "assembly", // load asset as a managed assembly (or debugging information) + Heap = "heap", // store asset into the native heap + ICU = "icu", // load asset as an ICU data archive + VFS = "vfs", // load asset into the virtual filesystem (for fopen, File.Open, etc) +} diff --git a/src/mono/wasm/runtime/src/runtime.ts b/src/mono/wasm/runtime/src/runtime.ts new file mode 100644 index 0000000000000..915a380e8f64f --- /dev/null +++ b/src/mono/wasm/runtime/src/runtime.ts @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +/// +/// +import { t_MONO, t_ModuleExtension } from './mono/types' + +export var Module: t_Module & t_ModuleExtension; +export var MONO: t_MONO; + +export function setMONO(mono: t_MONO, module: t_Module & any) { + Module = module; + MONO = mono; +} \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/startup.ts b/src/mono/wasm/runtime/src/startup.ts new file mode 100644 index 0000000000000..134b5d757675a --- /dev/null +++ b/src/mono/wasm/runtime/src/startup.ts @@ -0,0 +1,70 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +import { + mono_wasm_new_root, mono_wasm_new_roots, mono_wasm_release_roots, + mono_wasm_new_root_buffer, mono_wasm_new_root_buffer_from_pointer +} from './mono/roots' +import { + mono_wasm_add_dbg_command_received, + mono_wasm_send_dbg_command_with_parms, + mono_wasm_send_dbg_command, + mono_wasm_get_dbg_command_info, + mono_wasm_get_details, + mono_wasm_release_object, + mono_wasm_call_function_on, + mono_wasm_debugger_resume, + mono_wasm_detach_debugger, + mono_wasm_runtime_ready, + mono_wasm_get_loaded_files, + mono_wasm_raise_debug_event, + mono_wasm_fire_debugger_agent_message, +} from './mono/debug' +import { mono_wasm_load_bytes_into_heap } from './mono/roots' +import { StringDecoder } from './mono/string-decoder' +import { MONO, setMONO } from './runtime' +import { t_MONO, t_MonoSupportLib } from './mono/types' +import { + mono_load_runtime_and_bcl_args, mono_wasm_load_config, + mono_wasm_setenv, mono_wasm_set_runtime_options, + mono_wasm_load_data_archive, mono_wasm_asm_loaded +} from './mono/init' +import { prevent_timer_throttling, mono_set_timeout, schedule_background_exec } from './mono/scheduling' +import { mono_wasm_load_icu_data, mono_wasm_get_icudt_name } from './mono/icu' + +export function export_functions(mono: t_MONO, module: t_Module & t_MonoSupportLib) { + setMONO(mono, module) + MONO.string_decoder = new StringDecoder(); + MONO.mono_wasm_fire_debugger_agent_message = module.mono_wasm_fire_debugger_agent_message = mono_wasm_fire_debugger_agent_message; + MONO.mono_set_timeout = module.mono_set_timeout = mono_set_timeout; + MONO.mono_wasm_asm_loaded = module.mono_wasm_asm_loaded = mono_wasm_asm_loaded; + MONO.schedule_background_exec = module.schedule_background_exec = schedule_background_exec; + + MONO.mono_wasm_setenv = module.mono_wasm_setenv = mono_wasm_setenv; + MONO.mono_wasm_new_root_buffer = module.mono_wasm_new_root_buffer = mono_wasm_new_root_buffer; + MONO.mono_wasm_new_root_buffer_from_pointer = module.mono_wasm_new_root_buffer_from_pointer = mono_wasm_new_root_buffer_from_pointer; + MONO.mono_wasm_new_root = module.mono_wasm_new_root = mono_wasm_new_root; + MONO.mono_wasm_new_roots = module.mono_wasm_new_roots = mono_wasm_new_roots; + MONO.mono_wasm_release_roots = module.mono_wasm_release_roots = mono_wasm_release_roots; + MONO.mono_wasm_load_bytes_into_heap = module.mono_wasm_load_bytes_into_heap = mono_wasm_load_bytes_into_heap; + MONO.mono_wasm_get_loaded_files = module.mono_wasm_get_loaded_files = mono_wasm_get_loaded_files; + MONO.mono_wasm_load_icu_data = module.mono_wasm_load_icu_data = mono_wasm_load_icu_data; + MONO.mono_wasm_get_icudt_name = module.mono_wasm_get_icudt_name = mono_wasm_get_icudt_name; + + MONO.mono_wasm_set_runtime_options = mono_wasm_set_runtime_options; + MONO.mono_wasm_add_dbg_command_received = mono_wasm_add_dbg_command_received; + MONO.mono_wasm_send_dbg_command_with_parms = mono_wasm_send_dbg_command_with_parms; + MONO.mono_wasm_send_dbg_command = mono_wasm_send_dbg_command; + MONO.mono_wasm_get_dbg_command_info = mono_wasm_get_dbg_command_info; + MONO.mono_wasm_get_details = mono_wasm_get_details; + MONO.mono_wasm_release_object = mono_wasm_release_object; + MONO.mono_wasm_call_function_on = mono_wasm_call_function_on; + MONO.mono_wasm_debugger_resume = mono_wasm_debugger_resume; + MONO.mono_wasm_detach_debugger = mono_wasm_detach_debugger; + MONO.mono_wasm_runtime_ready = mono_wasm_runtime_ready; + MONO.mono_wasm_raise_debug_event = mono_wasm_raise_debug_event; + MONO.mono_load_runtime_and_bcl_args = mono_load_runtime_and_bcl_args; + MONO.prevent_timer_throttling = prevent_timer_throttling; + MONO.mono_wasm_load_config = mono_wasm_load_config; + MONO.mono_wasm_load_data_archive = mono_wasm_load_data_archive; +} diff --git a/src/mono/wasm/runtime/src/types/emscripten.d.ts b/src/mono/wasm/runtime/src/types/emscripten.d.ts new file mode 100644 index 0000000000000..0c30ec740af01 --- /dev/null +++ b/src/mono/wasm/runtime/src/types/emscripten.d.ts @@ -0,0 +1,56 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +declare interface ManagedPointer { } +declare interface NativePointer { } + +declare interface MonoString extends ManagedPointer { } +declare interface VoidPtr extends NativePointer { } +declare interface CharPtr extends NativePointer { } +declare interface Int32Ptr extends NativePointer { } +declare interface CharPtrPtr extends NativePointer { } + +declare var ENVIRONMENT_IS_WEB: boolean; +declare var ENVIRONMENT_IS_SHELL: boolean; +declare var ENVIRONMENT_IS_NODE: boolean; +declare var ENVIRONMENT_IS_WORKER: boolean; +declare var LibraryManager: any; + +declare function autoAddDeps(a: object, b: string): void; +declare function mergeInto(a: object, b: object): void; + +// TODO, what's wrong with EXPORTED_RUNTIME_METHODS ? +declare function locateFile(path: string, prefix?: string): string; + +declare var Module: t_Module; + +declare interface t_Module { + HEAP8: Int8Array, + HEAP16: Int16Array; + HEAP32: Int32Array; + HEAPU8: Uint8Array; + HEAPU16: Uint16Array; + HEAPU32: Uint32Array; + HEAPF32: Float32Array; + HEAPF64: Float64Array; + + // this should match emcc -s EXPORTED_FUNCTIONS + _malloc(amnt: number): number; + _free(amn: number): void; + + // this should match emcc -s EXPORTED_RUNTIME_METHODS + print(message: string): void; + ccall(ident: string, returnType?: string | null, argTypes?: string[], args?: any[], opts?: any): T; + cwrap(ident: string, returnType: string, argTypes?: string[], opts?: any): T; + cwrap(ident: string, ...args: any[]): T; + setValue(ptr: number, value: number, type: string, noSafe?: number | boolean): void; + getValue(ptr: number, type: string, noSafe?: number | boolean): number; + UTF8ToString(arg: CharPtr): string; + UTF8ArrayToString(str: TypedArray, heap: number[] | number, outIdx: number, maxBytesToWrite?: 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; + removeRunDependency(id: string): void; + addRunDependency(id: string): void; +} + +declare type TypedArray = Int8Array | Uint8Array | Uint8ClampedArray | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array; \ No newline at end of file diff --git a/src/mono/wasm/runtime/src/types/v8.d.ts b/src/mono/wasm/runtime/src/types/v8.d.ts new file mode 100644 index 0000000000000..9a59e88c408a7 --- /dev/null +++ b/src/mono/wasm/runtime/src/types/v8.d.ts @@ -0,0 +1,5 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +// read is a v8 debugger command +declare function read(name: string): string; \ No newline at end of file diff --git a/src/mono/wasm/runtime/tsconfig.json b/src/mono/wasm/runtime/tsconfig.json new file mode 100644 index 0000000000000..e7242a2ec478e --- /dev/null +++ b/src/mono/wasm/runtime/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "noEmitOnError": true, + "removeComments": false, + "sourceMap": false, + "target": "ES2015", + "moduleResolution": "Node", + "lib": [ + "esnext", + "dom" + ], + "strict": true, + "allowJs": true, + "outDir": "bin", + }, + "exclude": [ + "bin" + ] +} \ No newline at end of file diff --git a/src/mono/wasm/wasm.proj b/src/mono/wasm/wasm.proj index ec769911a2814..5e65625e0c7e9 100644 --- a/src/mono/wasm/wasm.proj +++ b/src/mono/wasm/wasm.proj @@ -66,8 +66,8 @@ <_EmccCommonFlags Include="-s ALLOW_MEMORY_GROWTH=1" /> <_EmccCommonFlags Include="-s NO_EXIT_RUNTIME=1" /> <_EmccCommonFlags Include="-s FORCE_FILESYSTEM=1" /> - <_EmccCommonFlags Include="-s "EXPORTED_RUNTIME_METHODS=['ccall', 'FS_createPath', 'FS_createDataFile', 'cwrap', 'setValue', 'getValue', 'UTF8ToString', 'UTF8ArrayToString', 'addFunction']"" /> - <_EmccCommonFlags Include="-s "EXPORTED_FUNCTIONS=['_putchar']"" /> + <_EmccCommonFlags Include="-s EXPORTED_RUNTIME_METHODS="['print','ccall','cwrap','setValue','getValue','UTF8ToString','UTF8ArrayToString','FS_createPath','FS_createDataFile','removeRunDependency','addRunDependency']"" /> + <_EmccCommonFlags Include="-s EXPORTED_FUNCTIONS="['_free','_malloc']"" /> <_EmccCommonFlags Include="--source-map-base http://example.com" /> <_EmccCommonFlags Include="-s STRICT_JS=1" /> @@ -164,6 +164,15 @@ DestinationFolder="$(MonoObjDir)" SkipUnchangedFiles="true" /> + + + + + + + + + Date: Sun, 26 Sep 2021 08:12:57 +0200 Subject: [PATCH 3/4] spaces instead of tabs, draft of lint config --- src/mono/wasm/runtime/.eslintrc.js | 38 + src/mono/wasm/runtime/package-lock.json | 1175 +++++++++++++++++++++++ src/mono/wasm/runtime/package.json | 6 +- src/mono/wasm/runtime/src/mono/init.ts | 790 +++++++-------- 4 files changed, 1613 insertions(+), 396 deletions(-) create mode 100644 src/mono/wasm/runtime/.eslintrc.js diff --git a/src/mono/wasm/runtime/.eslintrc.js b/src/mono/wasm/runtime/.eslintrc.js new file mode 100644 index 0000000000000..2a571451d5dc8 --- /dev/null +++ b/src/mono/wasm/runtime/.eslintrc.js @@ -0,0 +1,38 @@ +module.exports = { + 'env': { + 'browser': true, + 'es2021': true, + 'node': true + }, + 'extends': [ + 'eslint:recommended', + 'plugin:@typescript-eslint/recommended' + ], + 'parser': '@typescript-eslint/parser', + 'parserOptions': { + 'ecmaVersion': 12, + 'sourceType': 'module' + }, + 'plugins': [ + '@typescript-eslint' + ], + 'rules': { + 'indent': [ + 'error', + 4, + {SwitchCase: 1} + ], + 'linebreak-style': [ + 'error', + 'windows' + ], + 'quotes': [ + 'error', + 'single' + ], + 'semi': [ + 'error', + 'always' + ] + } +}; diff --git a/src/mono/wasm/runtime/package-lock.json b/src/mono/wasm/runtime/package-lock.json index 60dd9a6b6f885..aadc030a45f02 100644 --- a/src/mono/wasm/runtime/package-lock.json +++ b/src/mono/wasm/runtime/package-lock.json @@ -30,6 +30,66 @@ "js-tokens": "^4.0.0" } }, + "@eslint/eslintrc": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.1.1", + "espree": "^7.3.0", + "globals": "^13.9.0", + "ignore": "^4.0.6", + "import-fresh": "^3.2.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", + "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, "@rollup/plugin-typescript": { "version": "8.2.5", "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz", @@ -59,12 +119,147 @@ } } }, + "@types/json-schema": { + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", + "dev": true + }, "@types/node": { "version": "16.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.9.4.tgz", "integrity": "sha512-KDazLNYAGIuJugdbULwFZULF9qQ13yNWEBFnfVpqlpgAAo6H/qnM9RjBgh0A0kmHf3XxAKLdN5mTIng9iUvVLA==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.31.2.tgz", + "integrity": "sha512-w63SCQ4bIwWN/+3FxzpnWrDjQRXVEGiTt9tJTRptRXeFvdZc/wLiz3FQUwNQ2CVoRGI6KUWMNUj/pk63noUfcA==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "4.31.2", + "@typescript-eslint/scope-manager": "4.31.2", + "debug": "^4.3.1", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.1.0", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/experimental-utils": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz", + "integrity": "sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.7", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.31.2.tgz", + "integrity": "sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", + "debug": "^4.3.1" + } + }, + "@typescript-eslint/scope-manager": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz", + "integrity": "sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2" + } + }, + "@typescript-eslint/types": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.2.tgz", + "integrity": "sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz", + "integrity": "sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2", + "debug": "^4.3.1", + "globby": "^11.0.3", + "is-glob": "^4.0.1", + "semver": "^7.3.5", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz", + "integrity": "sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug==", + "dev": true, + "requires": { + "@typescript-eslint/types": "4.31.2", + "eslint-visitor-keys": "^2.0.0" + } + }, + "acorn": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", + "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, "ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -74,12 +269,64 @@ "color-convert": "^1.9.0" } }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -112,18 +359,381 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, + "eslint": { + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "dev": true, + "requires": { + "@babel/code-frame": "7.12.11", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^2.0.0", + "espree": "^7.3.1", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.1.2", + "globals": "^13.6.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^6.0.9", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.12.11", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", + "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + }, + "espree": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", + "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "dev": true, + "requires": { + "acorn": "^7.4.0", + "acorn-jsx": "^5.3.1", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", + "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, "estree-walker": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", "dev": true }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", + "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, "fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -137,6 +747,66 @@ "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", "dev": true }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", + "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.1.1", + "ignore": "^5.1.4", + "merge2": "^1.3.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + } + } + }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -152,6 +822,44 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, "is-core-module": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", @@ -161,6 +869,39 @@ "has": "^1.0.3" } }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, "jest-worker": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", @@ -195,24 +936,194 @@ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", + "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.2.3" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, "path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -222,6 +1133,18 @@ "safe-buffer": "^5.1.0" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, "resolve": { "version": "1.20.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", @@ -232,6 +1155,27 @@ "path-parse": "^1.0.6" } }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, "rollup": { "version": "2.56.3", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.3.tgz", @@ -253,12 +1197,30 @@ "terser": "^5.0.0" } }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "dev": true }, + "semver": { + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", + "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, "serialize-javascript": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", @@ -268,6 +1230,64 @@ "randombytes": "^2.1.0" } }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + } + } + }, "source-map": { "version": "0.7.3", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", @@ -292,6 +1312,38 @@ } } }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -301,6 +1353,40 @@ "has-flag": "^3.0.0" } }, + "table": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", + "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", + "dev": true, + "requires": { + "ajv": "^8.0.1", + "lodash.clonedeep": "^4.5.0", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, "terser": { "version": "5.8.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.8.0.tgz", @@ -312,17 +1398,106 @@ "source-map-support": "~0.5.20" } }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, "tslib": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==", "dev": true }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, "typescript": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "v8-compile-cache": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", + "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true } } } diff --git a/src/mono/wasm/runtime/package.json b/src/mono/wasm/runtime/package.json index 16199c17c40e8..dec085458d6d5 100644 --- a/src/mono/wasm/runtime/package.json +++ b/src/mono/wasm/runtime/package.json @@ -7,7 +7,8 @@ }, "version": "1.0.0", "scripts": { - "rollup": "rollup -c" + "rollup": "rollup -c", + "lint": "eslint src/**/*.ts" }, "keywords": [ "dotnet", @@ -18,6 +19,9 @@ "license": "MIT", "devDependencies": { "@rollup/plugin-typescript": "8.2.5", + "@typescript-eslint/eslint-plugin": "^4.31.2", + "@typescript-eslint/parser": "^4.31.2", + "eslint": "^7.32.0", "rollup": "2.56.3", "rollup-plugin-terser": "7.0.2", "tslib": "2.3.1", diff --git a/src/mono/wasm/runtime/src/mono/init.ts b/src/mono/wasm/runtime/src/mono/init.ts index 9ffe56dc11b0e..fd940352f13bd 100644 --- a/src/mono/wasm/runtime/src/mono/init.ts +++ b/src/mono/wasm/runtime/src/mono/init.ts @@ -12,89 +12,89 @@ import { mono_wasm_init_aot_profiler, mono_wasm_init_coverage_profiler } from '. // Set environment variable NAME to VALUE // Should be called before mono_load_runtime_and_bcl () in most cases export function mono_wasm_setenv(name: string, value: string) { - cwraps.mono_wasm_setenv(name, value); + cwraps.mono_wasm_setenv(name, value); } export function mono_wasm_set_runtime_options(options: string[]) { - var argv = Module._malloc(options.length * 4); - let aindex = 0; - for (var i = 0; i < options.length; ++i) { - Module.setValue(argv + (aindex * 4), cwraps.mono_wasm_strdup(options[i]), "i32"); - aindex += 1; - } - cwraps.mono_wasm_parse_runtime_options(options.length, argv); + var argv = Module._malloc(options.length * 4); + let aindex = 0; + for (var i = 0; i < options.length; ++i) { + Module.setValue(argv + (aindex * 4), cwraps.mono_wasm_strdup(options[i]), "i32"); + aindex += 1; + } + cwraps.mono_wasm_parse_runtime_options(options.length, argv); } function _handle_loaded_asset(ctx: MonoInitContext, asset: AssetEntry, url: string, blob: ArrayBuffer) { - var bytes = new Uint8Array(blob); - if (ctx.tracing) - console.log(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`); - - var virtualName: string = asset.virtual_path || asset.name; - var offset: VoidPtr | null = null; - - switch (asset.behavior) { - case "resource": - case "assembly": - ctx.loaded_files.push({ url: url, file: virtualName }); - case "heap": - case "icu": - offset = 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 (!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 = cwraps.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 (!mono_wasm_load_icu_data(offset!)) - console.error("Error loading ICU asset", asset.name); - } - else if (asset.behavior === "resource") { - cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length); - } + var bytes = new Uint8Array(blob); + if (ctx.tracing) + console.log(`MONO_WASM: Loaded:${asset.name} as ${asset.behavior} size ${bytes.length} from ${url}`); + + var virtualName: string = asset.virtual_path || asset.name; + var offset: VoidPtr | null = null; + + switch (asset.behavior) { + case "resource": + case "assembly": + ctx.loaded_files.push({ url: url, file: virtualName }); + case "heap": + case "icu": + offset = 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 (!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 = cwraps.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 (!mono_wasm_load_icu_data(offset!)) + console.error("Error loading ICU asset", asset.name); + } + else if (asset.behavior === "resource") { + cwraps.mono_wasm_add_satellite_assembly(virtualName, asset.culture!, offset!, bytes.length); + } } // Initializes the runtime and loads assemblies, debug information, and other files. @@ -137,291 +137,291 @@ function _handle_loaded_asset(ctx: MonoInitContext, asset: AssetEntry, url: stri // "auto" (default): if "icu" behavior assets are present, use ICU, otherwise invariant. // diagnostic_tracing: (optional) enables diagnostic log messages during startup export function mono_load_runtime_and_bcl_args(args: MonoConfig) { - try { - return _load_assets_and_runtime(args); - } catch (exc: any) { - console.error("error in mono_load_runtime_and_bcl_args:", exc.toString()); - throw exc; - } + try { + return _load_assets_and_runtime(args); + } catch (exc: any) { + console.error("error in mono_load_runtime_and_bcl_args:", exc.toString()); + throw exc; + } } function _apply_configuration_from_args(args: MonoConfig) { - for (var k in (args.environment_variables || {})) - mono_wasm_setenv(k, args.environment_variables![k]); + for (var k in (args.environment_variables || {})) + mono_wasm_setenv(k, args.environment_variables![k]); - if (args.runtime_options) - mono_wasm_set_runtime_options(args.runtime_options); + if (args.runtime_options) + mono_wasm_set_runtime_options(args.runtime_options); - if (args.aot_profiler_options) - mono_wasm_init_aot_profiler(args.aot_profiler_options); + if (args.aot_profiler_options) + mono_wasm_init_aot_profiler(args.aot_profiler_options); - if (args.coverage_profiler_options) - mono_wasm_init_coverage_profiler(args.coverage_profiler_options); + if (args.coverage_profiler_options) + mono_wasm_init_coverage_profiler(args.coverage_profiler_options); } function _get_fetch_file_cb_from_args(args: MonoConfig): (asset: string) => Promise { - if (typeof (args.fetch_file_cb) === "function") - return args.fetch_file_cb; - - if (ENVIRONMENT_IS_NODE) { - var fs = require('fs'); - return function (asset) { - console.debug("MONO_WASM: Loading... " + asset); - var binary = fs.readFileSync(asset); - var resolve_func2 = function (resolve: Function, reject: Function) { - resolve(new Uint8Array(binary)); - }; - - var resolve_func1 = function (resolve: Function, reject: Function) { - 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'."); - } + if (typeof (args.fetch_file_cb) === "function") + return args.fetch_file_cb; + + if (ENVIRONMENT_IS_NODE) { + var fs = require('fs'); + return function (asset) { + console.debug("MONO_WASM: Loading... " + asset); + var binary = fs.readFileSync(asset); + var resolve_func2 = function (resolve: Function, reject: Function) { + resolve(new Uint8Array(binary)); + }; + + var resolve_func1 = function (resolve: Function, reject: Function) { + 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'."); + } } function _finalize_startup(args: MonoConfig, ctx: MonoInitContext) { - var loaded_files_with_debug_info: string[] = []; - ctx.loaded_files.forEach(value => loaded_files_with_debug_info.push(value.url)); - MONO.loaded_assets = ctx.loaded_assets; - 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 = cwraps.mono_wasm_load_runtime; - - console.debug("MONO_WASM: Initializing mono runtime"); - - mono_wasm_globalization_init(args.globalization_mode!); - - if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { - try { - load_runtime("unused", args.debug_level || 0); - } catch (ex: any) { - Module.print("MONO_WASM: load_runtime () failed: " + ex); - Module.print("MONO_WASM: Stacktrace: \n"); - Module.print(ex.stack); - - var wasm_exit = cwraps.mono_wasm_exit; - wasm_exit(1); - } - } else { - load_runtime("unused", args.debug_level || 0); - } - - let tz; - try { - tz = Intl.DateTimeFormat().resolvedOptions().timeZone; - } catch { } - mono_wasm_setenv("TZ", tz || "UTC"); - mono_wasm_runtime_ready(); - args.loaded_cb(); + var loaded_files_with_debug_info: string[] = []; + ctx.loaded_files.forEach(value => loaded_files_with_debug_info.push(value.url)); + MONO.loaded_assets = ctx.loaded_assets; + 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 = cwraps.mono_wasm_load_runtime; + + console.debug("MONO_WASM: Initializing mono runtime"); + + mono_wasm_globalization_init(args.globalization_mode!); + + if (ENVIRONMENT_IS_SHELL || ENVIRONMENT_IS_NODE) { + try { + load_runtime("unused", args.debug_level || 0); + } catch (ex: any) { + Module.print("MONO_WASM: load_runtime () failed: " + ex); + Module.print("MONO_WASM: Stacktrace: \n"); + Module.print(ex.stack); + + var wasm_exit = cwraps.mono_wasm_exit; + wasm_exit(1); + } + } else { + load_runtime("unused", args.debug_level || 0); + } + + let tz; + try { + tz = Intl.DateTimeFormat().resolvedOptions().timeZone; + } catch { } + mono_wasm_setenv("TZ", tz || "UTC"); + mono_wasm_runtime_ready(); + args.loaded_cb(); } function _load_assets_and_runtime(args: MonoConfig) { - 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: MonoInitContext = { - tracing: args.diagnostic_tracing || false, - pending_count: args.assets.length, - 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)); - - _apply_configuration_from_args(args); - - var fetch_file_cb = _get_fetch_file_cb_from_args(args); - - var onPendingRequestComplete = function () { - --ctx.pending_count; - - if (ctx.pending_count === 0) { - try { - _finalize_startup(args, ctx); - } catch (exc) { - console.error("Unhandled exception in _finalize_startup", exc); - throw exc; - } - } - }; - - var processFetchResponseBuffer = function (asset: AssetEntry, url: string, buffer: ArrayBuffer) { - try { - _handle_loaded_asset(ctx, asset, url, buffer); - } catch (exc) { - console.error(`Unhandled exception in processFetchResponseBuffer ${url}`, exc); - throw exc; - } finally { - onPendingRequestComplete(); - } - }; - - args.assets.forEach(function (asset: AssetEntry) { - var sourceIndex = 0; - var sourcesList = asset.load_remote ? args.remote_sources! : [""]; - - var handleFetchResponse = function (response: Response) { - if (!response.ok) { - try { - attemptNextSource(); - return; - } catch (exc) { - console.error("MONO_WASM: Unhandled exception in handleFetchResponse attemptNextSource for asset", asset.name, exc); - throw exc; - } - } - - try { - var bufferPromise = response.arrayBuffer(); - bufferPromise.then((data) => processFetchResponseBuffer(asset, response.url, data)); - } catch (exc) { - console.error("MONO_WASM: Unhandled exception in handleFetchResponse for asset", asset.name, exc); - attemptNextSource(); - } - }; - - const 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$/) && args.ignore_pdb_load_errors); - - if (isOk) - console.debug(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 if (asset.behavior === "resource") { - var path = asset.culture !== '' ? `${asset.culture}/${asset.name}` : asset.name; - attemptUrl = locateFile(args.assembly_root + "/" + path); - } - else - attemptUrl = asset.name; - } else { - attemptUrl = sourcePrefix + asset.name; - } - - try { - if (asset.name === attemptUrl) { - if (ctx.tracing) - console.log("Attempting to fetch '" + attemptUrl + "'"); - } else { - if (ctx.tracing) - console.log("Attempting to fetch '" + attemptUrl + "' for", asset.name); - } - var fetch_promise = fetch_file_cb(attemptUrl); - fetch_promise.then(handleFetchResponse); - } catch (exc) { - console.error("MONO_WASM: Error fetching " + attemptUrl, exc); - attemptNextSource(); - } - }; - - attemptNextSource(); - }); + 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: MonoInitContext = { + tracing: args.diagnostic_tracing || false, + pending_count: args.assets.length, + 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)); + + _apply_configuration_from_args(args); + + var fetch_file_cb = _get_fetch_file_cb_from_args(args); + + var onPendingRequestComplete = function () { + --ctx.pending_count; + + if (ctx.pending_count === 0) { + try { + _finalize_startup(args, ctx); + } catch (exc) { + console.error("Unhandled exception in _finalize_startup", exc); + throw exc; + } + } + }; + + var processFetchResponseBuffer = function (asset: AssetEntry, url: string, buffer: ArrayBuffer) { + try { + _handle_loaded_asset(ctx, asset, url, buffer); + } catch (exc) { + console.error(`Unhandled exception in processFetchResponseBuffer ${url}`, exc); + throw exc; + } finally { + onPendingRequestComplete(); + } + }; + + args.assets.forEach(function (asset: AssetEntry) { + var sourceIndex = 0; + var sourcesList = asset.load_remote ? args.remote_sources! : [""]; + + var handleFetchResponse = function (response: Response) { + if (!response.ok) { + try { + attemptNextSource(); + return; + } catch (exc) { + console.error("MONO_WASM: Unhandled exception in handleFetchResponse attemptNextSource for asset", asset.name, exc); + throw exc; + } + } + + try { + var bufferPromise = response.arrayBuffer(); + bufferPromise.then((data) => processFetchResponseBuffer(asset, response.url, data)); + } catch (exc) { + console.error("MONO_WASM: Unhandled exception in handleFetchResponse for asset", asset.name, exc); + attemptNextSource(); + } + }; + + const 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$/) && args.ignore_pdb_load_errors); + + if (isOk) + console.debug(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 if (asset.behavior === "resource") { + var path = asset.culture !== '' ? `${asset.culture}/${asset.name}` : asset.name; + attemptUrl = locateFile(args.assembly_root + "/" + path); + } + else + attemptUrl = asset.name; + } else { + attemptUrl = sourcePrefix + asset.name; + } + + try { + if (asset.name === attemptUrl) { + if (ctx.tracing) + console.log("Attempting to fetch '" + attemptUrl + "'"); + } else { + if (ctx.tracing) + console.log("Attempting to fetch '" + attemptUrl + "' for", asset.name); + } + var fetch_promise = fetch_file_cb(attemptUrl); + fetch_promise.then(handleFetchResponse); + } catch (exc) { + console.error("MONO_WASM: Error fetching " + attemptUrl, exc); + attemptNextSource(); + } + }; + + attemptNextSource(); + }); } // used from ASP.NET export function mono_wasm_load_data_archive(data: TypedArray, prefix: string) { - 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 { - var 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 + 1); - folders.add(directory); - }); - folders.forEach(folder => { - Module['FS_createPath'](prefix, folder, true, true); - }); - - for (var 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; + 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 { + var 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 + 1); + folders.add(directory); + }); + folders.forEach(folder => { + Module['FS_createPath'](prefix, folder, true, true); + }); + + for (var 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; } /** @@ -432,67 +432,67 @@ export function mono_wasm_load_data_archive(data: TypedArray, prefix: string) { * @throws Will throw an error if the config file loading fails */ export async function mono_wasm_load_config(configFilePath: string) { - Module.addRunDependency(configFilePath); - try { - let config = null; - // NOTE: when we add nodejs make sure to include the nodejs fetch package - if (ENVIRONMENT_IS_WEB) { - const configRaw = await fetch(configFilePath); - config = await configRaw.json(); - } else if (ENVIRONMENT_IS_NODE) { - config = require(configFilePath); - } else { // shell or worker - config = JSON.parse(read(configFilePath)); // read is a v8 debugger command - } - MONO.config = config; - Module.config = MONO.config; - } catch (e) { - const errMessage = "failed to load config file " + configFilePath; - console.error(errMessage) - MONO.config = { message: errMessage, error: e }; - Module.config = MONO.config; - } finally { - Module.removeRunDependency(configFilePath); - } + Module.addRunDependency(configFilePath); + try { + let config = null; + // NOTE: when we add nodejs make sure to include the nodejs fetch package + if (ENVIRONMENT_IS_WEB) { + const configRaw = await fetch(configFilePath); + config = await configRaw.json(); + } else if (ENVIRONMENT_IS_NODE) { + config = require(configFilePath); + } else { // shell or worker + config = JSON.parse(read(configFilePath)); // read is a v8 debugger command + } + MONO.config = config; + Module.config = MONO.config; + } catch (e) { + const errMessage = "failed to load config file " + configFilePath; + console.error(errMessage) + MONO.config = { message: errMessage, error: e }; + Module.config = MONO.config; + } finally { + Module.removeRunDependency(configFilePath); + } } export function mono_wasm_asm_loaded(assembly_name: number, assembly_ptr: number, assembly_len: number, pdb_ptr: number, pdb_len: number) { - // Only trigger this codepath for assemblies loaded after app is ready - if (MONO.mono_wasm_runtime_is_ready !== true) - return; - - const assembly_name_str = assembly_name !== 0 ? Module.UTF8ToString(assembly_name).concat('.dll') : ''; - const assembly_data = new Uint8Array(Module.HEAPU8.buffer, assembly_ptr, assembly_len); - const assembly_b64 = toBase64StringImpl(assembly_data); - - let pdb_b64; - if (pdb_ptr) { - const pdb_data = new Uint8Array(Module.HEAPU8.buffer, pdb_ptr, pdb_len); - pdb_b64 = toBase64StringImpl(pdb_data); - } - - MONO.mono_wasm_raise_debug_event({ - eventName: 'AssemblyLoaded', - assembly_name: assembly_name_str, - assembly_b64, - pdb_b64 - }); + // Only trigger this codepath for assemblies loaded after app is ready + if (MONO.mono_wasm_runtime_is_ready !== true) + return; + + const assembly_name_str = assembly_name !== 0 ? Module.UTF8ToString(assembly_name).concat('.dll') : ''; + const assembly_data = new Uint8Array(Module.HEAPU8.buffer, assembly_ptr, assembly_len); + const assembly_b64 = toBase64StringImpl(assembly_data); + + let pdb_b64; + if (pdb_ptr) { + const pdb_data = new Uint8Array(Module.HEAPU8.buffer, pdb_ptr, pdb_len); + pdb_b64 = toBase64StringImpl(pdb_data); + } + + MONO.mono_wasm_raise_debug_event({ + eventName: 'AssemblyLoaded', + assembly_name: assembly_name_str, + assembly_b64, + pdb_b64 + }); } // @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. export function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr { - var memoryOffset = Module._malloc(bytes.length); - var heapBytes = new Uint8Array(Module.HEAPU8.buffer, memoryOffset, bytes.length); - heapBytes.set(bytes); - return memoryOffset; + var memoryOffset = Module._malloc(bytes.length); + var heapBytes = new Uint8Array(Module.HEAPU8.buffer, memoryOffset, bytes.length); + heapBytes.set(bytes); + return memoryOffset; } type MonoInitContext = { - tracing: boolean, - pending_count: number, - loaded_files: { url: string, file: string }[], - loaded_assets: { [id: string]: [VoidPtr, number] }, - createPath: Function, - createDataFile: Function + tracing: boolean, + pending_count: number, + loaded_files: { url: string, file: string }[], + loaded_assets: { [id: string]: [VoidPtr, number] }, + createPath: Function, + createDataFile: Function } \ No newline at end of file From cfc60b543e747c62b82360ce84b2df0d0e8717cf Mon Sep 17 00:00:00 2001 From: Pavel Savara Date: Sun, 26 Sep 2021 08:32:01 +0200 Subject: [PATCH 4/4] linker feedback --- src/mono/wasm/build/WasmApp.Native.targets | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets index 5a08fee4e2839..e4bb5ffb27836 100644 --- a/src/mono/wasm/build/WasmApp.Native.targets +++ b/src/mono/wasm/build/WasmApp.Native.targets @@ -218,7 +218,7 @@ <_WasmRuntimePackSrcFile ObjectFile="$(_WasmIntermediateOutputPath)%(FileName).o" /> <_DotnetJSSrcFile Include="$(_WasmRuntimePackSrcDir)\*.js" Exclude="$(_WasmRuntimePackSrcDir)\*.iffe.js"/> - <_DotnetJSPreFile Include="$(_WasmRuntimePackSrcDir)\*.iffe.js"/> + <_WasmExtraJSFile Include="$(_WasmRuntimePackSrcDir)\*.iffe.js" Kind="pre-js" /> <_WasmNativeFileForLinking Include="@(NativeFileReference)" /> @@ -338,9 +338,7 @@ <_EmccLinkStepArgs Include="@(_EmccLDFlags)" /> <_EmccLinkStepArgs Include="--js-library "%(_DotnetJSSrcFile.Identity)"" /> - <_EmccLinkStepArgs Include="--pre-js "%(_DotnetJSPreFile.Identity)"" /> <_WasmLinkDependencies Include="@(_DotnetJSSrcFile)" /> - <_WasmLinkDependencies Include="@(_DotnetJSPreFile)" /> <_EmccLinkStepArgs Include="--js-library "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'js-library'" /> <_EmccLinkStepArgs Include="--pre-js "%(_WasmExtraJSFile.Identity)"" Condition="'%(_WasmExtraJSFile.Kind)' == 'pre-js'" />