Bug 1703089 - Implement WebAssembly.Exception constructor r=rhunt

Differential Revision: https://phabricator.services.mozilla.com/D110851
This commit is contained in:
Asumu Takikawa 2021-07-21 19:17:52 +00:00
Родитель 8ed84b3bce
Коммит 87dd52d7c1
4 изменённых файлов: 259 добавлений и 11 удалений

Просмотреть файл

@ -465,6 +465,9 @@ MSG_DEF(JSMSG_WASM_BAD_EQREF_VALUE, 0, JSEXN_TYPEERR, "can only pass a Type
MSG_DEF(JSMSG_WASM_BAD_VAL_TYPE, 0, JSEXN_TYPEERR, "cannot pass v128 to or from JS")
MSG_DEF(JSMSG_WASM_BAD_STRING_VAL_TYPE, 0, JSEXN_TYPEERR, "bad value type")
MSG_DEF(JSMSG_WASM_BAD_STRING_IDX_TYPE, 0, JSEXN_TYPEERR, "bad index type")
MSG_DEF(JSMSG_WASM_BAD_EXN_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Tag")
MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD, 0, JSEXN_TYPEERR, "second argument must be an object")
MSG_DEF(JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, 2, JSEXN_TYPEERR, "expected {0} values but got {1}")
MSG_DEF(JSMSG_WASM_NO_TRANSFER, 0, JSEXN_TYPEERR, "cannot transfer WebAssembly/asm.js ArrayBuffer")
MSG_DEF(JSMSG_WASM_TEXT_FAIL, 1, JSEXN_SYNTAXERR, "wasm text error: {0}")
MSG_DEF(JSMSG_WASM_MISSING_MAXIMUM, 0, JSEXN_TYPEERR, "'shared' is true but maximum is not specified")

Просмотреть файл

@ -8,12 +8,6 @@ function testException() {
WebAssembly.RuntimeError,
/cannot call WebAssembly.Tag/
);
assertErrorMessage(
() => new WebAssembly.Exception(),
WebAssembly.RuntimeError,
/cannot call WebAssembly.Exception/
);
}
function testImports() {

Просмотреть файл

@ -0,0 +1,173 @@
// Tests for wasm exception proposal JS API features.
load(libdir + "eqArrayHelper.js");
assertErrorMessage(
() => WebAssembly.Exception(),
TypeError,
/calling a builtin Exception constructor without new is forbidden/
);
assertErrorMessage(
() => new WebAssembly.Exception(),
TypeError,
/At least 2 arguments required/
);
assertErrorMessage(
() => new WebAssembly.Exception(3, []),
TypeError,
/first argument must be a WebAssembly.Tag/
);
const { tag1, tag2, tag3, tag4, tag5, tag6, tag7 } = wasmEvalText(
`(module
(event (export "tag1") (param))
(event (export "tag2") (param i32))
(event (export "tag3") (param i32 f32))
(event (export "tag4") (param i32 externref i32))
(event (export "tag5") (param i32 externref i32 externref))
(event (export "tag6") (param funcref))
(event (export "tag7") (param i64)))`
).exports;
new WebAssembly.Exception(tag1, []);
new WebAssembly.Exception(tag2, [3]);
new WebAssembly.Exception(tag3, [3, 5.5]);
new WebAssembly.Exception(tag4, [3, "foo", 4]);
new WebAssembly.Exception(tag5, [3, "foo", 4, "bar"]);
assertErrorMessage(
() => new WebAssembly.Exception(tag2, []),
TypeError,
/expected 1 values but got 0/
);
assertErrorMessage(
() => new WebAssembly.Exception(tag2, [3n]),
TypeError,
/can't convert BigInt to number/
);
assertErrorMessage(
() => new WebAssembly.Exception(tag6, [undefined]),
TypeError,
/can only pass WebAssembly exported functions to funcref/
);
assertErrorMessage(
() => new WebAssembly.Exception(tag7, [undefined]),
TypeError,
/can't convert undefined to BigInt/
);
assertErrorMessage(
() => new WebAssembly.Exception(tag7, {}),
TypeError,
/\({}\) is not iterable/
);
assertErrorMessage(
() => new WebAssembly.Exception(tag7, 1),
TypeError,
/second argument must be an object/
);
// Test throwing a JS constructed exception to Wasm.
assertEq(
wasmEvalText(
`(module
(import "m" "exn" (event $exn (param i32)))
(import "m" "f" (func $f))
(func (export "f") (result i32)
try (result i32)
call $f
(i32.const 0)
catch $exn
end))`,
{
m: {
exn: tag2,
f: () => {
throw new WebAssembly.Exception(tag2, [42]);
},
},
}
).exports.f(),
42
);
assertEqArray(
wasmEvalText(
`(module
(import "m" "exn" (event $exn (param i32 f32)))
(import "m" "f" (func $f))
(func (export "f") (result i32 f32)
try (result i32 f32)
call $f
(i32.const 0)
(f32.const 0)
catch $exn
end))`,
{
m: {
exn: tag3,
f: () => {
throw new WebAssembly.Exception(tag3, [42, 5.5]);
},
},
}
).exports.f(),
[42, 5.5]
);
assertEqArray(
wasmEvalText(
`(module
(import "m" "exn" (event $exn (param i32 externref i32)))
(import "m" "f" (func $f))
(func (export "f") (result i32 externref i32)
try (result i32 externref i32)
call $f
(i32.const 0)
(ref.null extern)
(i32.const 0)
catch $exn
end))`,
{
m: {
exn: tag4,
f: () => {
throw new WebAssembly.Exception(tag4, [42, "foo", 42]);
},
},
}
).exports.f(),
[42, "foo", 42]
);
assertEq(
wasmEvalText(
`(module
(import "m" "exn" (event $exn))
(import "m" "f" (func $f))
(func (export "f") (result i32)
try (result i32)
call $f
(i32.const 0)
catch $exn
(i32.const 0)
catch_all
(i32.const 1)
end))`,
{
m: {
exn: tag1,
f: () => {
throw new WebAssembly.Exception(tag2, [42]);
},
},
}
).exports.f(),
1
);

Просмотреть файл

@ -3584,6 +3584,8 @@ void WasmTagObject::finalize(JSFreeOp* fop, JSObject* obj) {
}
}
static bool IsTagObject(JSObject* obj) { return obj->is<WasmTagObject>(); }
bool WasmTagObject::construct(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
@ -3710,14 +3712,90 @@ bool WasmExceptionObject::construct(JSContext* cx, unsigned argc, Value* vp) {
return false;
}
// FIXME: When the JS API is finalized, it may be possible to construct
// WebAssembly.Exception instances from JS, but not for now.
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_EXN_CONSTRUCTOR, "WebAssembly.Exception");
if (!args.requireAtLeast(cx, "WebAssembly.Exception", 2)) {
return false;
}
if (!args[0].isObject() || !IsTagObject(&args[0].toObject())) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_EXN_ARG);
return false;
}
RootedWasmTagObject exnTag(cx, &args[0].toObject().as<WasmTagObject>());
if (!args.get(1).isObject()) {
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_EXN_PAYLOAD);
return false;
}
JS::ForOfIterator iterator(cx);
if (!iterator.init(args.get(1), JS::ForOfIterator::ThrowOnNonIterable)) {
return false;
}
wasm::ValTypeVector& params = exnTag->valueTypes();
// This is pre-sizing the data buffer for the exception object.
size_t nbytes = 0;
for (const ValType param : params) {
if (!param.isReference()) {
nbytes += SizeOf(param);
}
}
RootedArrayBufferObject buf(cx, ArrayBufferObject::createZeroed(cx, nbytes));
if (!buf) {
return false;
}
RootedArrayObject refs(cx, NewDenseEmptyArray(cx));
if (!refs) {
return false;
}
uint8_t* bufPtr = buf->dataPointer();
RootedValue nextArg(cx);
for (size_t i = 0; i < params.length(); i++) {
bool done;
if (!iterator.next(&nextArg, &done)) {
return false;
}
if (done) {
UniqueChars expected(JS_smprintf("%zu", params.length()));
UniqueChars got(JS_smprintf("%zu", i));
JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr,
JSMSG_WASM_BAD_EXN_PAYLOAD_LEN, expected.get(),
got.get());
return false;
}
if (params[i].isReference()) {
RootedObject objPtr(cx);
if (!ToWebAssemblyValue(cx, nextArg, params[i], objPtr.address(), true)) {
return false;
}
if (!NewbornArrayPush(cx, refs, ObjectValue(*objPtr))) {
return false;
}
} else {
if (!ToWebAssemblyValue(cx, nextArg, params[i], bufPtr, true)) {
return false;
}
bufPtr += SizeOf(params[i]);
}
}
RootedWasmExceptionObject exnObj(
cx, WasmExceptionObject::create(cx, SharedExceptionTag(&exnTag->tag()),
buf, refs));
args.rval().setObject(*exnObj);
return true;
}
/* static */
WasmExceptionObject* WasmExceptionObject::create(JSContext* cx,
wasm::SharedExceptionTag tag,