Skip to content

fix: Add coercion for unsigned 64-bit types for bindings #2350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 16 additions & 10 deletions src/bindings/js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(");
Expand Down Expand Up @@ -1005,18 +1006,21 @@ 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);
}
}
}

/** 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(");
Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -1278,26 +1283,27 @@ 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;
}

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;
}

Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.debug.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -360,6 +368,8 @@ export const {
ConstEnum,
plainFunction,
plainFunction64,
getMaxUnsigned32,
getMaxUnsigned64,
bufferFunction,
stringFunction,
stringFunctionOptional,
Expand Down
11 changes: 11 additions & 0 deletions tests/compiler/bindings/esm.debug.wat
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions tests/compiler/bindings/esm.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);

{
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.release.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.release.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -360,6 +368,8 @@ export const {
ConstEnum,
plainFunction,
plainFunction64,
getMaxUnsigned32,
getMaxUnsigned64,
bufferFunction,
stringFunction,
stringFunctionOptional,
Expand Down
11 changes: 10 additions & 1 deletion tests/compiler/bindings/esm.release.wat
Original file line number Diff line number Diff line change
@@ -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)))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions tests/compiler/bindings/esm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/bindings/raw.debug.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
8 changes: 8 additions & 0 deletions tests/compiler/bindings/raw.debug.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
11 changes: 11 additions & 0 deletions tests/compiler/bindings/raw.debug.wat
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
Expand All @@ -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))
Expand Down Expand Up @@ -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))
Expand Down Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions tests/compiler/bindings/raw.release.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
8 changes: 8 additions & 0 deletions tests/compiler/bindings/raw.release.js
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down
Loading