Skip to content

Commit fa4a6ef

Browse files
authored
fix: Add coercion for unsigned 64-bit types for bindings (#2350)
1 parent d559982 commit fa4a6ef

File tree

15 files changed

+145
-12
lines changed

15 files changed

+145
-12
lines changed

src/bindings/js.ts

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,7 @@ export class JSBuilder extends ExportsWalker {
949949
/** Lifts a WebAssembly value to a JavaScript value. */
950950
makeLiftFromValue(name: string, type: Type, sb: string[] = this.sb): void {
951951
if (type.isInternalReference) {
952+
// Lift reference types
952953
const clazz = assert(type.getClassOrWrapper(this.program));
953954
if (clazz.extends(this.program.arrayBufferInstance.prototype)) {
954955
sb.push("__liftBuffer(");
@@ -1005,18 +1006,21 @@ export class JSBuilder extends ExportsWalker {
10051006
}
10061007
sb.push(")");
10071008
} else {
1008-
sb.push(name);
1009-
if (type.isUnsignedIntegerValue && type.size == 32) {
1010-
sb.push(" >>> 0");
1011-
} else if (type == Type.bool) {
1012-
sb.push(" != 0");
1009+
// Lift basic plain types
1010+
if (type == Type.bool) {
1011+
sb.push(`${name} != 0`);
1012+
} else if (type.isUnsignedIntegerValue && type.size >= 32) {
1013+
sb.push(type.size == 64 ? `BigInt.asUintN(64, ${name})` : `${name} >>> 0`);
1014+
} else {
1015+
sb.push(name);
10131016
}
10141017
}
10151018
}
10161019

10171020
/** Lowers a JavaScript value to a WebAssembly value. */
10181021
makeLowerToValue(name: string, type: Type, sb: string[] = this.sb): void {
10191022
if (type.isInternalReference) {
1023+
// Lower reference types
10201024
const clazz = assert(type.getClass());
10211025
if (clazz.extends(this.program.arrayBufferInstance.prototype)) {
10221026
sb.push("__lowerBuffer(");
@@ -1082,6 +1086,7 @@ export class JSBuilder extends ExportsWalker {
10821086
sb.push(" || __notnull()");
10831087
}
10841088
} else {
1089+
// Lower basic types
10851090
sb.push(name); // basic value
10861091
if (type.isIntegerValue && type.size == 64) {
10871092
sb.push(" || 0n");
@@ -1278,26 +1283,27 @@ enum Mode {
12781283

12791284
function isPlainValue(type: Type, kind: Mode): bool {
12801285
if (kind == Mode.IMPORT) {
1281-
// requires coercion of undefined to 0n
1282-
if (type.isIntegerValue && type.size == 64) return false;
12831286
// may be stored to an Uint8Array, make sure to store 1/0
12841287
if (type == Type.bool) return false;
1288+
// requires coercion of undefined to 0n
1289+
if (type.isIntegerValue && type.size == 64) return false;
12851290
} else {
1286-
// requires coercion from signed to unsigned
1287-
if (type.isUnsignedIntegerValue && type.size == 32) return false;
12881291
// requires coercion from 1/0 to true/false
12891292
if (type == Type.bool) return false;
1293+
// requires coercion from signed to unsigned for u32 and u64.
1294+
// Note, u8 and u16 doesn't overflow in native type so mark as plain
1295+
if (type.isUnsignedIntegerValue && type.size >= 32) return false;
12901296
}
12911297
return !type.isInternalReference;
12921298
}
12931299

12941300
function isPlainFunction(signature: Signature, mode: Mode): bool {
12951301
var parameterTypes = signature.parameterTypes;
12961302
var inverseMode = mode == Mode.IMPORT ? Mode.EXPORT : Mode.IMPORT;
1303+
if (!isPlainValue(signature.returnType, mode)) return false;
12971304
for (let i = 0, k = parameterTypes.length; i < k; ++i) {
12981305
if (!isPlainValue(parameterTypes[i], inverseMode)) return false;
12991306
}
1300-
if (!isPlainValue(signature.returnType, mode)) return false;
13011307
return true;
13021308
}
13031309

tests/compiler/bindings/esm.debug.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ export declare function plainFunction(a: number, b: number): number;
5252
* @returns `i64`
5353
*/
5454
export declare function plainFunction64(a: bigint, b: bigint): bigint;
55+
/**
56+
* bindings/esm/getMaxUnsigned32
57+
* @returns `u32`
58+
*/
59+
export declare function getMaxUnsigned32(): number;
60+
/**
61+
* bindings/esm/getMaxUnsigned64
62+
* @returns `u64`
63+
*/
64+
export declare function getMaxUnsigned64(): bigint;
5565
/**
5666
* bindings/esm/bufferFunction
5767
* @param a `~lib/arraybuffer/ArrayBuffer`

tests/compiler/bindings/esm.debug.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ async function instantiate(module, imports = {}) {
8686
b = b || 0n;
8787
return exports.plainFunction64(a, b);
8888
},
89+
getMaxUnsigned32() {
90+
// bindings/esm/getMaxUnsigned32() => u32
91+
return exports.getMaxUnsigned32() >>> 0;
92+
},
93+
getMaxUnsigned64() {
94+
// bindings/esm/getMaxUnsigned64() => u64
95+
return BigInt.asUintN(64, exports.getMaxUnsigned64());
96+
},
8997
bufferFunction(a, b) {
9098
// bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer
9199
a = __retain(__lowerBuffer(a) || __notnull());
@@ -360,6 +368,8 @@ export const {
360368
ConstEnum,
361369
plainFunction,
362370
plainFunction64,
371+
getMaxUnsigned32,
372+
getMaxUnsigned64,
363373
bufferFunction,
364374
stringFunction,
365375
stringFunctionOptional,

tests/compiler/bindings/esm.debug.wat

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
(type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64)))
1313
(type $f64_=>_f64 (func (param f64) (result f64)))
1414
(type $i64_i64_=>_i64 (func (param i64 i64) (result i64)))
15+
(type $none_=>_i64 (func (result i64)))
1516
(type $i32_i32_i64_=>_none (func (param i32 i32 i64)))
1617
(type $i32_i32_=>_f32 (func (param i32 i32) (result f32)))
1718
(type $i32_f32_=>_none (func (param i32 f32)))
@@ -34,6 +35,8 @@
3435
(global $bindings/esm/ConstEnum.ONE i32 (i32.const 1))
3536
(global $bindings/esm/ConstEnum.TWO i32 (i32.const 2))
3637
(global $bindings/esm/ConstEnum.THREE i32 (i32.const 3))
38+
(global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1))
39+
(global $~lib/builtins/u64.MAX_VALUE i64 (i64.const -1))
3740
(global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0))
3841
(global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1))
3942
(global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2))
@@ -91,6 +94,8 @@
9194
(export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE))
9295
(export "plainFunction" (func $bindings/esm/plainFunction))
9396
(export "plainFunction64" (func $bindings/esm/plainFunction64))
97+
(export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32))
98+
(export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64))
9499
(export "newInternref" (func $bindings/esm/newInternref))
95100
(export "__new" (func $~lib/rt/itcms/__new))
96101
(export "__pin" (func $~lib/rt/itcms/__pin))
@@ -118,6 +123,12 @@
118123
local.get $1
119124
i64.add
120125
)
126+
(func $bindings/esm/getMaxUnsigned32 (result i32)
127+
global.get $~lib/builtins/u32.MAX_VALUE
128+
)
129+
(func $bindings/esm/getMaxUnsigned64 (result i64)
130+
global.get $~lib/builtins/u64.MAX_VALUE
131+
)
121132
(func $~lib/arraybuffer/ArrayBuffer#get:byteLength (param $0 i32) (result i32)
122133
local.get $0
123134
i32.const 20

tests/compiler/bindings/esm.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ export async function postInstantiate(instance) {
3535
assert.strictEqual(exports.Enum.TWO, 2);
3636
assert.strictEqual(exports.Enum[2], "TWO");
3737

38+
assert.strictEqual(exports.getMaxUnsigned32(), 4294967295);
39+
assert.strictEqual(exports.getMaxUnsigned64(), 18446744073709551615n);
40+
3841
assert.strictEqual(exports.plainFunction(1, 2), 3);
3942

4043
{

tests/compiler/bindings/esm.release.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,16 @@ export declare function plainFunction(a: number, b: number): number;
5252
* @returns `i64`
5353
*/
5454
export declare function plainFunction64(a: bigint, b: bigint): bigint;
55+
/**
56+
* bindings/esm/getMaxUnsigned32
57+
* @returns `u32`
58+
*/
59+
export declare function getMaxUnsigned32(): number;
60+
/**
61+
* bindings/esm/getMaxUnsigned64
62+
* @returns `u64`
63+
*/
64+
export declare function getMaxUnsigned64(): bigint;
5565
/**
5666
* bindings/esm/bufferFunction
5767
* @param a `~lib/arraybuffer/ArrayBuffer`

tests/compiler/bindings/esm.release.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ async function instantiate(module, imports = {}) {
8686
b = b || 0n;
8787
return exports.plainFunction64(a, b);
8888
},
89+
getMaxUnsigned32() {
90+
// bindings/esm/getMaxUnsigned32() => u32
91+
return exports.getMaxUnsigned32() >>> 0;
92+
},
93+
getMaxUnsigned64() {
94+
// bindings/esm/getMaxUnsigned64() => u64
95+
return BigInt.asUintN(64, exports.getMaxUnsigned64());
96+
},
8997
bufferFunction(a, b) {
9098
// bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer
9199
a = __retain(__lowerBuffer(a) || __notnull());
@@ -360,6 +368,8 @@ export const {
360368
ConstEnum,
361369
plainFunction,
362370
plainFunction64,
371+
getMaxUnsigned32,
372+
getMaxUnsigned64,
363373
bufferFunction,
364374
stringFunction,
365375
stringFunctionOptional,

tests/compiler/bindings/esm.release.wat

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
(module
22
(type $i32_i32_=>_i32 (func (param i32 i32) (result i32)))
33
(type $i32_=>_none (func (param i32)))
4-
(type $none_=>_none (func))
54
(type $none_=>_i32 (func (result i32)))
5+
(type $none_=>_none (func))
66
(type $i32_i32_=>_none (func (param i32 i32)))
77
(type $i32_i32_i32_=>_none (func (param i32 i32 i32)))
88
(type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64)))
99
(type $f64_=>_f64 (func (param f64) (result f64)))
1010
(type $i64_i64_=>_i64 (func (param i64 i64) (result i64)))
11+
(type $none_=>_i64 (func (result i64)))
1112
(type $i32_i32_i32_i32_=>_none (func (param i32 i32 i32 i32)))
1213
(type $i32_i32_i64_=>_none (func (param i32 i32 i64)))
1314
(type $i32_=>_i32 (func (param i32) (result i32)))
@@ -91,6 +92,8 @@
9192
(export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE))
9293
(export "plainFunction" (func $bindings/esm/plainFunction))
9394
(export "plainFunction64" (func $bindings/esm/plainFunction64))
95+
(export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32))
96+
(export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64))
9497
(export "newInternref" (func $bindings/esm/newInternref))
9598
(export "__new" (func $~lib/rt/itcms/__new))
9699
(export "__pin" (func $~lib/rt/itcms/__pin))
@@ -118,6 +121,12 @@
118121
local.get $1
119122
i64.add
120123
)
124+
(func $bindings/esm/getMaxUnsigned32 (result i32)
125+
i32.const -1
126+
)
127+
(func $bindings/esm/getMaxUnsigned64 (result i64)
128+
i64.const -1
129+
)
121130
(func $~lib/rt/itcms/visitRoots
122131
(local $0 i32)
123132
(local $1 i32)

tests/compiler/bindings/esm.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ export function plainFunction64(a: i64, b: i64): i64 {
2626
return a + b;
2727
}
2828

29+
export function getMaxUnsigned32(): u32 {
30+
return u32.MAX_VALUE; // 4294967295
31+
}
32+
33+
export function getMaxUnsigned64(): u64 {
34+
return u64.MAX_VALUE; // 18446744073709551615
35+
}
36+
2937
export function bufferFunction(a: ArrayBuffer, b: ArrayBuffer): ArrayBuffer {
3038
var aByteLength = a.byteLength;
3139
var bByteLength = b.byteLength;

tests/compiler/bindings/raw.debug.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ declare namespace __AdaptedExports {
5353
* @returns `i64`
5454
*/
5555
export function plainFunction64(a: bigint, b: bigint): bigint;
56+
/**
57+
* bindings/esm/getMaxUnsigned32
58+
* @returns `u32`
59+
*/
60+
export function getMaxUnsigned32(): number;
61+
/**
62+
* bindings/esm/getMaxUnsigned64
63+
* @returns `u64`
64+
*/
65+
export function getMaxUnsigned64(): bigint;
5666
/**
5767
* bindings/esm/bufferFunction
5868
* @param a `~lib/arraybuffer/ArrayBuffer`

tests/compiler/bindings/raw.debug.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ export async function instantiate(module, imports = {}) {
8686
b = b || 0n;
8787
return exports.plainFunction64(a, b);
8888
},
89+
getMaxUnsigned32() {
90+
// bindings/esm/getMaxUnsigned32() => u32
91+
return exports.getMaxUnsigned32() >>> 0;
92+
},
93+
getMaxUnsigned64() {
94+
// bindings/esm/getMaxUnsigned64() => u64
95+
return BigInt.asUintN(64, exports.getMaxUnsigned64());
96+
},
8997
bufferFunction(a, b) {
9098
// bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer
9199
a = __retain(__lowerBuffer(a) || __notnull());

tests/compiler/bindings/raw.debug.wat

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
(type $i32_i32_f64_f64_f64_f64_f64_=>_none (func (param i32 i32 f64 f64 f64 f64 f64)))
1313
(type $f64_=>_f64 (func (param f64) (result f64)))
1414
(type $i64_i64_=>_i64 (func (param i64 i64) (result i64)))
15+
(type $none_=>_i64 (func (result i64)))
1516
(type $i32_i32_i64_=>_none (func (param i32 i32 i64)))
1617
(type $i32_i32_=>_f32 (func (param i32 i32) (result f32)))
1718
(type $i32_f32_=>_none (func (param i32 f32)))
@@ -34,6 +35,8 @@
3435
(global $bindings/esm/ConstEnum.ONE i32 (i32.const 1))
3536
(global $bindings/esm/ConstEnum.TWO i32 (i32.const 2))
3637
(global $bindings/esm/ConstEnum.THREE i32 (i32.const 3))
38+
(global $~lib/builtins/u32.MAX_VALUE i32 (i32.const -1))
39+
(global $~lib/builtins/u64.MAX_VALUE i64 (i64.const -1))
3740
(global $~lib/shared/runtime/Runtime.Stub i32 (i32.const 0))
3841
(global $~lib/shared/runtime/Runtime.Minimal i32 (i32.const 1))
3942
(global $~lib/shared/runtime/Runtime.Incremental i32 (i32.const 2))
@@ -91,6 +94,8 @@
9194
(export "ConstEnum.THREE" (global $bindings/esm/ConstEnum.THREE))
9295
(export "plainFunction" (func $bindings/esm/plainFunction))
9396
(export "plainFunction64" (func $bindings/esm/plainFunction64))
97+
(export "getMaxUnsigned32" (func $bindings/esm/getMaxUnsigned32))
98+
(export "getMaxUnsigned64" (func $bindings/esm/getMaxUnsigned64))
9499
(export "newInternref" (func $bindings/esm/newInternref))
95100
(export "__new" (func $~lib/rt/itcms/__new))
96101
(export "__pin" (func $~lib/rt/itcms/__pin))
@@ -121,6 +126,12 @@
121126
local.get $1
122127
i64.add
123128
)
129+
(func $bindings/esm/getMaxUnsigned32 (result i32)
130+
global.get $~lib/builtins/u32.MAX_VALUE
131+
)
132+
(func $bindings/esm/getMaxUnsigned64 (result i64)
133+
global.get $~lib/builtins/u64.MAX_VALUE
134+
)
124135
(func $~lib/arraybuffer/ArrayBuffer#get:byteLength (param $0 i32) (result i32)
125136
local.get $0
126137
i32.const 20

tests/compiler/bindings/raw.release.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ declare namespace __AdaptedExports {
5353
* @returns `i64`
5454
*/
5555
export function plainFunction64(a: bigint, b: bigint): bigint;
56+
/**
57+
* bindings/esm/getMaxUnsigned32
58+
* @returns `u32`
59+
*/
60+
export function getMaxUnsigned32(): number;
61+
/**
62+
* bindings/esm/getMaxUnsigned64
63+
* @returns `u64`
64+
*/
65+
export function getMaxUnsigned64(): bigint;
5666
/**
5767
* bindings/esm/bufferFunction
5868
* @param a `~lib/arraybuffer/ArrayBuffer`

tests/compiler/bindings/raw.release.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,14 @@ export async function instantiate(module, imports = {}) {
8686
b = b || 0n;
8787
return exports.plainFunction64(a, b);
8888
},
89+
getMaxUnsigned32() {
90+
// bindings/esm/getMaxUnsigned32() => u32
91+
return exports.getMaxUnsigned32() >>> 0;
92+
},
93+
getMaxUnsigned64() {
94+
// bindings/esm/getMaxUnsigned64() => u64
95+
return BigInt.asUintN(64, exports.getMaxUnsigned64());
96+
},
8997
bufferFunction(a, b) {
9098
// bindings/esm/bufferFunction(~lib/arraybuffer/ArrayBuffer, ~lib/arraybuffer/ArrayBuffer) => ~lib/arraybuffer/ArrayBuffer
9199
a = __retain(__lowerBuffer(a) || __notnull());

0 commit comments

Comments
 (0)