Commit 470a1001 authored by Karl Schimpf's avatar Karl Schimpf Committed by Commit Bot

Start migration of try/throw/catch to match proposal.

This CL does the first baby steps on moving the current (experimental)
exception handling to match that of the WebAssembly proposal.

It does the following:

1) Use exception tags instead of integers.

2) Only handle empty exception signatures (i.e. no values associated
   with the exception tag.

3) Only handle one catch clause.

4) Be sure to rethrow the exception if the exception tag does not match.

Note: There are many things that need to be fixed, and are too
numerous to list here. However, the code should have TODO's on each
missing parts of the implementation.

Also note that the code currently doesn't handle nested catch blocks,
nor does it change the throw value being an integer. Rather, the
integer value is still being thrown, and currently is the exception
tag. Therefore, we don't build an exception object. This is the reason
why this CL doesn't handle exceptions that pass values.

Also, the current implementation still can't handle multiple modules
because tag resolution (between) modules has not be implemented yet.

Bug: v8:6577
Change-Id: Id6d08b641b3c42d1eec7d4db582f2dab35406114
Reviewed-on: https://chromium-review.googlesource.com/591910Reviewed-by: 's avatarBrad Nelson <bradnelson@chromium.org>
Commit-Queue: Karl Schimpf <kschimpf@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47087}
parent 35c923cc
...@@ -1830,12 +1830,18 @@ Node* WasmGraphBuilder::Throw(Node* input) { ...@@ -1830,12 +1830,18 @@ Node* WasmGraphBuilder::Throw(Node* input) {
arraysize(parameters)); arraysize(parameters));
} }
Node* WasmGraphBuilder::Rethrow() {
SetNeedsStackCheck();
Node* result = BuildCallToRuntime(Runtime::kWasmRethrow, nullptr, 0);
return result;
}
Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) { Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) {
SetNeedsStackCheck(); SetNeedsStackCheck();
CommonOperatorBuilder* common = jsgraph()->common(); CommonOperatorBuilder* common = jsgraph()->common();
Node* parameters[] = {input}; // caught value Node* parameters[] = {input}; // caught value
Node* value = BuildCallToRuntime(Runtime::kWasmGetCaughtExceptionValue, Node* value = BuildCallToRuntime(Runtime::kWasmSetCaughtExceptionValue,
parameters, arraysize(parameters)); parameters, arraysize(parameters));
Node* is_smi; Node* is_smi;
......
...@@ -168,6 +168,7 @@ class WasmGraphBuilder { ...@@ -168,6 +168,7 @@ class WasmGraphBuilder {
wasm::WasmCodePosition position = wasm::kNoCodePosition); wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* GrowMemory(Node* input); Node* GrowMemory(Node* input);
Node* Throw(Node* input); Node* Throw(Node* input);
Node* Rethrow();
Node* Catch(Node* input, wasm::WasmCodePosition position); Node* Catch(Node* input, wasm::WasmCodePosition position);
unsigned InputCount(Node* node); unsigned InputCount(Node* node);
bool IsPhiWithMerge(Node* phi, Node* merge); bool IsPhiWithMerge(Node* phi, Node* merge);
......
...@@ -47,6 +47,17 @@ bool Isolate::has_pending_exception() { ...@@ -47,6 +47,17 @@ bool Isolate::has_pending_exception() {
return !thread_local_top_.pending_exception_->IsTheHole(this); return !thread_local_top_.pending_exception_->IsTheHole(this);
} }
Object* Isolate::get_wasm_caught_exception() const {
return thread_local_top_.wasm_caught_exception_;
}
void Isolate::set_wasm_caught_exception(Object* exception_obj) {
thread_local_top_.wasm_caught_exception_ = exception_obj;
}
void Isolate::clear_wasm_caught_exception() {
thread_local_top_.wasm_caught_exception_ = nullptr;
}
void Isolate::clear_pending_message() { void Isolate::clear_pending_message() {
thread_local_top_.pending_message_obj_ = heap_.the_hole_value(); thread_local_top_.pending_message_obj_ = heap_.the_hole_value();
......
...@@ -103,6 +103,7 @@ void ThreadLocalTop::InitializeInternal() { ...@@ -103,6 +103,7 @@ void ThreadLocalTop::InitializeInternal() {
// These members are re-initialized later after deserialization // These members are re-initialized later after deserialization
// is complete. // is complete.
pending_exception_ = NULL; pending_exception_ = NULL;
wasm_caught_exception_ = NULL;
rethrowing_message_ = false; rethrowing_message_ = false;
pending_message_obj_ = NULL; pending_message_obj_ = NULL;
scheduled_exception_ = NULL; scheduled_exception_ = NULL;
...@@ -215,6 +216,7 @@ void Isolate::IterateThread(ThreadVisitor* v, char* t) { ...@@ -215,6 +216,7 @@ void Isolate::IterateThread(ThreadVisitor* v, char* t) {
void Isolate::Iterate(RootVisitor* v, ThreadLocalTop* thread) { void Isolate::Iterate(RootVisitor* v, ThreadLocalTop* thread) {
// Visit the roots from the top for a given thread. // Visit the roots from the top for a given thread.
v->VisitRootPointer(Root::kTop, &thread->pending_exception_); v->VisitRootPointer(Root::kTop, &thread->pending_exception_);
v->VisitRootPointer(Root::kTop, &thread->wasm_caught_exception_);
v->VisitRootPointer(Root::kTop, &thread->pending_message_obj_); v->VisitRootPointer(Root::kTop, &thread->pending_message_obj_);
v->VisitRootPointer(Root::kTop, bit_cast<Object**>(&(thread->context_))); v->VisitRootPointer(Root::kTop, bit_cast<Object**>(&(thread->context_)));
v->VisitRootPointer(Root::kTop, &thread->scheduled_exception_); v->VisitRootPointer(Root::kTop, &thread->scheduled_exception_);
......
...@@ -326,6 +326,9 @@ class ThreadLocalTop BASE_EMBEDDED { ...@@ -326,6 +326,9 @@ class ThreadLocalTop BASE_EMBEDDED {
Context* context_; Context* context_;
ThreadId thread_id_; ThreadId thread_id_;
Object* pending_exception_; Object* pending_exception_;
// TODO(kschimpf): Change this to a stack of caught exceptions (rather than
// just innermost catching try block).
Object* wasm_caught_exception_;
// Communication channel between Isolate::FindHandler and the CEntryStub. // Communication channel between Isolate::FindHandler and the CEntryStub.
Context* pending_handler_context_; Context* pending_handler_context_;
...@@ -602,6 +605,11 @@ class Isolate { ...@@ -602,6 +605,11 @@ class Isolate {
inline void set_pending_exception(Object* exception_obj); inline void set_pending_exception(Object* exception_obj);
inline void clear_pending_exception(); inline void clear_pending_exception();
// Interface to wasm caught exception.
inline Object* get_wasm_caught_exception() const;
inline void set_wasm_caught_exception(Object* exception_obj);
inline void clear_wasm_caught_exception();
THREAD_LOCAL_TOP_ADDRESS(Object*, pending_exception) THREAD_LOCAL_TOP_ADDRESS(Object*, pending_exception)
inline bool has_pending_exception(); inline bool has_pending_exception();
......
...@@ -142,6 +142,8 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) { ...@@ -142,6 +142,8 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
} }
RUNTIME_FUNCTION(Runtime_WasmThrow) { RUNTIME_FUNCTION(Runtime_WasmThrow) {
// TODO(kschimpf): Change this to build a runtime exception with
// wasm properties, instead of just an integer.
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(2, args.length());
CONVERT_SMI_ARG_CHECKED(lower, 0); CONVERT_SMI_ARG_CHECKED(lower, 0);
...@@ -156,7 +158,15 @@ RUNTIME_FUNCTION(Runtime_WasmThrow) { ...@@ -156,7 +158,15 @@ RUNTIME_FUNCTION(Runtime_WasmThrow) {
return isolate->Throw(*isolate->factory()->NewNumberFromInt(thrown_value)); return isolate->Throw(*isolate->factory()->NewNumberFromInt(thrown_value));
} }
RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) { RUNTIME_FUNCTION(Runtime_WasmRethrow) {
HandleScope scope(isolate);
DCHECK_EQ(0, args.length());
Object* exception = isolate->get_wasm_caught_exception();
isolate->clear_wasm_caught_exception();
return isolate->Throw(exception);
}
RUNTIME_FUNCTION(Runtime_WasmSetCaughtExceptionValue) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
Object* exception = args[0]; Object* exception = args[0];
...@@ -164,6 +174,7 @@ RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) { ...@@ -164,6 +174,7 @@ RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) {
// Number or a Smi (which we have just converted to a Number.) This logic // Number or a Smi (which we have just converted to a Number.) This logic
// lives in Isolate::is_catchable_by_wasm(Object*). // lives in Isolate::is_catchable_by_wasm(Object*).
CHECK(exception->IsNumber()); CHECK(exception->IsNumber());
isolate->set_wasm_caught_exception(exception);
return exception; return exception;
} }
......
...@@ -634,7 +634,8 @@ namespace internal { ...@@ -634,7 +634,8 @@ namespace internal {
F(ThrowWasmStackOverflow, 0, 1) \ F(ThrowWasmStackOverflow, 0, 1) \
F(WasmThrowTypeError, 0, 1) \ F(WasmThrowTypeError, 0, 1) \
F(WasmThrow, 2, 1) \ F(WasmThrow, 2, 1) \
F(WasmGetCaughtExceptionValue, 1, 1) \ F(WasmRethrow, 0, 1) \
F(WasmSetCaughtExceptionValue, 1, 1) \
F(WasmRunInterpreter, 3, 1) \ F(WasmRunInterpreter, 3, 1) \
F(WasmStackGuard, 0, 1) \ F(WasmStackGuard, 0, 1) \
F(SetThreadInWasm, 0, 1) \ F(SetThreadInWasm, 0, 1) \
......
...@@ -13,6 +13,7 @@ namespace internal { ...@@ -13,6 +13,7 @@ namespace internal {
namespace wasm { namespace wasm {
struct WasmGlobal; struct WasmGlobal;
struct WasmException;
// Use this macro to check a condition if checked == true, and DCHECK the // Use this macro to check a condition if checked == true, and DCHECK the
// condition otherwise. // condition otherwise.
...@@ -34,6 +35,17 @@ struct LocalIndexOperand { ...@@ -34,6 +35,17 @@ struct LocalIndexOperand {
} }
}; };
template <bool checked>
struct ExceptionIndexOperand {
uint32_t index;
const WasmException* exception = nullptr;
unsigned length;
inline ExceptionIndexOperand(Decoder* decoder, const byte* pc) {
index = decoder->read_u32v<checked>(pc + 1, &length, "exception index");
}
};
template <bool checked> template <bool checked>
struct ImmI32Operand { struct ImmI32Operand {
int32_t value; int32_t value;
......
...@@ -44,9 +44,8 @@ namespace wasm { ...@@ -44,9 +44,8 @@ namespace wasm {
break; \ break; \
} }
#define PROTOTYPE_NOT_FUNCTIONAL(opcode) \ #define OPCODE_ERROR(opcode, message) \
errorf(pc_, "Prototype still not functional: %s", \ (errorf(pc_, "%s: %s", WasmOpcodes::OpcodeName(opcode), (message)))
WasmOpcodes::OpcodeName(opcode));
// An SsaEnv environment carries the current local variable renaming // An SsaEnv environment carries the current local variable renaming
// as well as the current effect and control dependency in the TF graph. // as well as the current effect and control dependency in the TF graph.
...@@ -82,8 +81,10 @@ struct Value { ...@@ -82,8 +81,10 @@ struct Value {
struct TryInfo : public ZoneObject { struct TryInfo : public ZoneObject {
SsaEnv* catch_env; SsaEnv* catch_env;
TFNode* exception; TFNode* exception;
size_t catch_count; // Number of catch blocks associated with the try.
explicit TryInfo(SsaEnv* c) : catch_env(c), exception(nullptr) {} explicit TryInfo(SsaEnv* c)
: catch_env(c), exception(nullptr), catch_count(0) {}
}; };
struct MergeValues { struct MergeValues {
...@@ -283,6 +284,15 @@ class WasmDecoder : public Decoder { ...@@ -283,6 +284,15 @@ class WasmDecoder : public Decoder {
return false; return false;
} }
inline bool Validate(const byte* pc, ExceptionIndexOperand<true>& operand) {
if (module_ != nullptr && operand.index < module_->exceptions.size()) {
operand.exception = &module_->exceptions[operand.index];
return true;
}
errorf(pc + 1, "Invalid exception index: %u", operand.index);
return false;
}
inline bool Validate(const byte* pc, GlobalIndexOperand<true>& operand) { inline bool Validate(const byte* pc, GlobalIndexOperand<true>& operand) {
if (module_ != nullptr && operand.index < module_->globals.size()) { if (module_ != nullptr && operand.index < module_->globals.size()) {
operand.global = &module_->globals[operand.index]; operand.global = &module_->globals[operand.index];
...@@ -462,10 +472,15 @@ class WasmDecoder : public Decoder { ...@@ -462,10 +472,15 @@ class WasmDecoder : public Decoder {
return 1 + operand.length; return 1 + operand.length;
} }
case kExprThrow:
case kExprCatch: {
ExceptionIndexOperand<true> operand(decoder, pc);
return 1 + operand.length;
}
case kExprSetLocal: case kExprSetLocal:
case kExprTeeLocal: case kExprTeeLocal:
case kExprGetLocal: case kExprGetLocal: {
case kExprCatch: {
LocalIndexOperand<true> operand(decoder, pc); LocalIndexOperand<true> operand(decoder, pc);
return 1 + operand.length; return 1 + operand.length;
} }
...@@ -752,6 +767,13 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -752,6 +767,13 @@ class WasmFullDecoder : public WasmDecoder {
return module_->has_memory; return module_->has_memory;
} }
template <bool check>
inline TFNode* GetExceptionTag(ExceptionIndexOperand<check>& operand) {
// TODO(kschimpf): Need to get runtime exception tag values. This
// code only handles non-imported/exported exceptions.
return BUILD(Int32Constant, operand.index);
}
// Decodes the body of a function. // Decodes the body of a function.
void DecodeFunctionBody() { void DecodeFunctionBody() {
TRACE("wasm-decode %p...%p (module+%u, %d bytes) %s\n", TRACE("wasm-decode %p...%p (module+%u, %d bytes) %s\n",
...@@ -808,15 +830,20 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -808,15 +830,20 @@ class WasmFullDecoder : public WasmDecoder {
case kExprRethrow: { case kExprRethrow: {
// TODO(kschimpf): Implement. // TODO(kschimpf): Implement.
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
PROTOTYPE_NOT_FUNCTIONAL(opcode); OPCODE_ERROR(opcode, "not implemented yet");
break; break;
} }
case kExprThrow: { case kExprThrow: {
// TODO(kschimpf): Fix to use type signature of exception.
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
PROTOTYPE_NOT_FUNCTIONAL(opcode); ExceptionIndexOperand<true> operand(this, pc_);
Value value = Pop(0, kWasmI32); len = 1 + operand.length;
BUILD(Throw, value.node); if (!Validate(pc_, operand)) break;
if (operand.exception->sig->parameter_count() > 0) {
// TODO(kschimpf): Fix to pull values off stack and build throw.
OPCODE_ERROR(opcode, "can't handle exceptions with values yet");
break;
}
BUILD(Throw, GetExceptionTag(operand));
// TODO(titzer): Throw should end control, but currently we build a // TODO(titzer): Throw should end control, but currently we build a
// (reachable) runtime call instead of connecting it directly to // (reachable) runtime call instead of connecting it directly to
// end. // end.
...@@ -838,49 +865,62 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -838,49 +865,62 @@ class WasmFullDecoder : public WasmDecoder {
case kExprCatch: { case kExprCatch: {
// TODO(kschimpf): Fix to use type signature of exception. // TODO(kschimpf): Fix to use type signature of exception.
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
PROTOTYPE_NOT_FUNCTIONAL(opcode); ExceptionIndexOperand<true> operand(this, pc_);
LocalIndexOperand<true> operand(this, pc_);
len = 1 + operand.length; len = 1 + operand.length;
if (!Validate(pc_, operand)) break;
if (control_.empty()) { if (control_.empty()) {
error("catch does not match any try"); error("catch does not match any try");
break; break;
} }
Control* c = &control_.back(); Control* c = &control_.back();
DCHECK_NOT_NULL(c->try_info);
if (!c->is_try()) { if (!c->is_try()) {
error("catch does not match any try"); error("catch does not match any try");
break; break;
} }
if (c->try_info->catch_env == nullptr) { if (c->try_info->catch_count > 0) {
error(pc_, "catch already present for try with catch"); OPCODE_ERROR(opcode, "multiple catch blocks not implemented");
break; break;
} }
++c->try_info->catch_count;
FallThruTo(c); FallThruTo(c);
stack_.resize(c->stack_depth); stack_.resize(c->stack_depth);
DCHECK_NOT_NULL(c->try_info);
SsaEnv* catch_env = c->try_info->catch_env; SsaEnv* catch_env = c->try_info->catch_env;
c->try_info->catch_env = nullptr;
SetEnv("catch:begin", catch_env); SetEnv("catch:begin", catch_env);
current_catch_ = c->previous_catch; current_catch_ = c->previous_catch;
if (Validate(pc_, operand)) { // Get the exception and see if wanted exception.
if (ssa_env_->locals) {
TFNode* exception_as_i32 = TFNode* exception_as_i32 =
BUILD(Catch, c->try_info->exception, position()); BUILD(Catch, c->try_info->exception, position());
ssa_env_->locals[operand.index] = exception_as_i32; TFNode* exception_tag = GetExceptionTag(operand);
} TFNode* compare_i32 = BUILD(Binop, kExprI32Eq, exception_as_i32,
} exception_tag, position());
TFNode* if_true = nullptr;
TFNode* if_false = nullptr;
BUILD(BranchNoHint, compare_i32, &if_true, &if_false);
SsaEnv* end_env = ssa_env_;
SsaEnv* false_env = Split(end_env);
false_env->control = if_false;
SsaEnv* true_env = Steal(ssa_env_);
true_env->control = if_true;
c->try_info->catch_env = false_env;
SetEnv("Try:catch", true_env);
len = 1 + operand.length;
// TODO(kschimpf): Add code to pop caught exception from isolate.
break; break;
} }
case kExprCatchAll: { case kExprCatchAll: {
// TODO(kschimpf): Implement. // TODO(kschimpf): Implement.
CHECK_PROTOTYPE_OPCODE(eh); CHECK_PROTOTYPE_OPCODE(eh);
PROTOTYPE_NOT_FUNCTIONAL(opcode); OPCODE_ERROR(opcode, "not implemented yet");
break; break;
} }
case kExprLoop: { case kExprLoop: {
...@@ -969,10 +1009,17 @@ class WasmFullDecoder : public WasmDecoder { ...@@ -969,10 +1009,17 @@ class WasmFullDecoder : public WasmDecoder {
name = "try:end"; name = "try:end";
// validate that catch was seen. // validate that catch was seen.
if (c->try_info->catch_env != nullptr) { if (c->try_info->catch_count == 0) {
error(pc_, "missing catch in try"); error(pc_, "missing catch in try");
break; break;
} }
SsaEnv* fallthru_ssa_env = ssa_env_;
DCHECK_NOT_NULL(c->try_info->catch_env);
SetEnv("Catch fail", c->try_info->catch_env);
BUILD0(Rethrow);
// TODO(karlschimpf): Why not use EndControl ()? (currently fails)
FallThruTo(c);
SetEnv("Catch fallthru", fallthru_ssa_env);
} }
FallThruTo(c); FallThruTo(c);
SetEnv(name, c->end_env); SetEnv(name, c->end_env);
......
...@@ -31,9 +31,9 @@ namespace wasm { ...@@ -31,9 +31,9 @@ namespace wasm {
#endif #endif
namespace { namespace {
const char kNameString[] = "name"; constexpr char kNameString[] = "name";
constexpr char kExceptionString[] = "exception";
const char kExceptionString[] = "exception"; constexpr char kUnknownString[] = "<unknown>";
template <size_t N> template <size_t N>
constexpr size_t num_chars(const char (&)[N]) { constexpr size_t num_chars(const char (&)[N]) {
...@@ -71,9 +71,10 @@ const char* SectionName(SectionCode code) { ...@@ -71,9 +71,10 @@ const char* SectionName(SectionCode code) {
case kNameSectionCode: case kNameSectionCode:
return kNameString; return kNameString;
case kExceptionSectionCode: case kExceptionSectionCode:
return kExceptionString; if (FLAG_experimental_wasm_eh) return kExceptionString;
return kUnknownString;
default: default:
return "<unknown>"; return kUnknownString;
} }
} }
...@@ -218,11 +219,6 @@ class WasmSectionIterator { ...@@ -218,11 +219,6 @@ class WasmSectionIterator {
strncmp(reinterpret_cast<const char*>(section_name_start), strncmp(reinterpret_cast<const char*>(section_name_start),
kNameString, num_chars(kNameString)) == 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",
...@@ -332,9 +328,25 @@ class ModuleDecoder : public Decoder { ...@@ -332,9 +328,25 @@ class ModuleDecoder : public Decoder {
errorf(pc(), "unexpected section: %s", SectionName(section_code)); errorf(pc(), "unexpected section: %s", SectionName(section_code));
return; return;
} }
if (section_code != kUnknownSectionCode) {
switch (section_code) {
case kUnknownSectionCode:
break;
case kExceptionSectionCode:
// Note: kExceptionSectionCode > kCodeSectionCode, but must appear
// before the code 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");
return;
}
break;
default:
next_section_ = section_code; next_section_ = section_code;
++next_section_; ++next_section_;
break;
} }
switch (section_code) { switch (section_code) {
...@@ -377,7 +389,11 @@ class ModuleDecoder : public Decoder { ...@@ -377,7 +389,11 @@ class ModuleDecoder : public Decoder {
DecodeNameSection(); DecodeNameSection();
break; break;
case kExceptionSectionCode: case kExceptionSectionCode:
if (FLAG_experimental_wasm_eh) {
DecodeExceptionSection(); DecodeExceptionSection();
} else {
errorf(pc(), "unexpected section: %s", SectionName(section_code));
}
break; break;
default: default:
errorf(pc(), "unexpected section: %s", SectionName(section_code)); errorf(pc(), "unexpected section: %s", SectionName(section_code));
...@@ -877,6 +893,7 @@ class ModuleDecoder : public Decoder { ...@@ -877,6 +893,7 @@ class ModuleDecoder : public Decoder {
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_section_ = kFirstSectionInModule;
uint32_t number_of_exception_sections = 0;
// We store next_section_ as uint8_t instead of SectionCode so that we can // 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 // increment it. This static_assert should make sure that SectionCode does not
// get bigger than uint8_t accidentially. // get bigger than uint8_t accidentially.
......
...@@ -34,16 +34,17 @@ enum SectionCode : int8_t { ...@@ -34,16 +34,17 @@ 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) kExceptionSectionCode = 13, // Exception section
// Helper values // Helper values
kFirstSectionInModule = kTypeSectionCode, kFirstSectionInModule = kTypeSectionCode,
kLastKnownModuleSection = kExceptionSectionCode,
}; };
enum NameSectionType : uint8_t { kModule = 0, kFunction = 1, kLocal = 2 }; enum NameSectionType : uint8_t { kModule = 0, kFunction = 1, kLocal = 2 };
inline bool IsValidSectionCode(uint8_t byte) { inline bool IsValidSectionCode(uint8_t byte) {
return kTypeSectionCode <= byte && byte <= kDataSectionCode; return kTypeSectionCode <= byte && byte <= kLastKnownModuleSection;
} }
const char* SectionName(SectionCode code); const char* SectionName(SectionCode code);
......
...@@ -143,12 +143,17 @@ void wasm::PrintWasmText(const WasmModule *module, ...@@ -143,12 +143,17 @@ void wasm::PrintWasmText(const WasmModule *module,
} }
case kExprGetLocal: case kExprGetLocal:
case kExprSetLocal: case kExprSetLocal:
case kExprTeeLocal: case kExprTeeLocal: {
case kExprCatch: {
LocalIndexOperand<false> operand(&i, i.pc()); LocalIndexOperand<false> operand(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index; os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
break; break;
} }
case kExprThrow:
case kExprCatch: {
ExceptionIndexOperand<false> operand(&i, i.pc());
os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
break;
}
case kExprGetGlobal: case kExprGetGlobal:
case kExprSetGlobal: { case kExprSetGlobal: {
GlobalIndexOperand<false> operand(&i, i.pc()); GlobalIndexOperand<false> operand(&i, i.pc());
...@@ -183,7 +188,6 @@ void wasm::PrintWasmText(const WasmModule *module, ...@@ -183,7 +188,6 @@ void wasm::PrintWasmText(const WasmModule *module,
case kExprGrowMemory: case kExprGrowMemory:
case kExprDrop: case kExprDrop:
case kExprSelect: case kExprSelect:
case kExprThrow:
os << WasmOpcodes::OpcodeName(opcode); os << WasmOpcodes::OpcodeName(opcode);
break; break;
......
...@@ -177,9 +177,6 @@ ...@@ -177,9 +177,6 @@
# BUG(v8:6306). # BUG(v8:6306).
'wasm/huge-memory': [SKIP], 'wasm/huge-memory': [SKIP],
# BUG(v8:6577).
'wasm/exceptions': [SKIP],
}], # ALWAYS }], # ALWAYS
['novfp3 == True', { ['novfp3 == True', {
......
...@@ -7,6 +7,73 @@ ...@@ -7,6 +7,73 @@
load("test/mjsunit/wasm/wasm-constants.js"); load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js"); load("test/mjsunit/wasm/wasm-module-builder.js");
// The following method doesn't attempt to catch an raised exception.
var test_throw = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_v);
builder.addFunction("throw_if_param_not_zero", kSig_i_i)
.addBody([
kExprGetLocal, 0,
kExprI32Const, 0,
kExprI32Ne,
kExprIf, kWasmStmt,
kExprThrow, 0,
kExprEnd,
kExprI32Const, 1
]).exportFunc();
return builder.instantiate();
})();
// Check the test_throw exists.
assertFalse(test_throw === undefined);
assertFalse(test_throw === null);
assertFalse(test_throw === 0);
assertEquals("object", typeof test_throw.exports);
assertEquals("function", typeof test_throw.exports.throw_if_param_not_zero);
// Test expected behavior of throws
assertEquals(1, test_throw.exports.throw_if_param_not_zero(0));
assertWasmThrows([], function() { test_throw.exports.throw_if_param_not_zero(10) });
assertWasmThrows([], function() { test_throw.exports.throw_if_param_not_zero(-1) });
// Now that we know throwing works, we test catching the exceptions we raise.
var test_catch = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_v);
builder.addFunction("simple_throw_catch_to_0_1", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprThrow, 0,
kExprEnd,
kExprI32Const, 1,
kExprCatch, 0,
kExprI32Const, 0,
kExprEnd
]).exportFunc();
return builder.instantiate();
})();
// Check the test_catch exists.
assertFalse(test_catch === undefined);
assertFalse(test_catch === null);
assertFalse(test_catch === 0);
assertEquals("object", typeof test_catch.exports);
assertEquals("function", typeof test_catch.exports.simple_throw_catch_to_0_1);
// Test expected behavior of simple catch.
assertEquals(0, test_catch.exports.simple_throw_catch_to_0_1(0));
assertEquals(1, test_catch.exports.simple_throw_catch_to_0_1(1));
/* TODO(kschimpf) Convert these tests to work for the proposed exceptions.
// The following methods do not attempt to catch the exception they raise. // The following methods do not attempt to catch the exception they raise.
var test_throw = (function () { var test_throw = (function () {
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
...@@ -229,16 +296,16 @@ var test_catch = (function () { ...@@ -229,16 +296,16 @@ var test_catch = (function () {
kExprI32Eq, kExprI32Eq,
kExprIf, kWasmStmt, kExprIf, kWasmStmt,
kExprGetLocal, 2, kExprGetLocal, 2,
kExprI32Const, /*64=*/ 192, 0, kExprI32Const, / *64=* / 192, 0,
kExprI32Ior, kExprI32Ior,
kExprThrow, kExprThrow,
kExprUnreachable, kExprUnreachable,
kExprEnd, kExprEnd,
kExprI32Const, /*128=*/ 128, 1, kExprI32Const, / *128=* / 128, 1,
kExprI32Ior, kExprI32Ior,
kExprCatch, 1, kExprCatch, 1,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprI32Const, /*256=*/ 128, 2, kExprI32Const, / *256=* / 128, 2,
kExprI32Ior, kExprI32Ior,
kExprEnd, kExprEnd,
]) ])
...@@ -252,7 +319,7 @@ var test_catch = (function () { ...@@ -252,7 +319,7 @@ var test_catch = (function () {
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, kWasmThrowFunction, kExprCallFunction, kWasmThrowFunction,
kExprI32Const, /*-1=*/ 127, kExprI32Const, / *-1=* / 127,
kExprCatch, 1, kExprCatch, 1,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprEnd kExprEnd
...@@ -287,7 +354,7 @@ var test_catch = (function () { ...@@ -287,7 +354,7 @@ var test_catch = (function () {
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI32Const, 0, kExprI32Const, 0,
kExprCallFunction, kFromIndirectCalleeHelper, kExprCallFunction, kFromIndirectCalleeHelper,
kExprI32Const, /*-1=*/ 127, kExprI32Const, / *-1=* / 127,
kExprCatch, 1, kExprCatch, 1,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprEnd kExprEnd
...@@ -301,7 +368,7 @@ var test_catch = (function () { ...@@ -301,7 +368,7 @@ var test_catch = (function () {
kExprTry, kWasmI32, kExprTry, kWasmI32,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprCallFunction, kJSThrowI, kExprCallFunction, kJSThrowI,
kExprI32Const, /*-1=*/ 127, kExprI32Const, / *-1=* / 127,
kExprCatch, 1, kExprCatch, 1,
kExprGetLocal, 1, kExprGetLocal, 1,
kExprEnd, kExprEnd,
...@@ -381,3 +448,4 @@ assertEquals(-10, test_catch.exports.from_js(-10)); ...@@ -381,3 +448,4 @@ assertEquals(-10, test_catch.exports.from_js(-10));
assertThrowsEquals(test_catch.exports.string_from_js, "use wasm;"); assertThrowsEquals(test_catch.exports.string_from_js, "use wasm;");
assertThrowsEquals(test_catch.exports.large_from_js, 1e+28); assertThrowsEquals(test_catch.exports.large_from_js, 1e+28);
assertThrowsEquals(test_catch.exports.undefined_from_js, undefined); assertThrowsEquals(test_catch.exports.undefined_from_js, undefined);
*/
...@@ -64,6 +64,7 @@ let kElementSectionCode = 9; // Elements section ...@@ -64,6 +64,7 @@ let kElementSectionCode = 9; // Elements section
let kCodeSectionCode = 10; // Function code let kCodeSectionCode = 10; // Function code
let kDataSectionCode = 11; // Data segments let kDataSectionCode = 11; // Data segments
let kNameSectionCode = 12; // Name section (encoded as string) let kNameSectionCode = 12; // Name section (encoded as string)
let kExceptionSectionCode = 13; // Exception section (must appear before code section)
// Name section types // Name section types
let kModuleNameCode = 0; let kModuleNameCode = 0;
...@@ -357,8 +358,7 @@ function assertTraps(trap, code) { ...@@ -357,8 +358,7 @@ function assertTraps(trap, code) {
throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]); throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
} }
function assertWasmThrows(value, code) { function assertWasmThrows(values, code) {
assertEquals('number', typeof value);
try { try {
if (typeof code === 'function') { if (typeof code === 'function') {
code(); code();
...@@ -366,10 +366,11 @@ function assertWasmThrows(value, code) { ...@@ -366,10 +366,11 @@ function assertWasmThrows(value, code) {
eval(code); eval(code);
} }
} catch (e) { } catch (e) {
assertEquals('number', typeof e); // TODO(kschimpf): Extract values from the exception.
assertEquals(value, e); let e_values = [];
assertEquals(values, e_values);
// Success. // Success.
return; return;
} }
throw new MjsUnitAssertionError('Did not throw, expected: ' + value); throw new MjsUnitAssertionError('Did not throw, expected: ' + values);
} }
...@@ -153,6 +153,7 @@ class WasmModuleBuilder { ...@@ -153,6 +153,7 @@ class WasmModuleBuilder {
this.imports = []; this.imports = [];
this.exports = []; this.exports = [];
this.globals = []; this.globals = [];
this.exceptions = [];
this.functions = []; this.functions = [];
this.function_table = []; this.function_table = [];
this.function_table_length = 0; this.function_table_length = 0;
...@@ -208,6 +209,13 @@ class WasmModuleBuilder { ...@@ -208,6 +209,13 @@ class WasmModuleBuilder {
return glob; return glob;
} }
addException(type) {
if (type.results.length != 0)
throw new Error('Invalid exception signature: ' + type);
this.exceptions.push(type);
return this.exceptions.length - 1;
}
addFunction(name, type) { addFunction(name, type) {
let type_index = (typeof type) == "number" ? type : this.addType(type); let type_index = (typeof type) == "number" ? type : this.addType(type);
let func = new WasmFunctionBuilder(this, name, type_index); let func = new WasmFunctionBuilder(this, name, type_index);
...@@ -487,6 +495,20 @@ class WasmModuleBuilder { ...@@ -487,6 +495,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.enit_u8(param);
}
}
});
}
// Add function bodies. // Add function bodies.
if (wasm.functions.length > 0) { if (wasm.functions.length > 0) {
// emit function bodies // emit function bodies
......
...@@ -186,6 +186,9 @@ class FunctionBodyDecoderTest : public TestWithZone { ...@@ -186,6 +186,9 @@ class FunctionBodyDecoderTest : public TestWithZone {
}; };
namespace { namespace {
constexpr size_t kMaxByteSizedLeb128 = 127;
// A helper for tests that require a module environment for functions, // A helper for tests that require a module environment for functions,
// globals, or memories. // globals, or memories.
class TestModuleEnv : public ModuleEnv { class TestModuleEnv : public ModuleEnv {
...@@ -196,12 +199,12 @@ class TestModuleEnv : public ModuleEnv { ...@@ -196,12 +199,12 @@ class TestModuleEnv : public ModuleEnv {
} }
byte AddGlobal(ValueType type, bool mutability = true) { byte AddGlobal(ValueType type, bool mutability = true) {
mod.globals.push_back({type, mutability, WasmInitExpr(), 0, false, false}); mod.globals.push_back({type, mutability, WasmInitExpr(), 0, false, false});
CHECK(mod.globals.size() <= 127); CHECK(mod.globals.size() <= kMaxByteSizedLeb128);
return static_cast<byte>(mod.globals.size() - 1); return static_cast<byte>(mod.globals.size() - 1);
} }
byte AddSignature(FunctionSig* sig) { byte AddSignature(FunctionSig* sig) {
mod.signatures.push_back(sig); mod.signatures.push_back(sig);
CHECK(mod.signatures.size() <= 127); CHECK(mod.signatures.size() <= kMaxByteSizedLeb128);
return static_cast<byte>(mod.signatures.size() - 1); return static_cast<byte>(mod.signatures.size() - 1);
} }
byte AddFunction(FunctionSig* sig) { byte AddFunction(FunctionSig* sig) {
...@@ -212,7 +215,7 @@ class TestModuleEnv : public ModuleEnv { ...@@ -212,7 +215,7 @@ class TestModuleEnv : public ModuleEnv {
{0, 0}, // code {0, 0}, // code
false, // import false, // import
false}); // export false}); // export
CHECK(mod.functions.size() <= 127); CHECK(mod.functions.size() <= kMaxByteSizedLeb128);
return static_cast<byte>(mod.functions.size() - 1); return static_cast<byte>(mod.functions.size() - 1);
} }
byte AddImport(FunctionSig* sig) { byte AddImport(FunctionSig* sig) {
...@@ -220,6 +223,11 @@ class TestModuleEnv : public ModuleEnv { ...@@ -220,6 +223,11 @@ class TestModuleEnv : public ModuleEnv {
mod.functions[result].imported = true; mod.functions[result].imported = true;
return result; return result;
} }
byte AddException(WasmExceptionSig* sig) {
mod.exceptions.emplace_back(sig);
CHECK(mod.signatures.size() <= kMaxByteSizedLeb128);
return static_cast<byte>(mod.exceptions.size() - 1);
}
void InitializeMemory() { void InitializeMemory() {
mod.has_memory = true; mod.has_memory = true;
...@@ -2233,28 +2241,53 @@ TEST_F(FunctionBodyDecoderTest, Select_TypeCheck) { ...@@ -2233,28 +2241,53 @@ TEST_F(FunctionBodyDecoderTest, Select_TypeCheck) {
TEST_F(FunctionBodyDecoderTest, Throw) { TEST_F(FunctionBodyDecoderTest, Throw) {
EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
// TODO(kschimpf): Need to fix throw to use declared exception. TestModuleEnv module_env;
EXPECT_FAILURE(v_i, WASM_GET_LOCAL(0), kExprThrow); module = &module_env;
module_env.AddException(sigs.v_v());
module_env.AddException(sigs.v_i());
AddLocals(kWasmI32, 1);
EXPECT_VERIFIES(v_v, kExprThrow, 0);
// exception index out of range.
EXPECT_FAILURE(v_v, kExprThrow, 2);
// TODO(kschimpf): Fix when we can create exceptions with values.
EXPECT_FAILURE(v_v, WASM_I32V(0), kExprThrow, 1);
EXPECT_FAILURE(i_d, WASM_GET_LOCAL(0), kExprThrow, WASM_I32V(0)); // TODO(kschimpf): Add more tests.
EXPECT_FAILURE(i_f, WASM_GET_LOCAL(0), kExprThrow, WASM_I32V(0));
EXPECT_FAILURE(l_l, WASM_GET_LOCAL(0), kExprThrow, WASM_I64V(0));
} }
TEST_F(FunctionBodyDecoderTest, ThrowUnreachable) { TEST_F(FunctionBodyDecoderTest, ThrowUnreachable) {
// TODO(titzer): unreachable code after throw should validate. // TODO(titzer): unreachable code after throw should validate.
// EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
// EXPECT_VERIFIES(v_i, WASM_GET_LOCAL(0), kExprThrow, kExprSetLocal, 0); TestModuleEnv module_env;
module = &module_env;
module_env.AddException(sigs.v_v());
module_env.AddException(sigs.v_i());
AddLocals(kWasmI32, 1);
EXPECT_VERIFIES(i_i, kExprThrow, 0, WASM_GET_LOCAL(0));
// TODO(kschimpf): Add more (block-level) tests of unreachable to see
// if they validate.
} }
#define WASM_TRY_OP kExprTry, kLocalVoid #define WASM_TRY_OP kExprTry, kLocalVoid
#define WASM_CATCH(local) kExprCatch, static_cast<byte>(local) #define WASM_CATCH(index) kExprCatch, static_cast<byte>(index)
TEST_F(FunctionBodyDecoderTest, TryCatch) { TEST_F(FunctionBodyDecoderTest, TryCatch) {
EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
TestModuleEnv module_env;
module = &module_env;
module_env.AddException(sigs.v_v());
module_env.AddException(sigs.v_v());
// TODO(kschimpf): Need to fix catch to use declared exception. // TODO(kschimpf): Need to fix catch to use declared exception.
EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0), kExprEnd); EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(0), kExprEnd);
// Missing catch. // Missing catch.
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd); EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd);
...@@ -2263,7 +2296,8 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) { ...@@ -2263,7 +2296,8 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0)); EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0));
// Double catch. // Double catch.
EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0), WASM_CATCH(0), kExprEnd); // TODO(kschimpf): Fix this to verify.
EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0), WASM_CATCH(1), kExprEnd);
} }
TEST_F(FunctionBodyDecoderTest, MultiValBlock1) { TEST_F(FunctionBodyDecoderTest, MultiValBlock1) {
...@@ -2416,7 +2450,7 @@ TEST_F(WasmOpcodeLengthTest, Statements) { ...@@ -2416,7 +2450,7 @@ TEST_F(WasmOpcodeLengthTest, Statements) {
EXPECT_LENGTH(1, kExprSelect); EXPECT_LENGTH(1, kExprSelect);
EXPECT_LENGTH(2, kExprBr); EXPECT_LENGTH(2, kExprBr);
EXPECT_LENGTH(2, kExprBrIf); EXPECT_LENGTH(2, kExprBrIf);
EXPECT_LENGTH(1, kExprThrow); EXPECT_LENGTH(2, kExprThrow);
EXPECT_LENGTH(2, kExprTry); EXPECT_LENGTH(2, kExprTry);
EXPECT_LENGTH(2, kExprCatch); EXPECT_LENGTH(2, kExprCatch);
} }
......
...@@ -50,10 +50,16 @@ namespace wasm { ...@@ -50,10 +50,16 @@ 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) \ #define SECTION_EXCEPTIONS(size) SECTION(Exception, 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 FAIL_IF_NO_EXPERIMENTAL_EH(data) \
do { \
ModuleResult result = DecodeModule((data), (data) + sizeof((data))); \
EXPECT_FALSE(result.ok()); \
EXPECT_EQ(0u, result.val->exceptions.size()); \
} while (0)
#define X1(...) __VA_ARGS__ #define X1(...) __VA_ARGS__
#define X2(...) __VA_ARGS__, __VA_ARGS__ #define X2(...) __VA_ARGS__, __VA_ARGS__
#define X3(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__ #define X3(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__
...@@ -369,31 +375,22 @@ TEST_F(WasmModuleVerifyTest, ZeroExceptions) { ...@@ -369,31 +375,22 @@ TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
static const byte data[] = { static const byte data[] = {
SECTION_EXCEPTIONS(1), 0, SECTION_EXCEPTIONS(1), 0,
}; };
FAIL_IF_NO_EXPERIMENTAL_EH(data);
{
// Should decode exception section with no exceptions
EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result); EXPECT_OK(result);
EXPECT_EQ(0u, result.val->exceptions.size()); 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) { TEST_F(WasmModuleVerifyTest, OneI32Exception) {
static const byte data[] = { static const byte data[] = {
SECTION_EXCEPTIONS(3), 1, SECTION_EXCEPTIONS(3), 1,
1, // except[0] (i32) // except[0] (i32)
kLocalI32, 1, kLocalI32,
}; };
FAIL_IF_NO_EXPERIMENTAL_EH(data);
{
// Should decode to exactly one exception
EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result); EXPECT_OK(result);
...@@ -402,25 +399,16 @@ TEST_F(WasmModuleVerifyTest, OneI32Exception) { ...@@ -402,25 +399,16 @@ TEST_F(WasmModuleVerifyTest, OneI32Exception) {
const WasmException& e0 = result.val->exceptions.front(); const WasmException& e0 = result.val->exceptions.front();
EXPECT_EQ(1u, e0.sig->parameter_count()); EXPECT_EQ(1u, e0.sig->parameter_count());
EXPECT_EQ(MachineRepresentation::kWord32, e0.sig->GetParam(0)); 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) { TEST_F(WasmModuleVerifyTest, TwoExceptions) {
static const byte data[] = {SECTION_EXCEPTIONS(6), static const byte data[] = {SECTION_EXCEPTIONS(6), 2,
2, // except[0] (f32, i64)
2, // except[0] (f32, i64) 2, kLocalF32, kLocalI64,
kLocalF32, // except[1] (i32)
kLocalI64, 1, kLocalI32};
1, // except[1] (i32) FAIL_IF_NO_EXPERIMENTAL_EH(data);
kLocalI32};
{
// Should decode to exactly two exceptions
EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_OK(result); EXPECT_OK(result);
...@@ -429,32 +417,20 @@ TEST_F(WasmModuleVerifyTest, TwoExceptions) { ...@@ -429,32 +417,20 @@ TEST_F(WasmModuleVerifyTest, TwoExceptions) {
EXPECT_EQ(2u, e0.sig->parameter_count()); EXPECT_EQ(2u, e0.sig->parameter_count());
EXPECT_EQ(MachineRepresentation::kFloat32, e0.sig->GetParam(0)); EXPECT_EQ(MachineRepresentation::kFloat32, e0.sig->GetParam(0));
EXPECT_EQ(MachineRepresentation::kWord64, e0.sig->GetParam(1)); EXPECT_EQ(MachineRepresentation::kWord64, e0.sig->GetParam(1));
} const WasmException& e1 = result.val->exceptions.back();
{ EXPECT_EQ(MachineRepresentation::kWord32, e1.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, Exception_invalid_type) { TEST_F(WasmModuleVerifyTest, Exception_invalid_type) {
static const byte data[] = {SECTION_EXCEPTIONS(3), 1, static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
1, // except[0] (?) // except[0] (?)
64}; 1, 64};
FAIL_IF_NO_EXPERIMENTAL_EH(data);
{
// Should fail decoding exception section. // Should fail decoding exception section.
EXPERIMENTAL_FLAG_SCOPE(eh); EXPERIMENTAL_FLAG_SCOPE(eh);
ModuleResult result = DecodeModule(data, data + sizeof(data)); ModuleResult result = DecodeModule(data, data + sizeof(data));
EXPECT_FALSE(result.ok()); 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) {
......
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