Bug 1651725 - Add 'minimum' argument to WASM's Memory & Table r=lth

This adds a 'minimum' argument to the JS-API for the WebAssembly.Memory
and WebAssembly.Table objects. This parameter represents the minimum
size of the object which was specified by the 'initial' parameter. This
supports using either initial or minimum to specify the parameter.

This commit is part of the work to add type reflections to the
WebAssembly JS-API as specified in the js-types proposal.

Differential Revision: https://phabricator.services.mozilla.com/D85887
This commit is contained in:
Jessica Tallon 2020-09-21 11:01:19 +00:00
parent c9e95718ff
commit 1ab5dc76df
10 changed files with 126 additions and 13 deletions

View File

@ -763,3 +763,17 @@ def can_build_data_file(yasm, gnu_as, target, compile_environment):
if not yasm and not gnu_as: if not yasm and not gnu_as:
die('Building ICU requires either yasm or a GNU assembler. If you do not have ' die('Building ICU requires either yasm or a GNU assembler. If you do not have '
'either of those available for this platform you must use --without-intl-api') 'either of those available for this platform you must use --without-intl-api')
# Initial support for WebAssembly JS-API Type Reflections
# =======================================================
@depends(milestone.is_nightly)
def default_wasm_type_reflections(is_nightly):
return is_nightly
js_option('--enable-wasm-type-reflections',
default=default_wasm_type_reflections,
help='{Enable|Disable} type reflection in WASM JS-API')
set_config('ENABLE_WASM_TYPE_REFLECTIONS', depends_if('--enable-wasm-type-reflections')(lambda x: True))
set_define('ENABLE_WASM_TYPE_REFLECTIONS', depends_if('--enable-wasm-type-reflections')(lambda x: True))

View File

@ -454,6 +454,9 @@ MSG_DEF(JSMSG_WASM_TYPEREF_FROM_JS, 0, JSEXN_TYPEERR, "conversion from Ja
MSG_DEF(JSMSG_WASM_TYPEREF_TO_JS, 0, JSEXN_TYPEERR, "conversion from WebAssembly typed ref to JavaScript value unimplemented") MSG_DEF(JSMSG_WASM_TYPEREF_TO_JS, 0, JSEXN_TYPEERR, "conversion from WebAssembly typed ref to JavaScript value unimplemented")
MSG_DEF(JSMSG_WASM_WRONG_NUMBER_OF_VALUES, 2, JSEXN_TYPEERR, "wrong number of values returned by JavaScript to WebAssembly (expected {0}, got {1})") MSG_DEF(JSMSG_WASM_WRONG_NUMBER_OF_VALUES, 2, JSEXN_TYPEERR, "wrong number of values returned by JavaScript to WebAssembly (expected {0}, got {1})")
MSG_DEF(JSMSG_WASM_NONSHARED_WAIT , 0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory") MSG_DEF(JSMSG_WASM_NONSHARED_WAIT , 0, JSEXN_WASMRUNTIMEERROR, "atomic wait on non-shared memory")
MSG_DEF(JSMSG_WASM_NULL_REQUIRED, 0, JSEXN_TYPEERR, "nullref requires a null value")
MSG_DEF(JSMSG_WASM_SUPPLY_ONLY_ONE, 2, JSEXN_TYPEERR, "exactly one of {0} and {1} must be supplied")
MSG_DEF(JSMSG_WASM_MISSING_REQUIRED, 1, JSEXN_TYPEERR, "Missing required argument {0}")
// Proxy // Proxy
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value") MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")

View File

@ -912,7 +912,8 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
} }
uint32_t initial = 0; uint32_t initial = 0;
if (!EnforceRangeU32(cx, initialVal, kind, "initial size", &initial)) { if (!initialVal.isUndefined() &&
!EnforceRangeU32(cx, initialVal, kind, "initial size", &initial)) {
return false; return false;
} }
limits->initial = initial; limits->initial = initial;
@ -923,6 +924,30 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
return false; return false;
} }
#ifdef ENABLE_WASM_TYPE_REFLECTIONS
// Get minimum parameter.
JSAtom* minimumAtom = Atomize(cx, "minimum", strlen("minimum"));
if (!minimumAtom) {
return false;
}
RootedId minimumId(cx, AtomToId(minimumAtom));
RootedValue minimumVal(cx);
if (!GetProperty(cx, obj, obj, minimumId, &minimumVal)) {
return false;
}
uint32_t minimum = 0;
if (!minimumVal.isUndefined() &&
!EnforceRangeU32(cx, minimumVal, kind, "initial size", &minimum)) {
return false;
}
if (!minimumVal.isUndefined()) {
limits->initial = minimum;
}
#endif
// Get maximum parameter.
JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum")); JSAtom* maximumAtom = Atomize(cx, "maximum", strlen("maximum"));
if (!maximumAtom) { if (!maximumAtom) {
return false; return false;
@ -986,6 +1011,21 @@ static bool GetLimits(JSContext* cx, HandleObject obj, uint32_t maximumField,
} }
} }
#ifdef ENABLE_WASM_TYPE_REFLECTIONS
// Check both minimum and initial are not supplied.
if (minimumVal.isUndefined() == initialVal.isUndefined()) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_SUPPLY_ONLY_ONE, "minimum", "initial");
return false;
}
#else
if (initialVal.isUndefined()) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_MISSING_REQUIRED, "initial");
return false;
}
#endif
return true; return true;
} }

View File

@ -0,0 +1,3 @@
[constructor-types.tentative.any.js]
expected:
if release_or_beta: FAIL

View File

@ -0,0 +1,3 @@
[constructor-types.tentative.any.js]
expected:
if release_or_beta: FAIL

View File

@ -0,0 +1,20 @@
// META: global=jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/memory/assertions.js
test(() => {
const argument = { initial: 5, minimum: 6 };
assert_throws_js(TypeError, () => new WebAssembly.Memory(argument));
}, "Initializing with both initial and minimum");
test(() => {
const argument = { minimum: 0 };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 0 });
}, "Zero minimum");
test(() => {
const argument = { minimum: 4 };
const memory = new WebAssembly.Memory(argument);
assert_Memory(memory, { "size": 4 });
}, "Non-zero minimum");

View File

@ -72,7 +72,16 @@ test(() => {
assert_unreached(`Should not call [[HasProperty]] with ${x}`); assert_unreached(`Should not call [[HasProperty]] with ${x}`);
}, },
get(o, x) { get(o, x) {
return 0; // Due to the requirement not to supply both minimum and initial, we need to ignore one of them.
switch (x) {
case "shared":
return false;
case "minimum":
case "maximum":
return 0;
default:
return undefined;
}
}, },
}); });
new WebAssembly.Memory(proxy); new WebAssembly.Memory(proxy);

View File

@ -11,3 +11,14 @@ function assert_equal_to_array(table, expected, message) {
assert_throws_js(RangeError, () => table.get(expected.length + 1), assert_throws_js(RangeError, () => table.get(expected.length + 1),
`${message}: table.get(${expected.length + 1} of ${expected.length})`); `${message}: table.get(${expected.length + 1} of ${expected.length})`);
} }
function assert_Table(actual, expected) {
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
"prototype");
assert_true(Object.isExtensible(actual), "extensible");
assert_equals(actual.length, expected.length, "length");
for (let i = 0; i < expected.length; ++i) {
assert_equals(actual.get(i), null, `actual.get(${i})`);
}
}

View File

@ -0,0 +1,20 @@
// META: global=jsshell
// META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/table/assertions.js
test(() => {
const argument = { "element": "anyfunc", "initial": 0, "minimum": 0 };
assert_throws_js(TypeError, () => WebAssembly.Table(argument));
}, "Supplying both initial and minimum");
test(() => {
const argument = { "element": "anyfunc", "minimum": 0 };
const table = new WebAssembly.Table(argument);
assert_Table(table, { "length": 0 });
}, "Basic (zero, minimum)");
test(() => {
const argument = { "element": "anyfunc", "minimum": 5 };
const table = new WebAssembly.Table(argument);
assert_Table(table, { "length": 5 });
}, "Basic (non-zero, minimum)");

View File

@ -1,16 +1,6 @@
// META: global=window,dedicatedworker,jsshell // META: global=window,dedicatedworker,jsshell
// META: script=/wasm/jsapi/assertions.js // META: script=/wasm/jsapi/assertions.js
// META: script=/wasm/jsapi/table/assertions.js
function assert_Table(actual, expected) {
assert_equals(Object.getPrototypeOf(actual), WebAssembly.Table.prototype,
"prototype");
assert_true(Object.isExtensible(actual), "extensible");
assert_equals(actual.length, expected.length, "length");
for (let i = 0; i < expected.length; ++i) {
assert_equals(actual.get(i), null, `actual.get(${i})`);
}
}
test(() => { test(() => {
assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table"); assert_function_name(WebAssembly.Table, "Table", "WebAssembly.Table");