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
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
@@ -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,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(");
@@ -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,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;
}

10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.debug.d.ts
Original file line number Diff line number Diff line change
@@ -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`
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.debug.js
Original file line number Diff line number Diff line change
@@ -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,
11 changes: 11 additions & 0 deletions tests/compiler/bindings/esm.debug.wat
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions tests/compiler/bindings/esm.js
Original file line number Diff line number Diff line change
@@ -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);

{
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.release.d.ts
Original file line number Diff line number Diff line change
@@ -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`
10 changes: 10 additions & 0 deletions tests/compiler/bindings/esm.release.js
Original file line number Diff line number Diff line change
@@ -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,
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)))
@@ -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)
8 changes: 8 additions & 0 deletions tests/compiler/bindings/esm.ts
Original file line number Diff line number Diff line change
@@ -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;
10 changes: 10 additions & 0 deletions tests/compiler/bindings/raw.debug.d.ts
Original file line number Diff line number Diff line change
@@ -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`
8 changes: 8 additions & 0 deletions tests/compiler/bindings/raw.debug.js
Original file line number Diff line number Diff line change
@@ -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());
11 changes: 11 additions & 0 deletions tests/compiler/bindings/raw.debug.wat
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions tests/compiler/bindings/raw.release.d.ts
Original file line number Diff line number Diff line change
@@ -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`
8 changes: 8 additions & 0 deletions tests/compiler/bindings/raw.release.js
Original file line number Diff line number Diff line change
@@ -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());
Loading