Commit f420a264 authored by Thibaud Michaud's avatar Thibaud Michaud Committed by V8 LUCI CQ

[wasm][eh] Add WebAssembly.Exception constructor

WebAssembly.Exception is the static representation of a wasm exception.
It holds the signature and the tag of the exception, can be imported and
exported from a wasm module, and will eventually allow inspecting a
wasm-thrown exception from JS.

R=clemensb@chromium.org

Bug: v8:8091
Change-Id: Ided352777e1217e6f873b84a2fc21c3acf59ff6c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2966384Reviewed-by: 's avatarClemens Backes <clemensb@chromium.org>
Commit-Queue: Thibaud Michaud <thibaudm@chromium.org>
Cr-Commit-Position: refs/heads/master@{#75214}
parent 9caf26b9
......@@ -1396,15 +1396,6 @@ void WebAssemblyGlobal(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(Utils::ToLocal(global_js_object));
}
// WebAssembly.Exception
void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
HandleScope scope(isolate);
ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception()");
thrower.TypeError("WebAssembly.Exception cannot be called");
}
namespace {
uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context,
......@@ -1420,6 +1411,69 @@ uint32_t GetIterableLength(i::Isolate* isolate, Local<Context> context,
} // namespace
// WebAssembly.Exception
void WebAssemblyException(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
HandleScope scope(isolate);
ScheduledErrorThrower thrower(i_isolate, "WebAssembly.Exception()");
if (!args.IsConstructCall()) {
thrower.TypeError("WebAssembly.Exception must be invoked with 'new'");
return;
}
if (!args[0]->IsObject()) {
thrower.TypeError("Argument 0 must be an exception type");
return;
}
Local<Object> event_type = Local<Object>::Cast(args[0]);
Local<Context> context = isolate->GetCurrentContext();
auto enabled_features = i::wasm::WasmFeatures::FromIsolate(i_isolate);
// Load the 'parameters' property of the event type.
Local<String> parameters_key = v8_str(isolate, "parameters");
v8::MaybeLocal<v8::Value> parameters_maybe =
event_type->Get(context, parameters_key);
v8::Local<v8::Value> parameters_value;
if (!parameters_maybe.ToLocal(&parameters_value) ||
!parameters_value->IsObject()) {
thrower.TypeError("Argument 0 must be an exception type with 'parameters'");
return;
}
Local<Object> parameters = parameters_value.As<Object>();
uint32_t parameters_len = GetIterableLength(i_isolate, context, parameters);
if (parameters_len == i::kMaxUInt32) {
thrower.TypeError("Argument 0 contains parameters without 'length'");
return;
}
if (parameters_len > i::wasm::kV8MaxWasmFunctionParams) {
thrower.TypeError("Argument 0 contains too many parameters");
return;
}
// Decode the exception type and construct a signature.
std::vector<i::wasm::ValueType> param_types(parameters_len,
i::wasm::kWasmVoid);
for (uint32_t i = 0; i < parameters_len; ++i) {
i::wasm::ValueType& type = param_types[i];
MaybeLocal<Value> maybe = parameters->Get(context, i);
if (!GetValueType(isolate, maybe, context, &type, enabled_features) ||
type == i::wasm::kWasmVoid) {
thrower.TypeError(
"Argument 0 parameter type at index #%u must be a value type", i);
return;
}
}
const i::wasm::FunctionSig sig{0, parameters_len, param_types.data()};
// Set the tag index to 0. It is only used for debugging purposes, and has no
// meaningful value when declared outside of a wasm module.
auto tag = i::WasmExceptionTag::New(i_isolate, 0);
i::Handle<i::Object> exception =
i::WasmExceptionObject::New(i_isolate, &sig, tag);
args.GetReturnValue().Set(Utils::ToLocal(exception));
}
// WebAssembly.Function
void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::Isolate* isolate = args.GetIsolate();
......@@ -1443,8 +1497,8 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
v8::MaybeLocal<v8::Value> parameters_maybe =
function_type->Get(context, parameters_key);
v8::Local<v8::Value> parameters_value;
if (!parameters_maybe.ToLocal(&parameters_value)) return;
if (!parameters_value->IsObject()) {
if (!parameters_maybe.ToLocal(&parameters_value) ||
!parameters_value->IsObject()) {
thrower.TypeError("Argument 0 must be a function type with 'parameters'");
return;
}
......@@ -1486,8 +1540,8 @@ void WebAssemblyFunction(const v8::FunctionCallbackInfo<v8::Value>& args) {
for (uint32_t i = 0; i < parameters_len; ++i) {
i::wasm::ValueType type;
MaybeLocal<Value> maybe = parameters->Get(context, i);
if (!GetValueType(isolate, maybe, context, &type, enabled_features)) return;
if (type == i::wasm::kWasmVoid) {
if (!GetValueType(isolate, maybe, context, &type, enabled_features) ||
type == i::wasm::kWasmVoid) {
thrower.TypeError(
"Argument 0 parameter type at index #%u must be a value type", i);
return;
......
// Copyright 2021 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --experimental-wasm-eh
load("test/mjsunit/wasm/wasm-module-builder.js");
(function TestImport() {
print(arguments.callee.name);
assertThrows(() => new WebAssembly.Exception(), TypeError,
/Argument 0 must be an exception type/);
assertThrows(() => new WebAssembly.Exception({}), TypeError,
/Argument 0 must be an exception type with 'parameters'/);
assertThrows(() => new WebAssembly.Exception({parameters: ['foo']}), TypeError,
/Argument 0 parameter type at index #0 must be a value type/);
assertThrows(() => new WebAssembly.Exception({parameters: {}}), TypeError,
/Argument 0 contains parameters without 'length'/);
let js_except_i32 = new WebAssembly.Exception({parameters: ['i32']});
let js_except_v = new WebAssembly.Exception({parameters: []});
let builder = new WasmModuleBuilder();
builder.addImportedException("m", "ex", kSig_v_i);
assertDoesNotThrow(() => builder.instantiate({ m: { ex: js_except_i32 }}));
assertThrows(
() => builder.instantiate({ m: { ex: js_except_v }}), WebAssembly.LinkError,
/imported exception does not match the expected type/);
assertThrows(
() => builder.instantiate({ m: { ex: js_except_v }}), WebAssembly.LinkError,
/imported exception does not match the expected type/);
})();
(function TestExport() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex", kExternalException, except);
let instance = builder.instantiate();
assertTrue(Object.prototype.hasOwnProperty.call(instance.exports, 'ex'));
assertEquals("object", typeof instance.exports.ex);
assertInstanceof(instance.exports.ex, WebAssembly.Exception);
assertSame(instance.exports.ex.constructor, WebAssembly.Exception);
})();
(function TestImportExport() {
print(arguments.callee.name);
let js_ex_i32 = new WebAssembly.Exception({parameters: ['i32']});
let builder = new WasmModuleBuilder();
let index = builder.addImportedException("m", "ex", kSig_v_i);
builder.addExportOfKind("ex", kExternalException, index);
let instance = builder.instantiate({ m: { ex: js_ex_i32 }});
let res = instance.exports.ex;
assertEquals(res, js_ex_i32);
})();
......@@ -6,19 +6,6 @@
d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
(function TestExportSimple() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex", kExternalException, except);
let instance = builder.instantiate();
assertTrue(Object.prototype.hasOwnProperty.call(instance.exports, 'ex'));
assertEquals("object", typeof instance.exports.ex);
assertInstanceof(instance.exports.ex, WebAssembly.Exception);
assertSame(instance.exports.ex.constructor, WebAssembly.Exception);
})();
(function TestExportMultiple() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
......@@ -68,14 +55,3 @@ d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js");
let exports = WebAssembly.Module.exports(module);
assertArrayEquals([{ name: "ex", kind: "exception" }], exports);
})();
(function TestConstructorNonCallable() {
print(arguments.callee.name);
// TODO(wasm): Currently the constructor function of an exported exception is
// not callable. This can/will change once the proposal matures, at which
// point we should add a full exceptions-api.js test suite for the API and
// remove this test case from this file.
assertThrows(
() => WebAssembly.Exception(), TypeError,
/WebAssembly.Exception cannot be called/);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment