Commit 8238a9b2 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Add preliminary support for imported exceptions.

This adds the ability to import exception into a module at instantiation
time. Only a {WasmExceptionObject} that has been exported by another
module instance can be imported, all other values are rejected.

Note that currently there is no signature check being performed to make
sure the imported exception matches the expected type. Also the identity
of imported exceptions is not yet preserved.

Furthermore the engine does not yet match thrown exception objects on a
global level across modules. Hence imported exceptions will (wrongly)
behave as completely new types within the module.

R=clemensh@chromium.org
TEST=mjsunit/wasm/exceptions-import,unittests/WasmModuleVerifyTest
BUG=v8:8091

Change-Id: If247762b949a1ba4a87d13bc3e790a45dbc67815
Reviewed-on: https://chromium-review.googlesource.com/1216402
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55777}
parent 7d98d8e0
......@@ -1745,6 +1745,17 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
}
break;
}
case kExternalException: {
if (!value->IsWasmExceptionObject()) {
ReportLinkError("exception import requires a WebAssembly.Exception",
index, module_name, import_name);
return -1;
}
// TODO(mstarzinger): Actually add imported exceptions to the instance
// exception table, making sure to preserve object identity. Also check
// against the expected signature.
break;
}
default:
UNREACHABLE();
break;
......
......@@ -525,6 +525,17 @@ class ModuleDecoderImpl : public Decoder {
}
break;
}
case kExternalException: {
// ===== Imported exception ======================================
if (!enabled_features_.eh) {
errorf(pos, "unknown import kind 0x%02x", import->kind);
break;
}
import->index = static_cast<uint32_t>(module_->exceptions.size());
module_->exceptions.emplace_back(
consume_exception_sig(module_->signature_zone.get()));
break;
}
default:
errorf(pos, "unknown import kind 0x%02x", import->kind);
break;
......
......@@ -106,6 +106,7 @@ Handle<JSArray> GetImports(Isolate* isolate,
Handle<String> table_string = factory->InternalizeUtf8String("table");
Handle<String> memory_string = factory->InternalizeUtf8String("memory");
Handle<String> global_string = factory->InternalizeUtf8String("global");
Handle<String> exception_string = factory->InternalizeUtf8String("exception");
// Create the result array.
const WasmModule* module = module_object->module();
......@@ -138,6 +139,9 @@ Handle<JSArray> GetImports(Isolate* isolate,
case kExternalGlobal:
import_kind = global_string;
break;
case kExternalException:
import_kind = exception_string;
break;
default:
UNREACHABLE();
}
......
// Copyright 2018 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: --expose-wasm --experimental-wasm-eh
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
// Helper function to return a new exported exception with the {kSig_v_v} type
// signature from an anonymous module. The underlying module is thrown away.
// This allows tests to reason solely about importing exceptions.
function NewExportedException() {
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex", kExternalException, except);
let instance = builder.instantiate();
return instance.exports.ex;
}
(function TestImportSimple() {
print(arguments.callee.name);
let exported = NewExportedException();
let builder = new WasmModuleBuilder();
let except = builder.addImportedException("m", "ex", kSig_v_v);
assertDoesNotThrow(() => builder.instantiate({ m: { ex: exported }}));
})();
(function TestImportMultiple() {
print(arguments.callee.name);
let exported = NewExportedException();
let builder = new WasmModuleBuilder();
let except1 = builder.addImportedException("m", "ex1", kSig_v_v);
let except2 = builder.addImportedException("m", "ex2", kSig_v_v);
let except3 = builder.addException(kSig_v_v);
builder.addExportOfKind("ex2", kExternalException, except2);
builder.addExportOfKind("ex3", kExternalException, except3);
let instance = builder.instantiate({ m: { ex1: exported, ex2: exported }});
assertTrue(except1 < except3 && except2 < except3);
assertEquals(undefined, instance.exports.ex1);
// TODO(mstarzinger): Enable once identity of imported exception is preserved.
//assertSame(exported, instance.exports.ex2);
assertNotSame(exported, instance.exports.ex3);
})();
(function TestImportMissing() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addImportedException("m", "ex", kSig_v_v);
assertThrows(
() => builder.instantiate({}), TypeError,
/module is not an object or function/);
assertThrows(
() => builder.instantiate({ m: {}}), WebAssembly.LinkError,
/exception import requires a WebAssembly.Exception/);
})();
(function TestImportValueMismatch() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addImportedException("m", "ex", kSig_v_v);
assertThrows(
() => builder.instantiate({ m: { ex: 23 }}), WebAssembly.LinkError,
/exception import requires a WebAssembly.Exception/);
assertThrows(
() => builder.instantiate({ m: { ex: {} }}), WebAssembly.LinkError,
/exception import requires a WebAssembly.Exception/);
var monkey = Object.create(NewExportedException());
assertThrows(
() => builder.instantiate({ m: { ex: monkey }}), WebAssembly.LinkError,
/exception import requires a WebAssembly.Exception/);
})();
(function TestImportSignatureMismatch() {
print(arguments.callee.name);
let exported = NewExportedException();
let builder = new WasmModuleBuilder();
let except = builder.addImportedException("m", "ex", kSig_v_i);
// TODO(mstarzinger): Enable once proper signature check is performed.
//assertThrows(
// () => builder.instantiate({ m: { ex: exported }}), WebAssembly.LinkError,
// /imported exception does not match expected type/);
})();
(function TestImportModuleGetImports() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addImportedException("m", "ex", kSig_v_v);
let module = new WebAssembly.Module(builder.toBuffer());
let imports = WebAssembly.Module.imports(module);
assertArrayEquals([{ module: "m", name: "ex", kind: "exception" }], imports);
})();
......@@ -179,6 +179,7 @@ class WasmModuleBuilder {
this.explicit = [];
this.num_imported_funcs = 0;
this.num_imported_globals = 0;
this.num_imported_exceptions = 0;
return this;
}
......@@ -228,10 +229,12 @@ class WasmModuleBuilder {
}
addException(type) {
if (type.results.length != 0)
throw new Error('Invalid exception signature: ' + type);
if (type.results.length != 0) {
throw new Error('Exception signature must have void result: ' + type);
}
let except_index = this.exceptions.length + this.num_imported_exceptions;
this.exceptions.push(type);
return this.exceptions.length - 1;
return except_index;
}
addFunction(name, type) {
......@@ -269,6 +272,18 @@ class WasmModuleBuilder {
this.imports.push(o);
}
addImportedException(module = "", name, type) {
if (type.results.length != 0) {
throw new Error('Exception signature must have void result: ' + type);
}
if (this.exceptions.length != 0) {
throw new Error('Imported exceptions must be declared before local ones');
}
let o = {module: module, name: name, kind: kExternalException, type: type};
this.imports.push(o);
return this.num_imported_exceptions++;
}
addExport(name, index) {
this.exports.push({name: name, kind: kExternalFunction, index: index});
return this;
......@@ -378,6 +393,11 @@ class WasmModuleBuilder {
section.emit_u8(has_max ? 1 : 0); // flags
section.emit_u32v(imp.initial); // initial
if (has_max) section.emit_u32v(imp.maximum); // maximum
} else if (imp.kind == kExternalException) {
section.emit_u32v(imp.type.params.length);
for (let param of imp.type.params) {
section.emit_u8(param);
}
} else {
throw new Error("unknown/unsupported import kind " + imp.kind);
}
......
......@@ -551,6 +551,25 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeImport) {
EXPECT_FALSE(result.ok());
}
TEST_F(WasmModuleVerifyTest, ExceptionImport) {
static const byte data[] = {SECTION(Import, 9), // section header
1, // number of imports
NAME_LENGTH(1), // --
'm', // module name
NAME_LENGTH(2), // --
'e', 'x', // exception name
kExternalException, // import kind
// except[0] (i32)
1, kLocalI32};
FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(1u, result.val->exceptions.size());
EXPECT_EQ(1u, result.val->import_table.size());
}
TEST_F(WasmModuleVerifyTest, ExceptionExport) {
static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
// except[0] (i32)
......
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