Skip to content

Commit

Permalink
Merge JS-BigInt-integration proposal into spec
Browse files Browse the repository at this point in the history
See the JS-BigInt-integration proposal here:

https://github.com/WebAssembly/JS-BigInt-integration
  • Loading branch information
Ms2ger committed May 11, 2020
1 parent cc2d59b commit 1411462
Show file tree
Hide file tree
Showing 5 changed files with 556 additions and 61 deletions.
31 changes: 16 additions & 15 deletions document/js-api/index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ urlPrefix: https://tc39.github.io/ecma262/; spec: ECMASCRIPT
text: CreateArrayFromList; url: sec-createarrayfromlist
text: GetMethod; url: sec-getmethod
text: IterableToList; url: sec-iterabletolist
text: ToBigInt64; url: #sec-tobigint64
text: BigInt; url: #sec-ecmascript-language-types-bigint-type
type: abstract-op
text: CreateMethodProperty; url: sec-createmethodproperty
urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: dfn
Expand Down Expand Up @@ -172,6 +174,7 @@ urlPrefix: https://webassembly.github.io/spec/core/; spec: WebAssembly; type: df
text: current frame; url: exec/conventions.html#exec-notation-textual
text: module; for: frame; url: exec/runtime.html#syntax-frame
text: memaddrs; for: moduleinst; url: exec/runtime.html#syntax-moduleinst
text: signed_64; url: exec/numerics.html#aux-signed
text: sequence; url: syntax/conventions.html#grammar-notation
urlPrefix: https://heycam.github.io/webidl/; spec: WebIDL
type: dfn
Expand Down Expand Up @@ -348,8 +351,11 @@ A {{Module}} object represents a single WebAssembly module. Each {{Module}} obje
1. Let |externfunc| be the [=external value=] [=external value|func=] |funcaddr|.
1. [=list/Append=] |externfunc| to |imports|.
1. If |externtype| is of the form [=global=] <var ignore>mut</var> |valtype|,
1. If [=Type=](|v|) is Number,
1. If |valtype| is [=i64=], throw a {{LinkError}} exception.
1. If [=Type=](|v|) is Number or BigInt,
1. If |valtype| is [=i64=] and [=Type=](|v|) is Number,
1. Throw a {{LinkError}} exception.
1. If |valtype| is not [=i64=] and [=Type=](|v|) is BigInt,
1. Throw a {{LinkError}} exception.
1. Let |value| be [=ToWebAssemblyValue=](|v|, |valtype|).
1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let (|store|, |globaladdr|) be [=global_alloc=](|store|, [=const=] |valtype|, |value|).
Expand Down Expand Up @@ -871,7 +877,6 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
1. If |v| is undefined,
1. Let |value| be [=DefaultValue=](|valuetype|).
1. Otherwise,
1. If |valuetype| is [=i64=], throw a {{TypeError}} exception.
1. Let |value| be [=ToWebAssemblyValue=](|v|, |valuetype|).
1. If |mutable| is true, let |globaltype| be [=var=] |valuetype|; otherwise, let |globaltype| be [=const=] |valuetype|.
1. Let |store| be the current agent's [=associated store=].
Expand All @@ -884,8 +889,6 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
The algorithm <dfn>GetGlobalValue</dfn>({{Global}} |global|) performs the following steps:
1. Let |store| be the current agent's [=associated store=].
1. Let |globaladdr| be |global|.\[[Global]].
1. Let |globaltype| be [=global_type=](|store|, |globaladdr|).
1. If |globaltype| is of the form <var ignore>mut</var> [=i64=], throw a {{TypeError}}.
1. Let |value| be [=global_read=](|store|, |globaladdr|).
1. Return [=ToJSValue=](|value|).
</div>
Expand All @@ -899,7 +902,6 @@ which can be simultaneously referenced by multiple {{Instance}} objects. Each
1. Let |globaladdr| be **this**.\[[Global]].
1. Let |mut| |valuetype| be [=global_type=](|store|, |globaladdr|).
1. If |mut| is [=const=], throw a {{TypeError}}.
1. If |valuetype| is [=i64=], throw a {{TypeError}}.
1. Let |value| be [=ToWebAssemblyValue=](**the given value**, |valuetype|).
1. Let |store| be [=global_write=](|store|, |globaladdr|, |value|).
1. If |store| is [=error=], throw a {{RangeError}} exception.
Expand Down Expand Up @@ -958,11 +960,7 @@ This slot holds a [=function address=] relative to the [=surrounding agent=]'s [

1. Let |store| be the [=surrounding agent=]'s [=associated store=].
1. Let |functype| be [=func_type=](|store|, |funcaddr|).
1. Let [|parameters|][|results|] be |functype|.
1. If |parameters| or |results| contains an [=i64=], throw a {{TypeError}}.

Note: the above error is thrown each time the \[[Call]] method is invoked.

1. Let [|parameters|][<var ignore>results</var>] be |functype|.
1. Let |args| be an empty [=list=] of WebAssembly values.
1. Let |i| be 0.
1. [=list/iterate|For each=] |t| of |parameters|,
Expand Down Expand Up @@ -990,8 +988,7 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not
<div algorithm>
To <dfn>run a host function</dfn> from the JavaScript object |func|, type |functype|, and [=list=] of [=WebAssembly values=] |arguments|, perform the following steps:

1. Let [|parameters|][|results|] be |functype|.
1. If either |parameters| or |results| contains [=i64=], throw a {{TypeError}}.
1. Let [<var ignore>parameters</var>][|results|] be |functype|.
1. Let |jsArguments| be an empty [=list=].
1. [=list/iterate|For each=] |arg| of |arguments|,
1. [=list/Append=] ! [=ToJSValue=](|arg|) to |jsArguments|.
Expand Down Expand Up @@ -1028,7 +1025,9 @@ Note: Exported Functions do not have a \[[Construct]] method and thus it is not
<div algorithm>
The algorithm <dfn>ToJSValue</dfn>(|w|) coerces a [=WebAssembly value=] to a JavaScript value by performing the following steps:

1. Assert: |w| is not of the form [=i64.const=] <var ignore>i64</var>.
1. If |w| is of the form [=i64.const=] |i64|,
1. Let |v| be [=signed_64=](|i64|).
1. Return a [=BigInt=] representing the mathematical value |v|.
1. If |w| is of the form [=i32.const=] |i32|, return [=the Number value=] for [=signed_32=](|i32|).
1. If |w| is of the form [=f32.const=] |f32|, return [=the Number value=] for |f32|.
1. If |w| is of the form [=f64.const=] |f64|, return [=the Number value=] for |f64|.
Expand All @@ -1042,7 +1041,9 @@ Note: Number values which are equal to NaN may have various observable NaN paylo
The algorithm <dfn>ToWebAssemblyValue</dfn>(|v|, |type|) coerce a JavaScript value to a [=WebAssembly value=] performs the following steps:


1. Assert: |type| is not [=i64=].
1. If |type| is [=i64=],
1. Let |i64| be ? [=ToBigInt64=](|v|).
1. Return [=i64.const=] |i64|.
1. If |type| is [=i32=],
1. Let |i32| be ? [=ToInt32=](|v|).
1. Return [=i32.const=] |i32|.
Expand Down
58 changes: 44 additions & 14 deletions test/js-api/bad-imports.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,16 @@
* (if any) yields the correct error.
*/
function test_bad_imports(t) {
function value_type(type) {
switch (type) {
case "i32": return kWasmI32;
case "i64": return kWasmI64;
case "f32": return kWasmF32;
case "f64": return kWasmF64;
default: throw new TypeError(`Unexpected type ${type}`);
}
}

for (const value of [null, true, "", Symbol(), 1, 0.1, NaN]) {
t(`Non-object imports argument: ${format_value(value)}`,
TypeError,
Expand Down Expand Up @@ -45,17 +55,6 @@ function test_bad_imports(t) {
value);
}

t(`Importing an i64 global`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", kWasmI64);
},
{
"module": {
"global": 0,
},
});

for (const value of [undefined, null, true, "", Symbol(), 1, 0.1, NaN, {}]) {
t(`Importing a function with an incorrectly-typed value: ${format_value(value)}`,
WebAssembly.LinkError,
Expand All @@ -82,17 +81,48 @@ function test_bad_imports(t) {
[new WebAssembly.Global({value: "f32"}), "WebAssembly.Global object (wrong value type)"],
];

for (const [value, name = format_value(value)] of nonGlobals) {
t(`Importing a global with an incorrectly-typed value: ${name}`,
for (const type of ["i32", "i64", "f32", "f64"]) {
const extendedNonGlobals = nonGlobals.concat([
type === "i64" ? [0, "Number"] : [0n, "BigInt"]
]);
for (const [value, name = format_value(value)] of extendedNonGlobals) {
t(`Importing an ${type} global with an incorrectly-typed value: ${name}`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", value_type(type));
},
{
"module": {
"global": value,
},
});
}
}

for (const type of ["i32", "i64", "f32", "f64"]) {
const value = type === "i64" ? 0n : 0;
t(`Importing an ${type} mutable global with a primitive value`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", kWasmI32);
builder.addImportedGlobal("module", "global", value_type(type), true);
},
{
"module": {
"global": value,
},
});

const global = new WebAssembly.Global({ "value": type }, value);
t(`Importing an ${type} mutable global with an immutable Global object`,
WebAssembly.LinkError,
builder => {
builder.addImportedGlobal("module", "global", value_type(type), true);
},
{
"module": {
"global": global,
},
});
}

const nonMemories = [
Expand Down
47 changes: 43 additions & 4 deletions test/js-api/global/constructor.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,7 @@ test(() => {
test(() => {
const argument = { "value": "i64" };
const global = new WebAssembly.Global(argument);
assert_throws_js(TypeError, () => global.value);
assert_throws_js(TypeError, () => global.valueOf());
assert_Global(global, 0n);
}, "i64 with default");

for (const type of ["i32", "f32", "f64"]) {
Expand All @@ -108,8 +107,10 @@ for (const type of ["i32", "f32", "f64"]) {
[false, 0],
[2, 2],
["3", 3],
[{ toString() { return "5" } }, 5, "object with toString"],
[{ valueOf() { return "8" } }, 8, "object with valueOf"],
[{ toString() { return "5" } }, 5, "object with toString returning string"],
[{ valueOf() { return "8" } }, 8, "object with valueOf returning string"],
[{ toString() { return 6 } }, 6, "object with toString returning number"],
[{ valueOf() { return 9 } }, 9, "object with valueOf returning number"],
];
for (const [value, expected, name = format_value(value)] of valueArguments) {
test(() => {
Expand All @@ -118,6 +119,44 @@ for (const type of ["i32", "f32", "f64"]) {
assert_Global(global, expected);
}, `Explicit value ${name} for type ${type}`);
}

test(() => {
const argument = { "value": type };
assert_throws_js(TypeError, () => new WebAssembly.Global(argument, 0n));
}, `BigInt value for type ${type}`);
}

const valueArguments = [
[undefined, 0n],
[true, 1n],
[false, 0n],
["3", 3n],
[123n, 123n],
[{ toString() { return "5" } }, 5n, "object with toString returning string"],
[{ valueOf() { return "8" } }, 8n, "object with valueOf returning string"],
[{ toString() { return 6n } }, 6n, "object with toString returning bigint"],
[{ valueOf() { return 9n } }, 9n, "object with valueOf returning bigint"],
];
for (const [value, expected, name = format_value(value)] of valueArguments) {
test(() => {
const argument = { "value": "i64" };
const global = new WebAssembly.Global(argument, value);
assert_Global(global, expected);
}, `Explicit value ${name} for type i64`);
}

const invalidBigints = [
null,
666,
{ toString() { return 5 } },
{ valueOf() { return 8 } },
Symbol(),
];
for (const invalidBigint of invalidBigints) {
test(() => {
var argument = { "value": "i64" };
assert_throws_js(TypeError, () => new WebAssembly.Global(argument, invalidBigint));
}, `Pass non-bigint as i64 Global value: ${format_value(invalidBigint)}`);
}

test(() => {
Expand Down
72 changes: 44 additions & 28 deletions test/js-api/global/value-get-set.any.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ test(() => {
}
}, "Branding");

for (const type of ["i32", "f32", "f64"]) {
for (const type of ["i32", "i64", "f32", "f64"]) {
const [initial, value, invalid] = type === "i64" ? [0n, 1n, 2] : [0, 1, 2n];
const immutableOptions = [
[{}, "missing"],
[{ "mutable": undefined }, "undefined"],
Expand All @@ -41,29 +42,29 @@ for (const type of ["i32", "f32", "f64"]) {
test(() => {
opts.value = type;
const global = new WebAssembly.Global(opts);
assert_equals(global.value, 0, "initial value");
assert_equals(global.valueOf(), 0, "initial valueOf");
assert_equals(global.value, initial, "initial value");
assert_equals(global.valueOf(), initial, "initial valueOf");

assert_throws_js(TypeError, () => global.value = 1);
assert_throws_js(TypeError, () => global.value = value);

assert_equals(global.value, 0, "post-set value");
assert_equals(global.valueOf(), 0, "post-set valueOf");
assert_equals(global.value, initial, "post-set value");
assert_equals(global.valueOf(), initial, "post-set valueOf");
}, `Immutable ${type} (${name})`);

test(t => {
opts.value = type;
const global = new WebAssembly.Global(opts);
assert_equals(global.value, 0, "initial value");
assert_equals(global.valueOf(), 0, "initial valueOf");
assert_equals(global.value, initial, "initial value");
assert_equals(global.valueOf(), initial, "initial valueOf");

const value = {
valueOf: t.unreached_func("should not call valueOf"),
toString: t.unreached_func("should not call toString"),
};
assert_throws_js(TypeError, () => global.value = value);

assert_equals(global.value, 0, "post-set value");
assert_equals(global.valueOf(), 0, "post-set valueOf");
assert_equals(global.value, initial, "post-set value");
assert_equals(global.valueOf(), initial, "post-set valueOf");
}, `Immutable ${type} with ToNumber side-effects (${name})`);
}

Expand All @@ -77,34 +78,49 @@ for (const type of ["i32", "f32", "f64"]) {
test(() => {
opts.value = type;
const global = new WebAssembly.Global(opts);
assert_equals(global.value, 0, "initial value");
assert_equals(global.valueOf(), 0, "initial valueOf");
assert_equals(global.value, initial, "initial value");
assert_equals(global.valueOf(), initial, "initial valueOf");

global.value = value;

global.value = 1;
assert_throws_js(TypeError, () => global.value = invalid);

assert_equals(global.value, 1, "post-set value");
assert_equals(global.valueOf(), 1, "post-set valueOf");
assert_equals(global.value, value, "post-set value");
assert_equals(global.valueOf(), value, "post-set valueOf");
}, `Mutable ${type} (${name})`);
}
}

test(() => {
const argument = { "value": "i64", "mutable": true };
const global = new WebAssembly.Global(argument);
assert_throws_js(TypeError, () => global.value);
assert_throws_js(TypeError, () => global.value = 0);
assert_throws_js(TypeError, () => global.valueOf());
}, "i64 with default");

test(t => {
const argument = { "value": "i64", "mutable": true };
const global = new WebAssembly.Global(argument);
const value = {
valueOf: t.unreached_func("should not call valueOf"),
toString: t.unreached_func("should not call toString"),
};
assert_throws_js(TypeError, () => global.value = value);
}, "i64 with ToNumber side-effects");
assert_equals(global.value, 0n, "initial value using ToJSValue");

const valid = [
[123n, 123n],
[2n ** 63n, - (2n ** 63n)],
[true, 1n],
[false, 0n],
["456", 456n],
];
for (const [input, output] of valid) {
global.value = input;
assert_equals(global.valueOf(), output, "post-set valueOf");
}

const invalid = [
undefined,
null,
0,
1,
4.2,
Symbol(),
];
for (const input of invalid) {
assert_throws_js(TypeError, () => global.value = input);
}
}, "i64 mutability");

test(() => {
const argument = { "value": "i32", "mutable": true };
Expand Down
Loading

0 comments on commit 1411462

Please sign in to comment.