[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: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55777}
This commit is contained in:
Michael Starzinger 2018-09-11 10:38:24 +02:00 committed by Commit Bot
parent 7d98d8e01d
commit 8238a9b245
6 changed files with 166 additions and 3 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -0,0 +1,98 @@
// 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);
})();

View File

@ -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);
}

View File

@ -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)