diff --git a/src/bindings/js.ts b/src/bindings/js.ts index 52f0e6d08d..a38d90c4ec 100644 --- a/src/bindings/js.ts +++ b/src/bindings/js.ts @@ -949,6 +949,7 @@ export class JSBuilder extends ExportsWalker { /** Lifts a WebAssembly value to a JavaScript value. */ makeLiftFromValue(name: string, type: Type, sb: string[] = this.sb): void { if (type.isInternalReference) { + // Lift reference types const clazz = assert(type.getClassOrWrapper(this.program)); if (clazz.extends(this.program.arrayBufferInstance.prototype)) { sb.push("__liftBuffer("); @@ -1005,11 +1006,13 @@ export class JSBuilder extends ExportsWalker { } sb.push(")"); } else { - sb.push(name); - if (type.isUnsignedIntegerValue && type.size == 32) { - sb.push(" >>> 0"); - } else if (type == Type.bool) { - sb.push(" != 0"); + // Lift basic plain types + if (type == Type.bool) { + sb.push(`${name} != 0`); + } else if (type.isUnsignedIntegerValue && type.size >= 32) { + sb.push(type.size == 64 ? `BigInt.asUintN(64, ${name})` : `${name} >>> 0`); + } else { + sb.push(name); } } } @@ -1017,6 +1020,7 @@ export class JSBuilder extends ExportsWalker { /** Lowers a JavaScript value to a WebAssembly value. */ makeLowerToValue(name: string, type: Type, sb: string[] = this.sb): void { if (type.isInternalReference) { + // Lower reference types const clazz = assert(type.getClass()); if (clazz.extends(this.program.arrayBufferInstance.prototype)) { sb.push("__lowerBuffer("); @@ -1082,6 +1086,7 @@ export class JSBuilder extends ExportsWalker { sb.push(" || __notnull()"); } } else { + // Lower basic types sb.push(name); // basic value if (type.isIntegerValue && type.size == 64) { sb.push(" || 0n"); @@ -1278,15 +1283,16 @@ enum Mode { function isPlainValue(type: Type, kind: Mode): bool { if (kind == Mode.IMPORT) { - // requires coercion of undefined to 0n - if (type.isIntegerValue && type.size == 64) return false; // may be stored to an Uint8Array, make sure to store 1/0 if (type == Type.bool) return false; + // requires coercion of undefined to 0n + if (type.isIntegerValue && type.size == 64) return false; } else { - // requires coercion from signed to unsigned - if (type.isUnsignedIntegerValue && type.size == 32) return false; // requires coercion from 1/0 to true/false if (type == Type.bool) return false; + // requires coercion from signed to unsigned for u32 and u64. + // Note, u8 and u16 doesn't overflow in native type so mark as plain + if (type.isUnsignedIntegerValue && type.size >= 32) return false; } return !type.isInternalReference; } @@ -1294,10 +1300,10 @@ function isPlainValue(type: Type, kind: Mode): bool { function isPlainFunction(signature: Signature, mode: Mode): bool { var parameterTypes = signature.parameterTypes; var inverseMode = mode == Mode.IMPORT ? Mode.EXPORT : Mode.IMPORT; + if (!isPlainValue(signature.returnType, mode)) return false; for (let i = 0, k = parameterTypes.length; i < k; ++i) { if (!isPlainValue(parameterTypes[i], inverseMode)) return false; } - if (!isPlainValue(signature.returnType, mode)) return false; return true; } diff --git a/tests/compiler/bindings/esm.debug.d.ts b/tests/compiler/bindings/esm.debug.d.ts index a9d795fee1..cbf52b0406 100644 --- a/tests/compiler/bindings/esm.debug.d.ts +++ b/tests/compiler/bindings/esm.debug.d.ts @@ -52,6 +52,16 @@ export declare function plainFunction(a: number, b: number): number; * @returns `i64` */ export declare function plainFunction64(a: bigint, b: bigint): bigint; +/** + * bindings/esm/getMaxUnsigned32 + * @returns `u32` + */ +export declare function getMaxUnsigned32(): number; +/** + * bindings/esm/getMaxUnsigned64 + * @returns `u64` + */ +export declare function getMaxUnsigned64(): bigint; /** * bindings/esm/bufferFunction * @param a `~lib/arraybuffer/ArrayBuffer` diff --git a/tests/compiler/bindings/esm.debug.js b/tests/compiler/bindings/esm.debug.js index ad6cfd1467..21ebe81e62 100644 --- a/tests/compiler/bindings/esm.debug.js +++ b/tests/compiler/bindings/esm.debug.js @@ -86,6 +86,14 @@ async function instantiate(module, imports = {}) { b = b || 0n; return exports.plainFunction64(a, b); }, + getMaxUnsigned32() { + // bindings/esm/getMaxUnsigned32() => u32 + return exports.getMaxUnsigned32() >>> 0; + }, + getMaxUnsigned64() { + // bindings/esm/getMaxUnsigned64() => u64 + return BigInt.asUintN(64, exports.getMaxUnsigned64()); + }, bufferFunction(a, b) { // bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer a = __retain(__lowerBuffer(a) || __notnull()); @@ -360,6 +368,8 @@ export const { ConstEnum, plainFunction, plainFunction64, + getMaxUnsigned32, + getMaxUnsigned64, bufferFunction, stringFunction, stringFunctionOptional, diff --git a/tests/compiler/bindings/esm.debug.wat b/tests/compiler/bindings/esm.debug.wat index e811a94fe6..f045953ecc 100644 --- a/tests/compiler/bindings/esm.debug.wat +++ b/tests/compiler/bindings/esm.debug.wat @@ -12,6 +12,7 @@ (type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64))) (type $f64_=>_f64 (func (param f64) (result f64))) (type $i64_i64_=>_i64 (func (param i64 i64) (result i64))) + (type $none_=>_i64 (func (result i64))) (type $i32_i32_i64_=>_none (func (param i32 i32 i64))) (type $i32_i32_=>_f32 (func (param i32 i32) (result f32))) (type $i32_f32_=>_none (func (param i32 f32))) @@ -34,6 +35,8 @@ (global $bindings/esm/ConstEnum.ONE i32 (i32.const 1)) (global $bindings/esm/ConstEnum.TWO i32 (i32.const 2)) (global $bindings/esm/ConstEnum.THREE i32 (i32.const 3)) + (global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1)) + (global $~lib/builtins/u64.MAX_VALUE i64 (i64.const -1)) (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) @@ -91,6 +94,8 @@ (export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE)) (export "plainFunction" (func $bindings/esm/plainFunction)) (export "plainFunction64" (func $bindings/esm/plainFunction64)) + (export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32)) + (export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64)) (export "newInternref" (func $bindings/esm/newInternref)) (export "__new" (func $~lib/rt/itcms/__new)) (export "__pin" (func $~lib/rt/itcms/__pin)) @@ -118,6 +123,12 @@ local.get $1 i64.add ) + (func $bindings/esm/getMaxUnsigned32 (result i32) + global.get $~lib/builtins/u32.MAX_VALUE + ) + (func $bindings/esm/getMaxUnsigned64 (result i64) + global.get $~lib/builtins/u64.MAX_VALUE + ) (func $~lib/arraybuffer/ArrayBuffer#get:byteLength (param $0 i32) (result i32) local.get $0 i32.const 20 diff --git a/tests/compiler/bindings/esm.js b/tests/compiler/bindings/esm.js index b279d8e09d..63c6fc8792 100644 --- a/tests/compiler/bindings/esm.js +++ b/tests/compiler/bindings/esm.js @@ -35,6 +35,9 @@ export async function postInstantiate(instance) { assert.strictEqual(exports.Enum.TWO, 2); assert.strictEqual(exports.Enum[2], "TWO"); + assert.strictEqual(exports.getMaxUnsigned32(), 4294967295); + assert.strictEqual(exports.getMaxUnsigned64(), 18446744073709551615n); + assert.strictEqual(exports.plainFunction(1, 2), 3); { diff --git a/tests/compiler/bindings/esm.release.d.ts b/tests/compiler/bindings/esm.release.d.ts index a9d795fee1..cbf52b0406 100644 --- a/tests/compiler/bindings/esm.release.d.ts +++ b/tests/compiler/bindings/esm.release.d.ts @@ -52,6 +52,16 @@ export declare function plainFunction(a: number, b: number): number; * @returns `i64` */ export declare function plainFunction64(a: bigint, b: bigint): bigint; +/** + * bindings/esm/getMaxUnsigned32 + * @returns `u32` + */ +export declare function getMaxUnsigned32(): number; +/** + * bindings/esm/getMaxUnsigned64 + * @returns `u64` + */ +export declare function getMaxUnsigned64(): bigint; /** * bindings/esm/bufferFunction * @param a `~lib/arraybuffer/ArrayBuffer` diff --git a/tests/compiler/bindings/esm.release.js b/tests/compiler/bindings/esm.release.js index f2b51c2895..8285c32a1c 100644 --- a/tests/compiler/bindings/esm.release.js +++ b/tests/compiler/bindings/esm.release.js @@ -86,6 +86,14 @@ async function instantiate(module, imports = {}) { b = b || 0n; return exports.plainFunction64(a, b); }, + getMaxUnsigned32() { + // bindings/esm/getMaxUnsigned32() => u32 + return exports.getMaxUnsigned32() >>> 0; + }, + getMaxUnsigned64() { + // bindings/esm/getMaxUnsigned64() => u64 + return BigInt.asUintN(64, exports.getMaxUnsigned64()); + }, bufferFunction(a, b) { // bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer a = __retain(__lowerBuffer(a) || __notnull()); @@ -360,6 +368,8 @@ export const { ConstEnum, plainFunction, plainFunction64, + getMaxUnsigned32, + getMaxUnsigned64, bufferFunction, stringFunction, stringFunctionOptional, diff --git a/tests/compiler/bindings/esm.release.wat b/tests/compiler/bindings/esm.release.wat index 333923028d..76483b93d9 100644 --- a/tests/compiler/bindings/esm.release.wat +++ b/tests/compiler/bindings/esm.release.wat @@ -1,13 +1,14 @@ (module (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_=>_none (func (param i32))) - (type $none_=>_none (func)) (type $none_=>_i32 (func (result i32))) + (type $none_=>_none (func)) (type $i32_i32_=>_none (func (param i32 i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64))) (type $f64_=>_f64 (func (param f64) (result f64))) (type $i64_i64_=>_i64 (func (param i64 i64) (result i64))) + (type $none_=>_i64 (func (result i64))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_i64_=>_none (func (param i32 i32 i64))) (type $i32_=>_i32 (func (param i32) (result i32))) @@ -91,6 +92,8 @@ (export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE)) (export "plainFunction" (func $bindings/esm/plainFunction)) (export "plainFunction64" (func $bindings/esm/plainFunction64)) + (export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32)) + (export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64)) (export "newInternref" (func $bindings/esm/newInternref)) (export "__new" (func $~lib/rt/itcms/__new)) (export "__pin" (func $~lib/rt/itcms/__pin)) @@ -118,6 +121,12 @@ local.get $1 i64.add ) + (func $bindings/esm/getMaxUnsigned32 (result i32) + i32.const -1 + ) + (func $bindings/esm/getMaxUnsigned64 (result i64) + i64.const -1 + ) (func $~lib/rt/itcms/visitRoots (local $0 i32) (local $1 i32) diff --git a/tests/compiler/bindings/esm.ts b/tests/compiler/bindings/esm.ts index 944e35d859..3a000c534a 100644 --- a/tests/compiler/bindings/esm.ts +++ b/tests/compiler/bindings/esm.ts @@ -26,6 +26,14 @@ export function plainFunction64(a: i64, b: i64): i64 { return a + b; } +export function getMaxUnsigned32(): u32 { + return u32.MAX_VALUE; // 4294967295 +} + +export function getMaxUnsigned64(): u64 { + return u64.MAX_VALUE; // 18446744073709551615 +} + export function bufferFunction(a: ArrayBuffer, b: ArrayBuffer): ArrayBuffer { var aByteLength = a.byteLength; var bByteLength = b.byteLength; diff --git a/tests/compiler/bindings/raw.debug.d.ts b/tests/compiler/bindings/raw.debug.d.ts index 14bc6d53ec..15e2360189 100644 --- a/tests/compiler/bindings/raw.debug.d.ts +++ b/tests/compiler/bindings/raw.debug.d.ts @@ -53,6 +53,16 @@ declare namespace __AdaptedExports { * @returns `i64` */ export function plainFunction64(a: bigint, b: bigint): bigint; + /** + * bindings/esm/getMaxUnsigned32 + * @returns `u32` + */ + export function getMaxUnsigned32(): number; + /** + * bindings/esm/getMaxUnsigned64 + * @returns `u64` + */ + export function getMaxUnsigned64(): bigint; /** * bindings/esm/bufferFunction * @param a `~lib/arraybuffer/ArrayBuffer` diff --git a/tests/compiler/bindings/raw.debug.js b/tests/compiler/bindings/raw.debug.js index dabcb032b9..1091069c5b 100644 --- a/tests/compiler/bindings/raw.debug.js +++ b/tests/compiler/bindings/raw.debug.js @@ -86,6 +86,14 @@ export async function instantiate(module, imports = {}) { b = b || 0n; return exports.plainFunction64(a, b); }, + getMaxUnsigned32() { + // bindings/esm/getMaxUnsigned32() => u32 + return exports.getMaxUnsigned32() >>> 0; + }, + getMaxUnsigned64() { + // bindings/esm/getMaxUnsigned64() => u64 + return BigInt.asUintN(64, exports.getMaxUnsigned64()); + }, bufferFunction(a, b) { // bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer a = __retain(__lowerBuffer(a) || __notnull()); diff --git a/tests/compiler/bindings/raw.debug.wat b/tests/compiler/bindings/raw.debug.wat index f987fcf6b2..863c18d4e5 100644 --- a/tests/compiler/bindings/raw.debug.wat +++ b/tests/compiler/bindings/raw.debug.wat @@ -12,6 +12,7 @@ (type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64))) (type $f64_=>_f64 (func (param f64) (result f64))) (type $i64_i64_=>_i64 (func (param i64 i64) (result i64))) + (type $none_=>_i64 (func (result i64))) (type $i32_i32_i64_=>_none (func (param i32 i32 i64))) (type $i32_i32_=>_f32 (func (param i32 i32) (result f32))) (type $i32_f32_=>_none (func (param i32 f32))) @@ -34,6 +35,8 @@ (global $bindings/esm/ConstEnum.ONE i32 (i32.const 1)) (global $bindings/esm/ConstEnum.TWO i32 (i32.const 2)) (global $bindings/esm/ConstEnum.THREE i32 (i32.const 3)) + (global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1)) + (global $~lib/builtins/u64.MAX_VALUE i64 (i64.const -1)) (global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0)) (global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1)) (global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2)) @@ -91,6 +94,8 @@ (export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE)) (export "plainFunction" (func $bindings/esm/plainFunction)) (export "plainFunction64" (func $bindings/esm/plainFunction64)) + (export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32)) + (export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64)) (export "newInternref" (func $bindings/esm/newInternref)) (export "__new" (func $~lib/rt/itcms/__new)) (export "__pin" (func $~lib/rt/itcms/__pin)) @@ -121,6 +126,12 @@ local.get $1 i64.add ) + (func $bindings/esm/getMaxUnsigned32 (result i32) + global.get $~lib/builtins/u32.MAX_VALUE + ) + (func $bindings/esm/getMaxUnsigned64 (result i64) + global.get $~lib/builtins/u64.MAX_VALUE + ) (func $~lib/arraybuffer/ArrayBuffer#get:byteLength (param $0 i32) (result i32) local.get $0 i32.const 20 diff --git a/tests/compiler/bindings/raw.release.d.ts b/tests/compiler/bindings/raw.release.d.ts index 14bc6d53ec..15e2360189 100644 --- a/tests/compiler/bindings/raw.release.d.ts +++ b/tests/compiler/bindings/raw.release.d.ts @@ -53,6 +53,16 @@ declare namespace __AdaptedExports { * @returns `i64` */ export function plainFunction64(a: bigint, b: bigint): bigint; + /** + * bindings/esm/getMaxUnsigned32 + * @returns `u32` + */ + export function getMaxUnsigned32(): number; + /** + * bindings/esm/getMaxUnsigned64 + * @returns `u64` + */ + export function getMaxUnsigned64(): bigint; /** * bindings/esm/bufferFunction * @param a `~lib/arraybuffer/ArrayBuffer` diff --git a/tests/compiler/bindings/raw.release.js b/tests/compiler/bindings/raw.release.js index dabcb032b9..1091069c5b 100644 --- a/tests/compiler/bindings/raw.release.js +++ b/tests/compiler/bindings/raw.release.js @@ -86,6 +86,14 @@ export async function instantiate(module, imports = {}) { b = b || 0n; return exports.plainFunction64(a, b); }, + getMaxUnsigned32() { + // bindings/esm/getMaxUnsigned32() => u32 + return exports.getMaxUnsigned32() >>> 0; + }, + getMaxUnsigned64() { + // bindings/esm/getMaxUnsigned64() => u64 + return BigInt.asUintN(64, exports.getMaxUnsigned64()); + }, bufferFunction(a, b) { // bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer a = __retain(__lowerBuffer(a) || __notnull()); diff --git a/tests/compiler/bindings/raw.release.wat b/tests/compiler/bindings/raw.release.wat index f5f70ccc7a..b4c9560f48 100644 --- a/tests/compiler/bindings/raw.release.wat +++ b/tests/compiler/bindings/raw.release.wat @@ -1,13 +1,14 @@ (module (type $i32_i32_=>_i32 (func (param i32 i32) (result i32))) (type $i32_=>_none (func (param i32))) - (type $none_=>_none (func)) (type $none_=>_i32 (func (result i32))) + (type $none_=>_none (func)) (type $i32_i32_=>_none (func (param i32 i32))) (type $i32_i32_i32_=>_none (func (param i32 i32 i32))) (type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64))) (type $f64_=>_f64 (func (param f64) (result f64))) (type $i64_i64_=>_i64 (func (param i64 i64) (result i64))) + (type $none_=>_i64 (func (result i64))) (type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32))) (type $i32_i32_i64_=>_none (func (param i32 i32 i64))) (type $i32_=>_i32 (func (param i32) (result i32))) @@ -91,6 +92,8 @@ (export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE)) (export "plainFunction" (func $bindings/esm/plainFunction)) (export "plainFunction64" (func $bindings/esm/plainFunction64)) + (export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32)) + (export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64)) (export "newInternref" (func $bindings/esm/newInternref)) (export "__new" (func $~lib/rt/itcms/__new)) (export "__pin" (func $~lib/rt/itcms/__pin)) @@ -118,6 +121,12 @@ local.get $1 i64.add ) + (func $bindings/esm/getMaxUnsigned32 (result i32) + i32.const -1 + ) + (func $bindings/esm/getMaxUnsigned64 (result i64) + i64.const -1 + ) (func $~lib/rt/itcms/visitRoots (local $0 i32) (local $1 i32)