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) { ...@@ -2014,6 +2014,13 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
} }
break; 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: default:
UNREACHABLE(); UNREACHABLE();
break; break;
......
...@@ -48,6 +48,8 @@ const char* ExternalKindName(ImportExportKindCode kind) { ...@@ -48,6 +48,8 @@ const char* ExternalKindName(ImportExportKindCode kind) {
return "memory"; return "memory";
case kExternalGlobal: case kExternalGlobal:
return "global"; return "global";
case kExternalException:
return "exception";
} }
return "unknown"; return "unknown";
} }
...@@ -337,7 +339,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -337,7 +339,7 @@ class ModuleDecoderImpl : public Decoder {
static_cast<const void*>(bytes.end())); static_cast<const void*>(bytes.end()));
// Check if the section is out-of-order. // 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)); errorf(pc(), "unexpected section: %s", SectionName(section_code));
return; return;
} }
...@@ -346,19 +348,21 @@ class ModuleDecoderImpl : public Decoder { ...@@ -346,19 +348,21 @@ class ModuleDecoderImpl : public Decoder {
case kUnknownSectionCode: case kUnknownSectionCode:
break; break;
case kExceptionSectionCode: case kExceptionSectionCode:
// Note: kExceptionSectionCode > kCodeSectionCode, but must appear // Note: kExceptionSectionCode > kExportSectionCode, but must appear
// before the code section. Hence, treat it as a special case. // 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) { if (++number_of_exception_sections > 1) {
errorf(pc(), "Multiple exception sections not allowed"); errorf(pc(), "Multiple exception sections not allowed");
return; return;
} else if (next_section_ > kCodeSectionCode) { } else if (next_ordered_section_ > kExportSectionCode) {
errorf(pc(), "Exception section must appear before the code section"); errorf(pc(), "Exception section must appear before export section");
return; return;
} else if (next_ordered_section_ < kImportSectionCode) {
next_ordered_section_ = kImportSectionCode + 1;
} }
break; break;
default: default:
next_section_ = section_code; next_ordered_section_ = section_code + 1;
++next_section_;
break; break;
} }
...@@ -655,6 +659,15 @@ class ModuleDecoderImpl : public Decoder { ...@@ -655,6 +659,15 @@ class ModuleDecoderImpl : public Decoder {
} }
break; 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: default:
errorf(pos, "invalid export kind 0x%02x", exp->kind); errorf(pos, "invalid export kind 0x%02x", exp->kind);
break; break;
...@@ -935,12 +948,13 @@ class ModuleDecoderImpl : public Decoder { ...@@ -935,12 +948,13 @@ class ModuleDecoderImpl : public Decoder {
std::shared_ptr<WasmModule> module_; std::shared_ptr<WasmModule> module_;
Counters* counters_ = nullptr; Counters* counters_ = nullptr;
// The type section is the first section in a module. // 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; uint32_t number_of_exception_sections = 0;
// We store next_section_ as uint8_t instead of SectionCode so that we can // We store next_ordered_section_ as uint8_t instead of SectionCode so that we
// increment it. This static_assert should make sure that SectionCode does not // can increment it. This static_assert should make sure that SectionCode does
// get bigger than uint8_t accidentially. // not get bigger than uint8_t accidentially.
static_assert(sizeof(ModuleDecoderImpl::next_section_) == sizeof(SectionCode), static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) ==
sizeof(SectionCode),
"type mismatch"); "type mismatch");
Result<bool> intermediate_result_; Result<bool> intermediate_result_;
ModuleOrigin origin_; ModuleOrigin origin_;
...@@ -1108,6 +1122,10 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1108,6 +1122,10 @@ class ModuleDecoderImpl : public Decoder {
return consume_index("table index", module->tables, table); 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> template <typename T>
uint32_t consume_index(const char* name, std::vector<T>& vector, T** ptr) { uint32_t consume_index(const char* name, std::vector<T>& vector, T** ptr) {
const byte* pos = pc_; const byte* pos = pc_;
......
...@@ -36,7 +36,8 @@ enum ImportExportKindCode : uint8_t { ...@@ -36,7 +36,8 @@ enum ImportExportKindCode : uint8_t {
kExternalFunction = 0, kExternalFunction = 0,
kExternalTable = 1, kExternalTable = 1,
kExternalMemory = 2, kExternalMemory = 2,
kExternalGlobal = 3 kExternalGlobal = 3,
kExternalException = 4
}; };
// Binary encoding of maximum and shared flags for memories. // Binary encoding of maximum and shared flags for memories.
......
...@@ -187,6 +187,7 @@ Handle<JSArray> GetExports(Isolate* isolate, ...@@ -187,6 +187,7 @@ Handle<JSArray> GetExports(Isolate* isolate,
Handle<String> table_string = factory->InternalizeUtf8String("table"); Handle<String> table_string = factory->InternalizeUtf8String("table");
Handle<String> memory_string = factory->InternalizeUtf8String("memory"); Handle<String> memory_string = factory->InternalizeUtf8String("memory");
Handle<String> global_string = factory->InternalizeUtf8String("global"); Handle<String> global_string = factory->InternalizeUtf8String("global");
Handle<String> exception_string = factory->InternalizeUtf8String("exception");
// Create the result array. // Create the result array.
const WasmModule* module = module_object->module(); const WasmModule* module = module_object->module();
...@@ -217,6 +218,9 @@ Handle<JSArray> GetExports(Isolate* isolate, ...@@ -217,6 +218,9 @@ Handle<JSArray> GetExports(Isolate* isolate,
case kExternalGlobal: case kExternalGlobal:
export_kind = global_string; export_kind = global_string;
break; break;
case kExternalException:
export_kind = exception_string;
break;
default: default:
UNREACHABLE(); UNREACHABLE();
} }
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#define IMPORT_SIG_INDEX(v) U32V_1(v) #define IMPORT_SIG_INDEX(v) U32V_1(v)
#define FUNC_INDEX(v) U32V_1(v) #define FUNC_INDEX(v) U32V_1(v)
#define TABLE_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 NO_NAME U32V_1(0)
#define NAME_LENGTH(v) U32V_1(v) #define NAME_LENGTH(v) U32V_1(v)
#define ENTRY_COUNT(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; ...@@ -97,6 +97,7 @@ let kExternalFunction = 0;
let kExternalTable = 1; let kExternalTable = 1;
let kExternalMemory = 2; let kExternalMemory = 2;
let kExternalGlobal = 3; let kExternalGlobal = 3;
let kExternalException = 4;
let kTableZero = 0; let kTableZero = 0;
let kMemoryZero = 0; let kMemoryZero = 0;
......
...@@ -478,6 +478,20 @@ class WasmModuleBuilder { ...@@ -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. // Add export table.
var mem_export = (wasm.memory !== undefined && wasm.memory.exp); var mem_export = (wasm.memory !== undefined && wasm.memory.exp);
var exports_count = wasm.exports.length + (mem_export ? 1 : 0); var exports_count = wasm.exports.length + (mem_export ? 1 : 0);
...@@ -530,20 +544,6 @@ class WasmModuleBuilder { ...@@ -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. // Add function bodies.
if (wasm.functions.length > 0) { if (wasm.functions.length > 0) {
// emit function bodies // emit function bodies
......
...@@ -476,11 +476,9 @@ TEST_F(WasmModuleVerifyTest, ZeroExceptions) { ...@@ -476,11 +476,9 @@ TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
} }
TEST_F(WasmModuleVerifyTest, OneI32Exception) { TEST_F(WasmModuleVerifyTest, OneI32Exception) {
static const byte data[] = { static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
SECTION_EXCEPTIONS(3), 1, // except[0] (i32)
// except[0] (i32) 1, kLocalI32};
1, kLocalI32,
};
FAIL_IF_NO_EXPERIMENTAL_EH(data); FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
...@@ -525,9 +523,9 @@ TEST_F(WasmModuleVerifyTest, Exception_invalid_type) { ...@@ -525,9 +523,9 @@ TEST_F(WasmModuleVerifyTest, Exception_invalid_type) {
EXPECT_FALSE(result.ok()); EXPECT_FALSE(result.ok());
} }
TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeCode) { TEST_F(WasmModuleVerifyTest, ExceptionSectionCorrectPlacement) {
static const byte data[] = {SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, static const byte data[] = {SECTION(Import, 1), 0, SECTION_EXCEPTIONS(1), 0,
SECTION_EXCEPTIONS(1), 0, ONE_EMPTY_BODY}; SECTION(Export, 1), 0};
FAIL_IF_NO_EXPERIMENTAL_EH(data); FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
...@@ -535,9 +533,17 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeCode) { ...@@ -535,9 +533,17 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionBeforeCode) {
EXPECT_OK(result); EXPECT_OK(result);
} }
TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterCode) { TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterExport) {
static const byte data[] = {SIGNATURES_SECTION_VOID_VOID, ONE_EMPTY_FUNCTION, static const byte data[] = {SECTION(Export, 1), 0, SECTION_EXCEPTIONS(1), 0};
ONE_EMPTY_BODY, 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); FAIL_IF_NO_EXPERIMENTAL_EH(data);
WASM_FEATURE_SCOPE(eh); WASM_FEATURE_SCOPE(eh);
...@@ -545,6 +551,23 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterCode) { ...@@ -545,6 +551,23 @@ TEST_F(WasmModuleVerifyTest, ExceptionSectionAfterCode) {
EXPECT_FALSE(result.ok()); 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) { TEST_F(WasmModuleVerifyTest, OneSignature) {
{ {
static const byte data[] = {SIGNATURES_SECTION_VOID_VOID}; 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