Commit c0a9f50c authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[wasm] Add preliminary support for exported exceptions.

This adds the ability to add exception types to the export section of a
module and reference them via the local exception index. Currently the
export object then just contains the local index as a number, which is
only temporary until we have proper export wrappers for exceptions.

Also note that this tightens the restriction for the modules exception
section to be located in between the import and the export section.

R=clemensh@chromium.org
TEST=mjsunit/wasm/exceptions-export
BUG=v8:8091

Change-Id: Ie26081c3f94e71cb576057db7e45ec5bd0e112f9
Reviewed-on: https://chromium-review.googlesource.com/1206873
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Reviewed-by: 's avatarClemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55679}
parent 4b4c46b3
......@@ -2014,6 +2014,13 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
}
break;
}
case kExternalException: {
const WasmException& exception = module_->exceptions[exp.index];
// TODO(mstarzinger): Only temporary placeholder, use wrapper object.
desc.set_value(handle(Smi::FromInt(exp.index), isolate_));
USE(exception);
break;
}
default:
UNREACHABLE();
break;
......
......@@ -48,6 +48,8 @@ const char* ExternalKindName(ImportExportKindCode kind) {
return "memory";
case kExternalGlobal:
return "global";
case kExternalException:
return "exception";
}
return "unknown";
}
......@@ -337,7 +339,7 @@ class ModuleDecoderImpl : public Decoder {
static_cast<const void*>(bytes.end()));
// Check if the section is out-of-order.
if (section_code < next_section_) {
if (section_code < next_ordered_section_) {
errorf(pc(), "unexpected section: %s", SectionName(section_code));
return;
}
......@@ -346,19 +348,21 @@ class ModuleDecoderImpl : public Decoder {
case kUnknownSectionCode:
break;
case kExceptionSectionCode:
// Note: kExceptionSectionCode > kCodeSectionCode, but must appear
// before the code section. Hence, treat it as a special case.
// Note: kExceptionSectionCode > kExportSectionCode, but must appear
// before the export (and code) section, as well as after the import
// section. Hence, treat it as a special case.
if (++number_of_exception_sections > 1) {
errorf(pc(), "Multiple exception sections not allowed");
return;
} else if (next_section_ > kCodeSectionCode) {
errorf(pc(), "Exception section must appear before the code section");
} else if (next_ordered_section_ > kExportSectionCode) {
errorf(pc(), "Exception section must appear before export section");
return;
} else if (next_ordered_section_ < kImportSectionCode) {
next_ordered_section_ = kImportSectionCode + 1;
}
break;
default:
next_section_ = section_code;
++next_section_;
next_ordered_section_ = section_code + 1;
break;
}
......@@ -655,6 +659,15 @@ class ModuleDecoderImpl : public Decoder {
}
break;
}
case kExternalException: {
if (!enabled_features_.eh) {
errorf(pos, "invalid export kind 0x%02x", exp->kind);
break;
}
WasmException* exception = nullptr;
exp->index = consume_exception_index(module_.get(), &exception);
break;
}
default:
errorf(pos, "invalid export kind 0x%02x", exp->kind);
break;
......@@ -935,12 +948,13 @@ class ModuleDecoderImpl : public Decoder {
std::shared_ptr<WasmModule> module_;
Counters* counters_ = nullptr;
// The type section is the first section in a module.
uint8_t next_section_ = kFirstSectionInModule;
uint8_t next_ordered_section_ = kFirstSectionInModule;
uint32_t number_of_exception_sections = 0;
// We store next_section_ as uint8_t instead of SectionCode so that we can
// increment it. This static_assert should make sure that SectionCode does not
// get bigger than uint8_t accidentially.
static_assert(sizeof(ModuleDecoderImpl::next_section_) == sizeof(SectionCode),
// We store next_ordered_section_ as uint8_t instead of SectionCode so that we
// can increment it. This static_assert should make sure that SectionCode does
// not get bigger than uint8_t accidentially.
static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) ==
sizeof(SectionCode),
"type mismatch");
Result<bool> intermediate_result_;
ModuleOrigin origin_;
......@@ -1108,6 +1122,10 @@ class ModuleDecoderImpl : public Decoder {
return consume_index("table index", module->tables, table);
}
uint32_t consume_exception_index(WasmModule* module, WasmException** except) {
return consume_index("exception index", module->exceptions, except);
}
template <typename T>
uint32_t consume_index(const char* name, std::vector<T>& vector, T** ptr) {
const byte* pos = pc_;
......
......@@ -36,7 +36,8 @@ enum ImportExportKindCode : uint8_t {
kExternalFunction = 0,
kExternalTable = 1,
kExternalMemory = 2,
kExternalGlobal = 3
kExternalGlobal = 3,
kExternalException = 4
};
// Binary encoding of maximum and shared flags for memories.
......
......@@ -187,6 +187,7 @@ Handle<JSArray> GetExports(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();
......@@ -217,6 +218,9 @@ Handle<JSArray> GetExports(Isolate* isolate,
case kExternalGlobal:
export_kind = global_string;
break;
case kExternalException:
export_kind = exception_string;
break;
default:
UNREACHABLE();
}
......
......@@ -20,6 +20,7 @@
#define IMPORT_SIG_INDEX(v) U32V_1(v)
#define FUNC_INDEX(v) U32V_1(v)
#define TABLE_INDEX(v) U32V_1(v)
#define EXCEPTION_INDEX(v) U32V_1(v)
#define NO_NAME U32V_1(0)
#define NAME_LENGTH(v) U32V_1(v)
#define ENTRY_COUNT(v) U32V_1(v)
......
// 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");
(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'));
// TODO(mstarzinger): The following two expectations are only temporary until
// we actually have proper wrapper objects for exported exception types.
assertEquals("number", typeof instance.exports.ex);
assertEquals(except, instance.exports.ex);
})();
(function TestExportMultiple() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except1 = builder.addException(kSig_v_v);
let except2 = builder.addException(kSig_v_i);
builder.addExportOfKind("ex1a", kExternalException, except1);
builder.addExportOfKind("ex1b", kExternalException, except1);
builder.addExportOfKind("ex2", kExternalException, except2);
let instance = builder.instantiate();
assertTrue(Object.prototype.hasOwnProperty.call(instance.exports, 'ex1a'));
assertTrue(Object.prototype.hasOwnProperty.call(instance.exports, 'ex1b'));
assertTrue(Object.prototype.hasOwnProperty.call(instance.exports, 'ex2'));
assertSame(instance.exports.ex1a, instance.exports.ex1b);
assertNotSame(instance.exports.ex1a, instance.exports.ex2);
})();
(function TestExportOutOfBounds() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex_oob", kExternalException, except + 1);
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/Wasm decoding failed: exception index 1 out of bounds/);
})();
(function TestExportSameNameTwice() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex", kExternalException, except);
builder.addExportOfKind("ex", kExternalException, except);
assertThrows(
() => builder.instantiate(), WebAssembly.CompileError,
/Duplicate export name 'ex' for exception 0 and exception 0/);
})();
(function TestExportModuleGetExports() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex", kExternalException, except);
let module = new WebAssembly.Module(builder.toBuffer());
let exports = WebAssembly.Module.exports(module);
assertArrayEquals([{ name: "ex", kind: "exception" }], exports);
})();
......@@ -97,6 +97,7 @@ let kExternalFunction = 0;
let kExternalTable = 1;
let kExternalMemory = 2;
let kExternalGlobal = 3;
let kExternalException = 4;
let kTableZero = 0;
let kMemoryZero = 0;
......
......@@ -478,6 +478,20 @@ class WasmModuleBuilder {
});
}
// Add exceptions.
if (wasm.exceptions.length > 0) {
if (debug) print("emitting exceptions @ " + binary.length);
binary.emit_section(kExceptionSectionCode, section => {
section.emit_u32v(wasm.exceptions.length);
for (let type of wasm.exceptions) {
section.emit_u32v(type.params.length);
for (let param of type.params) {
section.emit_u8(param);
}
}
});
}
// Add export table.
var mem_export = (wasm.memory !== undefined && wasm.memory.exp);
var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
......@@ -530,20 +544,6 @@ class WasmModuleBuilder {
});
}
// Add exceptions.
if (wasm.exceptions.length > 0) {
if (debug) print("emitting exceptions @ " + binary.length);
binary.emit_section(kExceptionSectionCode, section => {
section.emit_u32v(wasm.exceptions.length);
for (let type of wasm.exceptions) {
section.emit_u32v(type.params.length);
for (let param of type.params) {
section.emit_u8(param);
}
}
});
}
// Add function bodies.
if (wasm.functions.length > 0) {
// emit function bodies
......
......@@ -476,11 +476,9 @@ TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
}
TEST_F(WasmModuleVerifyTest, OneI32Exception) {
static const byte data[] = {
SECTION_EXCEPTIONS(3), 1,
// except[0] (i32)
1, kLocalI32,
};
static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
// except[0] (i32)
1, kLocalI32};
FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh);
......@@ -525,9 +523,9 @@ TEST_F(WasmModuleVerifyTest, Exception_invalid_type) {
EXPECT_FALSE(result.ok());
}
TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeCode) {
static const byte data[] = {SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION,
SECTION_EXCEPTIONS(1), 0, ONE_EMPTY_BODY};
TEST_F(WasmModuleVerifyTest, ExceptionSectionCorrectPlacement) {
static const byte data[] = {SECTION(Import, 1), 0, SECTION_EXCEPTIONS(1), 0,
SECTION(Export, 1), 0};
FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh);
......@@ -535,9 +533,17 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeCode) {
EXPECT_OK(result);
}
TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterCode) {
static const byte data[] = {SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION,
ONE_EMPTY_BODY, SECTION_EXCEPTIONS(1), 0};
TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterExport) {
static const byte data[] = {SECTION(Export, 1), 0, SECTION_EXCEPTIONS(1), 0};
FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_FALSE(result.ok());
}
TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeImport) {
static const byte data[] = {SECTION_EXCEPTIONS(1), 0, SECTION(Import, 1), 0};
FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh);
......@@ -545,6 +551,23 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterCode) {
EXPECT_FALSE(result.ok());
}
TEST_F(WasmModuleVerifyTest, ExceptionExport) {
static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
// except[0] (i32)
1, kLocalI32, SECTION(Export, 4),
1, // exports
NO_NAME, // --
kExternalException, // --
EXCEPTION_INDEX(0)};
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->export_table.size());
}
TEST_F(WasmModuleVerifyTest, OneSignature) {
{
static const byte data[] = {SIGNATURES_SECTION_VOID_VOID};
......
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