Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/dotnet/runtime.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Savara <pavel.savara@gmail.com>2022-05-20 21:49:54 +0300
committerGitHub <noreply@github.com>2022-05-20 21:49:54 +0300
commite7886b24ba2ca7c9eff86a842ad3d97dd30380e4 (patch)
tree6e5e8c392205842f6b0d92f2eb31bce0be8a5150 /src/mono/wasm/runtime
parent810a7f9cd78d6611c0473940cb733231f3eabc06 (diff)
[wasm] improve memory access and marshaling range checks (#64845)
* new memory accessors setI52, setI64Big, getI52, getI64Big * removed support for long form automatic marshaler. It has impact to mono_bind_static_method * made assert silent on Release config * test for not marshaling long from C# to JS * negative test for marshaling NaN as long * fixed marshaling of uint32, uint16 and byte to be unsigned * implemented range check on all set memory operations * implemented also uint52 * differentiated bool marshaling because it shoud have different validation and message * inlined asserts * rename assert to mono_assert
Diffstat (limited to 'src/mono/wasm/runtime')
-rw-r--r--src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js3
-rw-r--r--src/mono/wasm/runtime/cwraps.ts4
-rw-r--r--src/mono/wasm/runtime/dotnet.d.ts34
-rw-r--r--src/mono/wasm/runtime/driver.c4
-rw-r--r--src/mono/wasm/runtime/es6/dotnet.es6.lib.js3
-rw-r--r--src/mono/wasm/runtime/exports.ts23
-rw-r--r--src/mono/wasm/runtime/memory.ts116
-rw-r--r--src/mono/wasm/runtime/method-binding.ts26
-rw-r--r--src/mono/wasm/runtime/rollup.config.js62
-rw-r--r--src/mono/wasm/runtime/startup.ts18
-rw-r--r--src/mono/wasm/runtime/types.ts9
11 files changed, 254 insertions, 48 deletions
diff --git a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
index de07811bdd6..79116860e1c 100644
--- a/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
+++ b/src/mono/wasm/runtime/cjs/dotnet.cjs.lib.js
@@ -10,11 +10,12 @@ const DotnetSupportLib = {
// we replace implementation of readAsync and fetch
// replacement of require is there for consistency with ES6 code
$DOTNET__postset: `
-let __dotnet_replacements = {readAsync, fetch: globalThis.fetch, require};
+let __dotnet_replacements = {readAsync, fetch: globalThis.fetch, require, updateGlobalBufferAndViews};
let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
{ isESM:false, isGlobal:ENVIRONMENT_IS_GLOBAL, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile, quit_, ExitStatus, requirePromise:Promise.resolve(require)},
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module },
__dotnet_replacements);
+updateGlobalBufferAndViews = __dotnet_replacements.updateGlobalBufferAndViews;
readAsync = __dotnet_replacements.readAsync;
var fetch = __dotnet_replacements.fetch;
require = __dotnet_replacements.requireOut;
diff --git a/src/mono/wasm/runtime/cwraps.ts b/src/mono/wasm/runtime/cwraps.ts
index 52663f7f39a..0c205b47e49 100644
--- a/src/mono/wasm/runtime/cwraps.ts
+++ b/src/mono/wasm/runtime/cwraps.ts
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
import {
- assert,
+ mono_assert,
MonoArray, MonoAssembly, MonoClass,
MonoMethod, MonoObject, MonoString,
MonoType, MonoObjectRef, MonoStringRef
@@ -197,7 +197,7 @@ export default wrapped_c_functions;
export function wrap_c_function(name: string): Function {
const wf: any = wrapped_c_functions;
const sig = fn_signatures.find(s => s[0] === name);
- assert(sig, () => `Function ${name} not found`);
+ mono_assert(sig, () => `Function ${name} not found`);
const fce = Module.cwrap(sig[0], sig[1], sig[2], sig[3]);
wf[sig[0]] = fce;
return fce;
diff --git a/src/mono/wasm/runtime/dotnet.d.ts b/src/mono/wasm/runtime/dotnet.d.ts
index 07c1db8ac7c..3c4c491a340 100644
--- a/src/mono/wasm/runtime/dotnet.d.ts
+++ b/src/mono/wasm/runtime/dotnet.d.ts
@@ -303,22 +303,40 @@ declare function mono_wasm_load_bytes_into_heap(bytes: Uint8Array): VoidPtr;
declare type _MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
declare type _NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;
+declare function setB32(offset: _MemOffset, value: number | boolean): void;
declare function setU8(offset: _MemOffset, value: number): void;
declare function setU16(offset: _MemOffset, value: number): void;
declare function setU32(offset: _MemOffset, value: _NumberOrPointer): void;
declare function setI8(offset: _MemOffset, value: number): void;
declare function setI16(offset: _MemOffset, value: number): void;
-declare function setI32(offset: _MemOffset, value: _NumberOrPointer): void;
-declare function setI64(offset: _MemOffset, value: number): void;
+declare function setI32(offset: _MemOffset, value: number): void;
+/**
+ * Throws for values which are not 52 bit integer. See Number.isSafeInteger()
+ */
+declare function setI52(offset: _MemOffset, value: number): void;
+/**
+ * Throws for values which are not 52 bit integer or are negative. See Number.isSafeInteger().
+ */
+declare function setU52(offset: _MemOffset, value: number): void;
+declare function setI64Big(offset: _MemOffset, value: bigint): void;
declare function setF32(offset: _MemOffset, value: number): void;
declare function setF64(offset: _MemOffset, value: number): void;
+declare function getB32(offset: _MemOffset): boolean;
declare function getU8(offset: _MemOffset): number;
declare function getU16(offset: _MemOffset): number;
declare function getU32(offset: _MemOffset): number;
declare function getI8(offset: _MemOffset): number;
declare function getI16(offset: _MemOffset): number;
declare function getI32(offset: _MemOffset): number;
-declare function getI64(offset: _MemOffset): number;
+/**
+ * Throws for Number.MIN_SAFE_INTEGER > value > Number.MAX_SAFE_INTEGER
+ */
+declare function getI52(offset: _MemOffset): number;
+/**
+ * Throws for Number.MIN_SAFE_INTEGER > value > Number.MAX_SAFE_INTEGER
+ */
+declare function getU52(offset: _MemOffset): number;
+declare function getI64Big(offset: _MemOffset): bigint;
declare function getF32(offset: _MemOffset): number;
declare function getF64(offset: _MemOffset): number;
@@ -343,19 +361,25 @@ declare const MONO: {
mono_wasm_load_runtime: (unused: string, debug_level: number) => void;
config: MonoConfig | MonoConfigError;
loaded_files: string[];
+ setB32: typeof setB32;
setI8: typeof setI8;
setI16: typeof setI16;
setI32: typeof setI32;
- setI64: typeof setI64;
+ setI52: typeof setI52;
+ setU52: typeof setU52;
+ setI64Big: typeof setI64Big;
setU8: typeof setU8;
setU16: typeof setU16;
setU32: typeof setU32;
setF32: typeof setF32;
setF64: typeof setF64;
+ getB32: typeof getB32;
getI8: typeof getI8;
getI16: typeof getI16;
getI32: typeof getI32;
- getI64: typeof getI64;
+ getI52: typeof getI52;
+ getU52: typeof getU52;
+ getI64Big: typeof getI64Big;
getU8: typeof getU8;
getU16: typeof getU16;
getU32: typeof getU32;
diff --git a/src/mono/wasm/runtime/driver.c b/src/mono/wasm/runtime/driver.c
index 3a5977ee2c9..596e4f05f3c 100644
--- a/src/mono/wasm/runtime/driver.c
+++ b/src/mono/wasm/runtime/driver.c
@@ -875,13 +875,13 @@ _marshal_type_from_mono_type (int mono_type, MonoClass *klass, MonoType *type)
case MONO_TYPE_PTR:
return MARSHAL_TYPE_POINTER;
case MONO_TYPE_I1:
- case MONO_TYPE_U1:
case MONO_TYPE_I2:
- case MONO_TYPE_U2:
case MONO_TYPE_I4:
return MARSHAL_TYPE_INT;
case MONO_TYPE_CHAR:
return MARSHAL_TYPE_CHAR;
+ case MONO_TYPE_U1:
+ case MONO_TYPE_U2:
case MONO_TYPE_U4: // The distinction between this and signed int is
// important due to how numbers work in JavaScript
return MARSHAL_TYPE_UINT32;
diff --git a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
index a110268beeb..f886800ab50 100644
--- a/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
+++ b/src/mono/wasm/runtime/es6/dotnet.es6.lib.js
@@ -14,7 +14,7 @@ const DotnetSupportLib = {
// Emscripten's getBinaryPromise is not async for NodeJs, but we would like to have it async, so we replace it.
// We also replace implementation of readAsync and fetch
$DOTNET__postset: `
-let __dotnet_replacements = {readAsync, fetch: globalThis.fetch, require};
+let __dotnet_replacements = {readAsync, fetch: globalThis.fetch, require, updateGlobalBufferAndViews};
if (ENVIRONMENT_IS_NODE) {
__dotnet_replacements.requirePromise = import('module').then(mod => {
const require = mod.createRequire(import.meta.url);
@@ -52,6 +52,7 @@ let __dotnet_exportedAPI = __dotnet_runtime.__initializeImportsAndExports(
{ isESM:true, isGlobal:false, isNode:ENVIRONMENT_IS_NODE, isShell:ENVIRONMENT_IS_SHELL, isWeb:ENVIRONMENT_IS_WEB, locateFile, quit_, ExitStatus, requirePromise:__dotnet_replacements.requirePromise },
{ mono:MONO, binding:BINDING, internal:INTERNAL, module:Module },
__dotnet_replacements);
+updateGlobalBufferAndViews = __dotnet_replacements.updateGlobalBufferAndViews;
readAsync = __dotnet_replacements.readAsync;
var fetch = __dotnet_replacements.fetch;
require = __dotnet_replacements.requireOut;
diff --git a/src/mono/wasm/runtime/exports.ts b/src/mono/wasm/runtime/exports.ts
index e370c2e97c3..ae7efdfab33 100644
--- a/src/mono/wasm/runtime/exports.ts
+++ b/src/mono/wasm/runtime/exports.ts
@@ -58,10 +58,10 @@ import { mono_wasm_release_cs_owned_object } from "./gc-handles";
import { mono_wasm_web_socket_open_ref, mono_wasm_web_socket_send, mono_wasm_web_socket_receive, mono_wasm_web_socket_close_ref, mono_wasm_web_socket_abort } from "./web-socket";
import cwraps from "./cwraps";
import {
- setI8, setI16, setI32, setI64,
+ setI8, setI16, setI32, setI52,
setU8, setU16, setU32, setF32, setF64,
- getI8, getI16, getI32, getI64,
- getU8, getU16, getU32, getF32, getF64,
+ getI8, getI16, getI32, getI52,
+ getU8, getU16, getU32, getF32, getF64, afterUpdateGlobalBufferAndViews, getI64Big, setI64Big, getU52, setU52, setB32, getB32,
} from "./memory";
import { create_weak_ref } from "./weak-ref";
import { fetch_like, readAsync_like } from "./polyfills";
@@ -93,19 +93,25 @@ const MONO = {
loaded_files: <string[]>[],
// memory accessors
+ setB32,
setI8,
setI16,
setI32,
- setI64,
+ setI52,
+ setU52,
+ setI64Big,
setU8,
setU16,
setU32,
setF32,
setF64,
+ getB32,
getI8,
getI16,
getI32,
- getI64,
+ getI52,
+ getU52,
+ getI64Big,
getU8,
getU16,
getU32,
@@ -179,7 +185,7 @@ let exportedAPI: DotnetPublicAPI;
function initializeImportsAndExports(
imports: { isESM: boolean, isGlobal: boolean, isNode: boolean, isShell: boolean, isWeb: boolean, locateFile: Function, quit_: Function, ExitStatus: ExitStatusError, requirePromise: Promise<Function> },
exports: { mono: any, binding: any, internal: any, module: any },
- replacements: { fetch: any, readAsync: any, require: any, requireOut: any, noExitRuntime: boolean },
+ replacements: { fetch: any, readAsync: any, require: any, requireOut: any, noExitRuntime: boolean, updateGlobalBufferAndViews: Function },
): DotnetPublicAPI {
const module = exports.module as DotnetModule;
const globalThisAny = globalThis as any;
@@ -236,6 +242,11 @@ function initializeImportsAndExports(
replacements.fetch = runtimeHelpers.fetch;
replacements.readAsync = readAsync_like;
replacements.requireOut = module.imports.require;
+ const originalUpdateGlobalBufferAndViews = replacements.updateGlobalBufferAndViews;
+ replacements.updateGlobalBufferAndViews = (buffer: Buffer) => {
+ originalUpdateGlobalBufferAndViews(buffer);
+ afterUpdateGlobalBufferAndViews(buffer);
+ };
replacements.noExitRuntime = ENVIRONMENT_IS_WEB;
diff --git a/src/mono/wasm/runtime/memory.ts b/src/mono/wasm/runtime/memory.ts
index e524bb48046..8e43d133e47 100644
--- a/src/mono/wasm/runtime/memory.ts
+++ b/src/mono/wasm/runtime/memory.ts
@@ -1,10 +1,12 @@
import { Module } from "./imports";
+import { mono_assert } from "./types";
import { VoidPtr, NativePointer, ManagedPointer } from "./types/emscripten";
import * as cuint64 from "./cuint64";
const alloca_stack: Array<VoidPtr> = [];
const alloca_buffer_size = 32 * 1024;
let alloca_base: VoidPtr, alloca_offset: VoidPtr, alloca_limit: VoidPtr;
+let HEAPI64: BigInt64Array = <any>null;
function _ensure_allocated(): void {
if (alloca_base)
@@ -14,6 +16,8 @@ function _ensure_allocated(): void {
alloca_limit = <VoidPtr>(<any>alloca_base + alloca_buffer_size);
}
+const is_bingint_supported = typeof BigInt !== "undefined" && typeof BigInt64Array !== "undefined";
+
export function temp_malloc(size: number): VoidPtr {
_ensure_allocated();
if (!alloca_stack.length)
@@ -41,33 +45,86 @@ export function _release_temp_frame(): void {
type _MemOffset = number | VoidPtr | NativePointer | ManagedPointer;
type _NumberOrPointer = number | VoidPtr | NativePointer | ManagedPointer;
+function is_int_in_range(value: Number, min: Number, max: Number) {
+ mono_assert(typeof value === "number", () => `Value is not integer but ${typeof value}`);
+ mono_assert(Number.isInteger(value), "Value is not integer but float");
+ mono_assert(value >= min && value <= max, () => `Overflow: value ${value} is out of ${min} ${max} range`);
+}
+
+export function setB32(offset: _MemOffset, value: number | boolean): void {
+ mono_assert(typeof value === "boolean", () => `Value is not boolean but ${typeof value}`);
+ Module.HEAP32[<any>offset >>> 2] = <any>!!value;
+}
+
export function setU8(offset: _MemOffset, value: number): void {
+ is_int_in_range(value, 0, 0xFF);
Module.HEAPU8[<any>offset] = value;
}
export function setU16(offset: _MemOffset, value: number): void {
+ is_int_in_range(value, 0, 0xFFFF);
Module.HEAPU16[<any>offset >>> 1] = value;
}
export function setU32(offset: _MemOffset, value: _NumberOrPointer): void {
+ is_int_in_range(<any>value, 0, 0xFFFF_FFFF);
Module.HEAPU32[<any>offset >>> 2] = <number><any>value;
}
export function setI8(offset: _MemOffset, value: number): void {
+ is_int_in_range(value, -0x80, 0x7F);
Module.HEAP8[<any>offset] = value;
}
export function setI16(offset: _MemOffset, value: number): void {
+ is_int_in_range(value, -0x8000, 0x7FFF);
Module.HEAP16[<any>offset >>> 1] = value;
}
-export function setI32(offset: _MemOffset, value: _NumberOrPointer): void {
- Module.HEAP32[<any>offset >>> 2] = <number><any>value;
+export function setI32(offset: _MemOffset, value: number): void {
+ is_int_in_range(<any>value, -0x8000_0000, 0x7FFF_FFFF);
+ Module.HEAP32[<any>offset >>> 2] = value;
+}
+
+/**
+ * Throws for values which are not 52 bit integer. See Number.isSafeInteger()
+ */
+export function setI52(offset: _MemOffset, value: number): void {
+ // 52 bits = 0x1F_FFFF_FFFF_FFFF
+ mono_assert(!Number.isNaN(value), "Can't convert Number.Nan into Int64");
+ mono_assert(Number.isSafeInteger(value), "Overflow: value out of Number.isSafeInteger range");
+ let hi: number;
+ let lo: number;
+ if (value < 0) {
+ value = -1 - value;
+ hi = 0x8000_0000 + ((value >>> 32) ^ 0x001F_FFFF);
+ lo = (value & 0xFFFF_FFFF) ^ 0xFFFF_FFFF;
+ }
+ else {
+ hi = value >>> 32;
+ lo = value & 0xFFFF_FFFF;
+ }
+ Module.HEAPU32[1 + <any>offset >>> 2] = hi;
+ Module.HEAPU32[<any>offset >>> 2] = lo;
}
-// NOTE: Accepts a number, not a BigInt, so values over Number.MAX_SAFE_INTEGER will be corrupted
-export function setI64(offset: _MemOffset, value: number): void {
- Module.setValue(<VoidPtr><any>offset, value, "i64");
+/**
+ * Throws for values which are not 52 bit integer or are negative. See Number.isSafeInteger().
+ */
+export function setU52(offset: _MemOffset, value: number): void {
+ // 52 bits = 0x1F_FFFF_FFFF_FFFF
+ mono_assert(!Number.isNaN(value), "Can't convert Number.Nan into UInt64");
+ mono_assert(Number.isSafeInteger(value), "Overflow: value out of Number.isSafeInteger range");
+ mono_assert(value >= 0, "Can't convert negative Number into UInt64");
+ const hi = value >>> 32;
+ const lo = value & 0xFFFF_FFFF;
+ Module.HEAPU32[1 + <any>offset >>> 2] = hi;
+ Module.HEAPU32[<any>offset >>> 2] = lo;
+}
+
+export function setI64Big(offset: _MemOffset, value: bigint): void {
+ mono_assert(is_bingint_supported, "BigInt is not supported.");
+ HEAPI64[<any>offset >>> 3] = value;
}
export function setF32(offset: _MemOffset, value: number): void {
@@ -79,6 +136,10 @@ export function setF64(offset: _MemOffset, value: number): void {
}
+export function getB32(offset: _MemOffset): boolean {
+ return !!(Module.HEAP32[<any>offset >>> 2]);
+}
+
export function getU8(offset: _MemOffset): number {
return Module.HEAPU8[<any>offset];
}
@@ -103,9 +164,42 @@ export function getI32(offset: _MemOffset): number {
return Module.HEAP32[<any>offset >>> 2];
}
-// NOTE: Returns a number, not a BigInt. This means values over Number.MAX_SAFE_INTEGER will be corrupted
-export function getI64(offset: _MemOffset): number {
- return Module.getValue(<number><any>offset, "i64");
+/**
+ * Throws for Number.MIN_SAFE_INTEGER > value > Number.MAX_SAFE_INTEGER
+ */
+export function getI52(offset: _MemOffset): number {
+ // 52 bits = 0x1F_FFFF_FFFF_FFFF
+ const hi = Module.HEAPU32[1 + (<any>offset >>> 2)];
+ const lo = Module.HEAPU32[<any>offset >>> 2];
+ const sign = hi & 0x8000_0000;
+ const exp = hi & 0x7FE0_0000;
+ if (sign) {
+ mono_assert(exp === 0x7FE0_0000, "Overflow: value out of Number.isSafeInteger range");
+ const nhi = (hi & 0x000F_FFFF) ^ 0x000F_FFFF;
+ const nlo = lo ^ 0xFFFF_FFFF;
+ return -1 - ((nhi * 0x1_0000_0000) + nlo);
+ }
+ else {
+ mono_assert(exp === 0, "Overflow: value out of Number.isSafeInteger range");
+ return (hi * 0x1_0000_0000) + lo;
+ }
+}
+
+/**
+ * Throws for Number.MIN_SAFE_INTEGER > value > Number.MAX_SAFE_INTEGER
+ */
+export function getU52(offset: _MemOffset): number {
+ // 52 bits = 0x1F_FFFF_FFFF_FFFF
+ const hi = Module.HEAPU32[1 + (<any>offset >>> 2)];
+ const lo = Module.HEAPU32[<any>offset >>> 2];
+ const exp_sign = hi & 0xFFE0_0000;
+ mono_assert(exp_sign === 0, "Overflow: value out of Number.isSafeInteger range");
+ return (hi * 0x1_0000_0000) + lo;
+}
+
+export function getI64Big(offset: _MemOffset): bigint {
+ mono_assert(is_bingint_supported, "BigInt is not supported.");
+ return HEAPI64[<any>offset >>> 3];
}
export function getF32(offset: _MemOffset): number {
@@ -116,6 +210,12 @@ export function getF64(offset: _MemOffset): number {
return Module.HEAPF64[<any>offset >>> 3];
}
+export function afterUpdateGlobalBufferAndViews(buffer: Buffer): void {
+ if (is_bingint_supported) {
+ HEAPI64 = new BigInt64Array(buffer);
+ }
+}
+
export function getCU64(offset: _MemOffset): cuint64.CUInt64 {
const lo = getU32(offset);
const hi = getU32(<any>offset + 4);
diff --git a/src/mono/wasm/runtime/method-binding.ts b/src/mono/wasm/runtime/method-binding.ts
index 6dc3daec9f7..d5cea1c6f0d 100644
--- a/src/mono/wasm/runtime/method-binding.ts
+++ b/src/mono/wasm/runtime/method-binding.ts
@@ -10,7 +10,7 @@ import { _unbox_mono_obj_root_with_known_nonprimitive_type } from "./cs-to-js";
import {
_create_temp_frame,
getI32, getU32, getF32, getF64,
- setI32, setU32, setF32, setF64, setI64,
+ setI32, setU32, setF32, setF64, setI52, setU52, setB32, getB32
} from "./memory";
import {
_get_args_root_buffer_for_method_call, _get_buffer_for_method_call,
@@ -130,8 +130,11 @@ export function _create_primitive_converters(): void {
// result.set ('k', { steps: [{ convert: js_to_mono_enum.bind (this), indirect: 'i64'}], size: 8});
result.set("j", { steps: [{ convert: js_to_mono_enum.bind(BINDING), indirect: "i32" }], size: 8 });
+ result.set("b", { steps: [{ indirect: "bool" }], size: 8 });
result.set("i", { steps: [{ indirect: "i32" }], size: 8 });
- result.set("l", { steps: [{ indirect: "i64" }], size: 8 });
+ result.set("I", { steps: [{ indirect: "u32" }], size: 8 });
+ result.set("l", { steps: [{ indirect: "i52" }], size: 8 });
+ result.set("L", { steps: [{ indirect: "u52" }], size: 8 });
result.set("f", { steps: [{ indirect: "float" }], size: 8 });
result.set("d", { steps: [{ indirect: "double" }], size: 8 });
}
@@ -218,7 +221,9 @@ export function _compile_converter_for_marshal_string(args_marshal: string/*Args
setU32,
setF32,
setF64,
- setI64,
+ setU52,
+ setI52,
+ setB32,
scratchValueRoot: converter.scratchValueRoot
};
let indirectLocalOffset = 0;
@@ -276,6 +281,9 @@ export function _compile_converter_for_marshal_string(args_marshal: string/*Args
const offsetText = `(indirectStart + ${indirectLocalOffset})`;
switch (step.indirect) {
+ case "bool":
+ body.push(`setB32(${offsetText}, ${valueKey});`);
+ break;
case "u32":
body.push(`setU32(${offsetText}, ${valueKey});`);
break;
@@ -288,8 +296,11 @@ export function _compile_converter_for_marshal_string(args_marshal: string/*Args
case "double":
body.push(`setF64(${offsetText}, ${valueKey});`);
break;
- case "i64":
- body.push(`setI64(${offsetText}, ${valueKey});`);
+ case "i52":
+ body.push(`setI52(${offsetText}, ${valueKey});`);
+ break;
+ case "u52":
+ body.push(`setU52(${offsetText}, ${valueKey});`);
break;
default:
throw new Error("Unimplemented indirect type: " + step.indirect);
@@ -421,6 +432,7 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha
token,
unbox_buffer,
unbox_buffer_size,
+ getB32,
getI32,
getU32,
getF32,
@@ -522,7 +534,7 @@ export function mono_bind_method(method: MonoMethod, this_arg: null, args_marsha
` case ${MarshalType.FP64}:`,
" result = getF64(unbox_buffer); break;",
` case ${MarshalType.BOOL}:`,
- " result = getI32(unbox_buffer) !== 0; break;",
+ " result = getB32(unbox_buffer); break;",
` case ${MarshalType.CHAR}:`,
" result = String.fromCharCode(getI32(unbox_buffer)); break;",
` case ${MarshalType.NULL}:`,
@@ -584,7 +596,7 @@ export type ArgsMarshalString = ""
| `${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${ArgsMarshal}${_ExtraArgsMarshalOperators}`;
*/
-type ConverterStepIndirects = "u32" | "i32" | "float" | "double" | "i64" | "reference"
+type ConverterStepIndirects = "u32" | "i32" | "float" | "double" | "u52" | "i52" | "reference" | "bool"
export type Converter = {
steps: {
diff --git a/src/mono/wasm/runtime/rollup.config.js b/src/mono/wasm/runtime/rollup.config.js
index 7225a2c954e..f37712aa155 100644
--- a/src/mono/wasm/runtime/rollup.config.js
+++ b/src/mono/wasm/runtime/rollup.config.js
@@ -7,6 +7,7 @@ import * as path from "path";
import { createHash } from "crypto";
import dts from "rollup-plugin-dts";
import consts from "rollup-plugin-consts";
+import { createFilter } from "@rollup/pluginutils";
const configuration = process.env.Configuration;
const isDebug = configuration !== "Release";
@@ -45,7 +46,19 @@ const banner_dts = banner + "//!\n//! This is generated file, see src/mono/wasm/
// emcc doesn't know how to load ES6 module, that's why we need the whole rollup.js
const format = "iife";
const name = "__dotnet_runtime";
-
+const inlineAssert = [
+ {
+ pattern: /mono_assert\(([^,]*), *"([^"]*)"\);/gm,
+ // eslint-disable-next-line quotes
+ replacement: 'if (!($1)) throw new Error("Assert failed: $2"); // inlined mono_assert'
+ },
+ {
+ pattern: /mono_assert\(([^,]*), \(\) => *`([^`]*)`\);/gm,
+ replacement: "if (!($1)) throw new Error(`Assert failed: $2`); // inlined mono_assert"
+ }, {
+ pattern: /^\s*mono_assert/gm,
+ failure: "previous regexp didn't inline all mono_assert statements"
+ }];
const iffeConfig = {
treeshake: !isDebug,
input: "exports.ts",
@@ -72,7 +85,7 @@ const iffeConfig = {
handler(warning);
},
- plugins: [consts({ productVersion, configuration }), typescript()]
+ plugins: [regexReplace(inlineAssert), consts({ productVersion, configuration }), typescript()]
};
const typesConfig = {
input: "./export-types.ts",
@@ -159,4 +172,47 @@ function checkFileExists(file) {
return fs.promises.access(file, fs.constants.F_OK)
.then(() => true)
.catch(() => false);
-} \ No newline at end of file
+}
+
+function regexReplace(replacements = []) {
+ const filter = createFilter("**/*.ts");
+
+ return {
+ name: "replace",
+
+ renderChunk(code, chunk) {
+ const id = chunk.fileName;
+ if (!filter(id)) return null;
+ return executeReplacement(this, code, id);
+ },
+
+ transform(code, id) {
+ if (!filter(id)) return null;
+ return executeReplacement(this, code, id);
+ }
+ };
+
+ function executeReplacement(self, code, id) {
+ // TODO use MagicString for sourcemap support
+ let fixed = code;
+ for (const rep of replacements) {
+ const { pattern, replacement, failure } = rep;
+ if (failure) {
+ const match = pattern.test(fixed);
+ if (match) {
+ self.error(failure + " " + id, pattern.lastIndex);
+ return null;
+ }
+ }
+ else {
+ fixed = fixed.replace(pattern, replacement);
+ }
+ }
+
+ if (fixed == code) {
+ return null;
+ }
+
+ return { code: fixed };
+ }
+}
diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts
index 3871aa5fb16..3a34af2244f 100644
--- a/src/mono/wasm/runtime/startup.ts
+++ b/src/mono/wasm/runtime/startup.ts
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-import { AllAssetEntryTypes, assert, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol, MonoObject } from "./types";
+import { AllAssetEntryTypes, mono_assert, AssetEntry, CharPtrNull, DotnetModule, GlobalizationMode, MonoConfig, MonoConfigError, wasm_type_symbol, MonoObject } from "./types";
import { ENVIRONMENT_IS_ESM, ENVIRONMENT_IS_NODE, ENVIRONMENT_IS_SHELL, INTERNAL, locateFile, Module, MONO, requirePromise, runtimeHelpers } from "./imports";
import cwraps from "./cwraps";
import { mono_wasm_raise_debug_event, mono_wasm_runtime_ready } from "./debug";
@@ -36,7 +36,7 @@ export function configure_emscripten_startup(module: DotnetModule, exportedAPI:
(typeof (globalThis.document.createElement) === "function")
) {
// blazor injects a module preload link element for dotnet.[version].[sha].js
- const blazorDotNetJS = Array.from (document.head.getElementsByTagName("link")).filter(elt => elt.rel !== undefined && elt.rel == "modulepreload" && elt.href !== undefined && elt.href.indexOf("dotnet") != -1 && elt.href.indexOf (".js") != -1);
+ const blazorDotNetJS = Array.from(document.head.getElementsByTagName("link")).filter(elt => elt.rel !== undefined && elt.rel == "modulepreload" && elt.href !== undefined && elt.href.indexOf("dotnet") != -1 && elt.href.indexOf(".js") != -1);
if (blazorDotNetJS.length == 1) {
const hr = blazorDotNetJS[0].href;
console.log("determined url of main script to be " + hr);
@@ -191,8 +191,8 @@ export function mono_wasm_set_runtime_options(options: string[]): void {
// this need to be run only after onRuntimeInitialized event, when the memory is ready
function _handle_fetched_asset(asset: AssetEntry, url?: string) {
- assert(ctx, "Context is expected");
- assert(asset.buffer, "asset.buffer is expected");
+ mono_assert(ctx, "Context is expected");
+ mono_assert(asset.buffer, "asset.buffer is expected");
const bytes = new Uint8Array(asset.buffer);
if (ctx.tracing)
@@ -304,7 +304,7 @@ function finalize_startup(config: MonoConfig | MonoConfigError | undefined): voi
const moduleExt = Module as DotnetModule;
- if(!Module.disableDotnet6Compatibility && Module.exports){
+ if (!Module.disableDotnet6Compatibility && Module.exports) {
// Export emscripten defined in module through EXPORTED_RUNTIME_METHODS
// Useful to export IDBFS or other similar types generally exposed as
// global types when emscripten is not modularized.
@@ -312,10 +312,10 @@ function finalize_startup(config: MonoConfig | MonoConfigError | undefined): voi
const exportName = Module.exports[i];
const exportValue = (<any>Module)[exportName];
- if(exportValue) {
+ if (exportValue) {
globalThisAny[exportName] = exportValue;
}
- else{
+ else {
console.warn(`MONO_WASM: The exported symbol ${exportName} could not be found in the emscripten module`);
}
}
@@ -586,8 +586,8 @@ async function mono_download_assets(config: MonoConfig | MonoConfigError | undef
}
function finalize_assets(config: MonoConfig | MonoConfigError | undefined): void {
- assert(config && !config.isError, "Expected config");
- assert(ctx && ctx.downloading_count == 0, "Expected assets to be downloaded");
+ mono_assert(config && !config.isError, "Expected config");
+ mono_assert(ctx && ctx.downloading_count == 0, "Expected assets to be downloaded");
try {
for (const fetch_result of ctx.resolved_promises!) {
diff --git a/src/mono/wasm/runtime/types.ts b/src/mono/wasm/runtime/types.ts
index d26277e38fb..e649ebc0f49 100644
--- a/src/mono/wasm/runtime/types.ts
+++ b/src/mono/wasm/runtime/types.ts
@@ -82,7 +82,7 @@ export type MonoConfig = {
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,
- wait_for_debugger ?: number
+ wait_for_debugger?: number
};
export type MonoConfigError = {
@@ -220,12 +220,13 @@ export type DotnetModuleConfigImports = {
url?: any;
}
-export function assert(condition: unknown, messageFactory: string | (() => string)): asserts condition {
+// see src\mono\wasm\runtime\rollup.config.js
+// inline this, because the lambda could allocate closure on hot path otherwise
+export function mono_assert(condition: unknown, messageFactory: string | (() => string)): asserts condition {
if (!condition) {
const message = typeof messageFactory === "string"
? messageFactory
: messageFactory();
- console.error(`Assert failed: ${message}`);
throw new Error(`Assert failed: ${message}`);
}
}
@@ -276,6 +277,6 @@ export const enum MarshalError {
// Evaluates whether a value is nullish (same definition used as the ?? operator,
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_operator)
-export function is_nullish<T> (value: T | null | undefined): value is null | undefined {
+export function is_nullish<T>(value: T | null | undefined): value is null | undefined {
return (value === undefined) || (value === null);
}