Commit 9c431505 authored by Karl Schimpf's avatar Karl Schimpf Committed by Commit Bot

Decode the exception section.

Modifies V8 to be able to parse the exception section (defining
exception types), when the experimental_wasm_eh flag is true.

Bug: v8:6577
Change-Id: I5d8b3fddaf5b0dec6b14ddd0992f9fb883e8dc90
Reviewed-on: https://chromium-review.googlesource.com/561757
Commit-Queue: Karl Schimpf <kschimpf@chromium.org>
Reviewed-by: 's avatarBill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#46539}
parent 1edb46cc
...@@ -29,6 +29,18 @@ namespace wasm { ...@@ -29,6 +29,18 @@ namespace wasm {
#else #else
#define TRACE(...) #define TRACE(...)
#endif #endif
namespace {
const char kNameString[] = "name";
const char kExceptionString[] = "exception";
template <size_t N>
constexpr size_t num_chars(const char (&)[N]) {
return N - 1; // remove null character at end.
}
} // namespace
const char* SectionName(SectionCode code) { const char* SectionName(SectionCode code) {
switch (code) { switch (code) {
...@@ -57,7 +69,9 @@ const char* SectionName(SectionCode code) { ...@@ -57,7 +69,9 @@ const char* SectionName(SectionCode code) {
case kDataSectionCode: case kDataSectionCode:
return "Data"; return "Data";
case kNameSectionCode: case kNameSectionCode:
return "Name"; return kNameString;
case kExceptionSectionCode:
return kExceptionString;
default: default:
return "<unknown>"; return "<unknown>";
} }
...@@ -65,9 +79,6 @@ const char* SectionName(SectionCode code) { ...@@ -65,9 +79,6 @@ const char* SectionName(SectionCode code) {
namespace { namespace {
const char* kNameString = "name";
const size_t kNameStringLength = 4;
ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) { ValueType TypeOf(const WasmModule* module, const WasmInitExpr& expr) {
switch (expr.kind) { switch (expr.kind) {
case WasmInitExpr::kNone: case WasmInitExpr::kNone:
...@@ -203,10 +214,15 @@ class WasmSectionIterator { ...@@ -203,10 +214,15 @@ class WasmSectionIterator {
static_cast<int>(section_name_start - decoder_.start()), static_cast<int>(section_name_start - decoder_.start()),
string.length() < 20 ? string.length() : 20, section_name_start); string.length() < 20 ? string.length() : 20, section_name_start);
if (string.length() == kNameStringLength && if (string.length() == num_chars(kNameString) &&
strncmp(reinterpret_cast<const char*>(section_name_start), strncmp(reinterpret_cast<const char*>(section_name_start),
kNameString, kNameStringLength) == 0) { kNameString, num_chars(kNameString)) == 0) {
section_code = kNameSectionCode; section_code = kNameSectionCode;
} else if (FLAG_experimental_wasm_eh &&
string.length() == num_chars(kExceptionString) &&
strncmp(reinterpret_cast<const char*>(section_name_start),
kExceptionString, num_chars(kExceptionString)) == 0) {
section_code = kExceptionSectionCode;
} }
} else if (!IsValidSectionCode(section_code)) { } else if (!IsValidSectionCode(section_code)) {
decoder_.errorf(decoder_.pc(), "unknown section code #0x%02x", decoder_.errorf(decoder_.pc(), "unknown section code #0x%02x",
...@@ -359,6 +375,9 @@ class ModuleDecoder : public Decoder { ...@@ -359,6 +375,9 @@ class ModuleDecoder : public Decoder {
case kNameSectionCode: case kNameSectionCode:
DecodeNameSection(); DecodeNameSection();
break; break;
case kExceptionSectionCode:
DecodeExceptionSection();
break;
default: default:
errorf(pc(), "unexpected section: %s", SectionName(section_code)); errorf(pc(), "unexpected section: %s", SectionName(section_code));
return; return;
...@@ -752,6 +771,17 @@ class ModuleDecoder : public Decoder { ...@@ -752,6 +771,17 @@ class ModuleDecoder : public Decoder {
consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr); consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
} }
void DecodeExceptionSection() {
uint32_t exception_count =
consume_count("exception count", kV8MaxWasmExceptions);
for (uint32_t i = 0; ok() && i < exception_count; ++i) {
TRACE("DecodeExceptionSignature[%d] module+%d\n", i,
static_cast<int>(pc_ - start_));
module_->exceptions.emplace_back(
consume_exception_sig(module_->signature_zone.get()));
}
}
ModuleResult FinishDecoding(bool verify_functions = true) { ModuleResult FinishDecoding(bool verify_functions = true) {
if (ok()) { if (ok()) {
CalculateGlobalOffsets(module_.get()); CalculateGlobalOffsets(module_.get());
...@@ -1177,9 +1207,20 @@ class ModuleDecoder : public Decoder { ...@@ -1177,9 +1207,20 @@ class ModuleDecoder : public Decoder {
} }
} }
// Parses a type entry, which is currently limited to functions only.
FunctionSig* consume_sig(Zone* zone) { FunctionSig* consume_sig(Zone* zone) {
if (!expect_u8("type form", kWasmFunctionTypeForm)) return nullptr; constexpr bool has_return_values = true;
return consume_sig_internal(zone, has_return_values);
}
WasmExceptionSig* consume_exception_sig(Zone* zone) {
constexpr bool has_return_values = true;
return consume_sig_internal(zone, !has_return_values);
}
private:
FunctionSig* consume_sig_internal(Zone* zone, bool has_return_values) {
if (has_return_values && !expect_u8("type form", kWasmFunctionTypeForm))
return nullptr;
// parse parameter types // parse parameter types
uint32_t param_count = uint32_t param_count =
consume_count("param count", kV8MaxWasmFunctionParams); consume_count("param count", kV8MaxWasmFunctionParams);
...@@ -1189,18 +1230,20 @@ class ModuleDecoder : public Decoder { ...@@ -1189,18 +1230,20 @@ class ModuleDecoder : public Decoder {
ValueType param = consume_value_type(); ValueType param = consume_value_type();
params.push_back(param); params.push_back(param);
} }
std::vector<ValueType> returns;
uint32_t return_count = 0;
if (has_return_values) {
// parse return types // parse return types
const size_t max_return_count = FLAG_experimental_wasm_mv const size_t max_return_count = FLAG_experimental_wasm_mv
? kV8MaxWasmFunctionMultiReturns ? kV8MaxWasmFunctionMultiReturns
: kV8MaxWasmFunctionReturns; : kV8MaxWasmFunctionReturns;
uint32_t return_count = consume_count("return count", max_return_count); return_count = consume_count("return count", max_return_count);
if (failed()) return nullptr; if (failed()) return nullptr;
std::vector<ValueType> returns;
for (uint32_t i = 0; ok() && i < return_count; ++i) { for (uint32_t i = 0; ok() && i < return_count; ++i) {
ValueType ret = consume_value_type(); ValueType ret = consume_value_type();
returns.push_back(ret); returns.push_back(ret);
} }
}
if (failed()) return nullptr; if (failed()) return nullptr;
......
...@@ -34,6 +34,7 @@ enum SectionCode : int8_t { ...@@ -34,6 +34,7 @@ enum SectionCode : int8_t {
kCodeSectionCode = 10, // Function code kCodeSectionCode = 10, // Function code
kDataSectionCode = 11, // Data segments kDataSectionCode = 11, // Data segments
kNameSectionCode = 12, // Name section (encoded as a string) kNameSectionCode = 12, // Name section (encoded as a string)
kExceptionSectionCode = 13, // Exception section (encoded as a string)
// Helper values // Helper values
kFirstSectionInModule = kTypeSectionCode, kFirstSectionInModule = kTypeSectionCode,
......
...@@ -20,6 +20,8 @@ constexpr size_t kV8MaxWasmFunctions = 1000000; ...@@ -20,6 +20,8 @@ constexpr size_t kV8MaxWasmFunctions = 1000000;
constexpr size_t kV8MaxWasmImports = 100000; constexpr size_t kV8MaxWasmImports = 100000;
constexpr size_t kV8MaxWasmExports = 100000; constexpr size_t kV8MaxWasmExports = 100000;
constexpr size_t kV8MaxWasmGlobals = 1000000; constexpr size_t kV8MaxWasmGlobals = 1000000;
constexpr size_t kV8MaxWasmExceptions = 1000000;
constexpr size_t kV8MaxWasmExceptionTypes = 1000000;
constexpr size_t kV8MaxWasmDataSegments = 100000; constexpr size_t kV8MaxWasmDataSegments = 100000;
// Don't use this limit directly, but use the value of FLAG_wasm_max_mem_pages. // Don't use this limit directly, but use the value of FLAG_wasm_max_mem_pages.
// Current limit mimics the maximum allowed allocation on an ArrayBuffer // Current limit mimics the maximum allowed allocation on an ArrayBuffer
......
...@@ -213,6 +213,9 @@ void RecordLazyCodeStats(Code* code, Counters* counters) { ...@@ -213,6 +213,9 @@ void RecordLazyCodeStats(Code* code, Counters* counters) {
} // namespace } // namespace
// static
const WasmExceptionSig wasm::WasmException::empty_sig_(0, 0, nullptr);
Handle<JSArrayBuffer> wasm::SetupArrayBuffer(Isolate* isolate, Handle<JSArrayBuffer> wasm::SetupArrayBuffer(Isolate* isolate,
void* allocation_base, void* allocation_base,
size_t allocation_length, size_t allocation_length,
......
...@@ -112,6 +112,20 @@ struct WasmGlobal { ...@@ -112,6 +112,20 @@ struct WasmGlobal {
bool exported; // true if exported. bool exported; // true if exported.
}; };
// Note: An exception signature only uses the params portion of a
// function signature.
typedef FunctionSig WasmExceptionSig;
struct WasmException {
explicit WasmException(const WasmExceptionSig* sig = &empty_sig_)
: sig(sig) {}
const WasmExceptionSig* sig; // type signature of the exception.
private:
static const WasmExceptionSig empty_sig_;
};
// Static representation of a wasm data segment. // Static representation of a wasm data segment.
struct WasmDataSegment { struct WasmDataSegment {
WasmInitExpr dest_addr; // destination memory address of the data. WasmInitExpr dest_addr; // destination memory address of the data.
...@@ -169,20 +183,21 @@ struct V8_EXPORT_PRIVATE WasmModule { ...@@ -169,20 +183,21 @@ struct V8_EXPORT_PRIVATE WasmModule {
bool mem_export = false; // true if the memory is exported bool mem_export = false; // true if the memory is exported
int start_function_index = -1; // start function, >= 0 if any int start_function_index = -1; // start function, >= 0 if any
std::vector<WasmGlobal> globals; // globals in this module. std::vector<WasmGlobal> globals;
uint32_t globals_size = 0; // size of globals table. uint32_t globals_size = 0;
uint32_t num_imported_functions = 0; // number of imported functions. uint32_t num_imported_functions = 0;
uint32_t num_declared_functions = 0; // number of declared functions. uint32_t num_declared_functions = 0;
uint32_t num_exported_functions = 0; // number of exported functions. uint32_t num_exported_functions = 0;
WireBytesRef name = {0, 0}; // module name, if any. WireBytesRef name = {0, 0};
// TODO(wasm): Add url here, for spec'ed location information. // TODO(wasm): Add url here, for spec'ed location information.
std::vector<FunctionSig*> signatures; // signatures in this module. std::vector<FunctionSig*> signatures;
std::vector<WasmFunction> functions; // functions in this module. std::vector<WasmFunction> functions;
std::vector<WasmDataSegment> data_segments; // data segments in this module. std::vector<WasmDataSegment> data_segments;
std::vector<WasmIndirectFunctionTable> function_tables; // function tables. std::vector<WasmIndirectFunctionTable> function_tables;
std::vector<WasmImport> import_table; // import table. std::vector<WasmImport> import_table;
std::vector<WasmExport> export_table; // export table. std::vector<WasmExport> export_table;
std::vector<WasmTableInit> table_inits; // initializations of tables std::vector<WasmException> exceptions;
std::vector<WasmTableInit> table_inits;
WasmModule() : WasmModule(nullptr) {} WasmModule() : WasmModule(nullptr) {}
WasmModule(std::unique_ptr<Zone> owned); WasmModule(std::unique_ptr<Zone> owned);
......
...@@ -50,6 +50,8 @@ namespace wasm { ...@@ -50,6 +50,8 @@ namespace wasm {
#define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 1), 0 #define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 1), 0
#define EMPTY_FUNCTION_BODIES_SECTION SECTION(Code, 1), 0 #define EMPTY_FUNCTION_BODIES_SECTION SECTION(Code, 1), 0
#define SECTION_NAMES(size) SECTION(Unknown, size + 5), 4, 'n', 'a', 'm', 'e' #define SECTION_NAMES(size) SECTION(Unknown, size + 5), 4, 'n', 'a', 'm', 'e'
#define SECTION_EXCEPTIONS(size) \
SECTION(Unknown, size + 10), 9, 'e', 'x', 'c', 'e', 'p', 't', 'i', 'o', 'n'
#define EMPTY_NAMES_SECTION SECTION_NAMES(1), 0 #define EMPTY_NAMES_SECTION SECTION_NAMES(1), 0
#define X1(...) __VA_ARGS__ #define X1(...) __VA_ARGS__
...@@ -363,6 +365,98 @@ TEST_F(WasmModuleVerifyTest, TwoGlobals) { ...@@ -363,6 +365,98 @@ TEST_F(WasmModuleVerifyTest, TwoGlobals) {
EXPECT_OFF_END_FAILURE(data, 1, sizeof(data)); EXPECT_OFF_END_FAILURE(data, 1, sizeof(data));
} }
TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
static const byte data[] = {
SECTION_EXCEPTIONS(1), 0,
};
{
// Should decode exception section with no exceptions
EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(0u, result.val->exceptions.size());
}
{
// Should read exception section as unknown section.
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(0u, result.val->exceptions.size());
}
}
TEST_F(WasmModuleVerifyTest, OneI32Exception) {
static const byte data[] = {
SECTION_EXCEPTIONS(3), 1,
1, // except[0] (i32)
kLocalI32,
};
{
// Should decode to exactly one exception
EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(1u, result.val->exceptions.size());
const WasmException& e0 = result.val->exceptions.front();
EXPECT_EQ(1u, e0.sig->parameter_count());
EXPECT_EQ(MachineRepresentation::kWord32, e0.sig->GetParam(0));
}
{
// Should read exception section as unknown section.
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(0u, result.val->exceptions.size());
}
}
TEST_F(WasmModuleVerifyTest, TwoExceptions) {
static const byte data[] = {SECTION_EXCEPTIONS(6),
2,
2, // except[0] (f32, i64)
kLocalF32,
kLocalI64,
1, // except[1] (i32)
kLocalI32};
{
// Should decode to exactly two exceptions
EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(2u, result.val->exceptions.size());
const WasmException& e0 = result.val->exceptions.front();
EXPECT_EQ(2u, e0.sig->parameter_count());
EXPECT_EQ(MachineRepresentation::kFloat32, e0.sig->GetParam(0));
EXPECT_EQ(MachineRepresentation::kWord64, e0.sig->GetParam(1));
}
{
// Should read exception section as unknown section.
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(0u, result.val->exceptions.size());
}
}
TEST_F(WasmModuleVerifyTest, Exception_invalid_type) {
static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
1, // except[0] (?)
64};
{
// Should fail decoding exception section.
EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_FALSE(result.ok());
}
{
// Should read exception section as unknown section.
ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result);
EXPECT_EQ(0u, result.val->exceptions.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