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/591910 Reviewed-by: Brad Nelson <bradnelson@chromium.org> Commit-Queue: Karl Schimpf <kschimpf@chromium.org> Cr-Commit-Position: refs/heads/master@{#47087}
This commit is contained in:
parent
35c923cc10
commit
470a10015d
@ -1830,12 +1830,18 @@ Node* WasmGraphBuilder::Throw(Node* input) {
|
||||
arraysize(parameters));
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::Rethrow() {
|
||||
SetNeedsStackCheck();
|
||||
Node* result = BuildCallToRuntime(Runtime::kWasmRethrow, nullptr, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) {
|
||||
SetNeedsStackCheck();
|
||||
CommonOperatorBuilder* common = jsgraph()->common();
|
||||
|
||||
Node* parameters[] = {input}; // caught value
|
||||
Node* value = BuildCallToRuntime(Runtime::kWasmGetCaughtExceptionValue,
|
||||
Node* value = BuildCallToRuntime(Runtime::kWasmSetCaughtExceptionValue,
|
||||
parameters, arraysize(parameters));
|
||||
|
||||
Node* is_smi;
|
||||
|
@ -168,6 +168,7 @@ class WasmGraphBuilder {
|
||||
wasm::WasmCodePosition position = wasm::kNoCodePosition);
|
||||
Node* GrowMemory(Node* input);
|
||||
Node* Throw(Node* input);
|
||||
Node* Rethrow();
|
||||
Node* Catch(Node* input, wasm::WasmCodePosition position);
|
||||
unsigned InputCount(Node* node);
|
||||
bool IsPhiWithMerge(Node* phi, Node* merge);
|
||||
|
@ -47,6 +47,17 @@ bool Isolate::has_pending_exception() {
|
||||
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() {
|
||||
thread_local_top_.pending_message_obj_ = heap_.the_hole_value();
|
||||
|
@ -103,6 +103,7 @@ void ThreadLocalTop::InitializeInternal() {
|
||||
// These members are re-initialized later after deserialization
|
||||
// is complete.
|
||||
pending_exception_ = NULL;
|
||||
wasm_caught_exception_ = NULL;
|
||||
rethrowing_message_ = false;
|
||||
pending_message_obj_ = NULL;
|
||||
scheduled_exception_ = NULL;
|
||||
@ -215,6 +216,7 @@ void Isolate::IterateThread(ThreadVisitor* v, char* t) {
|
||||
void Isolate::Iterate(RootVisitor* v, ThreadLocalTop* thread) {
|
||||
// Visit the roots from the top for a given thread.
|
||||
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, bit_cast<Object**>(&(thread->context_)));
|
||||
v->VisitRootPointer(Root::kTop, &thread->scheduled_exception_);
|
||||
|
@ -326,6 +326,9 @@ class ThreadLocalTop BASE_EMBEDDED {
|
||||
Context* context_;
|
||||
ThreadId thread_id_;
|
||||
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.
|
||||
Context* pending_handler_context_;
|
||||
@ -602,6 +605,11 @@ class Isolate {
|
||||
inline void set_pending_exception(Object* exception_obj);
|
||||
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)
|
||||
|
||||
inline bool has_pending_exception();
|
||||
|
@ -142,6 +142,8 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_WasmThrow) {
|
||||
// TODO(kschimpf): Change this to build a runtime exception with
|
||||
// wasm properties, instead of just an integer.
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_SMI_ARG_CHECKED(lower, 0);
|
||||
@ -156,7 +158,15 @@ RUNTIME_FUNCTION(Runtime_WasmThrow) {
|
||||
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);
|
||||
DCHECK_EQ(1, args.length());
|
||||
Object* exception = args[0];
|
||||
@ -164,6 +174,7 @@ RUNTIME_FUNCTION(Runtime_WasmGetCaughtExceptionValue) {
|
||||
// Number or a Smi (which we have just converted to a Number.) This logic
|
||||
// lives in Isolate::is_catchable_by_wasm(Object*).
|
||||
CHECK(exception->IsNumber());
|
||||
isolate->set_wasm_caught_exception(exception);
|
||||
return exception;
|
||||
}
|
||||
|
||||
|
@ -634,7 +634,8 @@ namespace internal {
|
||||
F(ThrowWasmStackOverflow, 0, 1) \
|
||||
F(WasmThrowTypeError, 0, 1) \
|
||||
F(WasmThrow, 2, 1) \
|
||||
F(WasmGetCaughtExceptionValue, 1, 1) \
|
||||
F(WasmRethrow, 0, 1) \
|
||||
F(WasmSetCaughtExceptionValue, 1, 1) \
|
||||
F(WasmRunInterpreter, 3, 1) \
|
||||
F(WasmStackGuard, 0, 1) \
|
||||
F(SetThreadInWasm, 0, 1) \
|
||||
|
@ -13,6 +13,7 @@ namespace internal {
|
||||
namespace wasm {
|
||||
|
||||
struct WasmGlobal;
|
||||
struct WasmException;
|
||||
|
||||
// Use this macro to check a condition if checked == true, and DCHECK the
|
||||
// condition otherwise.
|
||||
@ -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>
|
||||
struct ImmI32Operand {
|
||||
int32_t value;
|
||||
|
@ -44,9 +44,8 @@ namespace wasm {
|
||||
break; \
|
||||
}
|
||||
|
||||
#define PROTOTYPE_NOT_FUNCTIONAL(opcode) \
|
||||
errorf(pc_, "Prototype still not functional: %s", \
|
||||
WasmOpcodes::OpcodeName(opcode));
|
||||
#define OPCODE_ERROR(opcode, message) \
|
||||
(errorf(pc_, "%s: %s", WasmOpcodes::OpcodeName(opcode), (message)))
|
||||
|
||||
// An SsaEnv environment carries the current local variable renaming
|
||||
// as well as the current effect and control dependency in the TF graph.
|
||||
@ -82,8 +81,10 @@ struct Value {
|
||||
struct TryInfo : public ZoneObject {
|
||||
SsaEnv* catch_env;
|
||||
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 {
|
||||
@ -283,6 +284,15 @@ class WasmDecoder : public Decoder {
|
||||
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) {
|
||||
if (module_ != nullptr && operand.index < module_->globals.size()) {
|
||||
operand.global = &module_->globals[operand.index];
|
||||
@ -462,10 +472,15 @@ class WasmDecoder : public Decoder {
|
||||
return 1 + operand.length;
|
||||
}
|
||||
|
||||
case kExprThrow:
|
||||
case kExprCatch: {
|
||||
ExceptionIndexOperand<true> operand(decoder, pc);
|
||||
return 1 + operand.length;
|
||||
}
|
||||
|
||||
case kExprSetLocal:
|
||||
case kExprTeeLocal:
|
||||
case kExprGetLocal:
|
||||
case kExprCatch: {
|
||||
case kExprGetLocal: {
|
||||
LocalIndexOperand<true> operand(decoder, pc);
|
||||
return 1 + operand.length;
|
||||
}
|
||||
@ -752,6 +767,13 @@ class WasmFullDecoder : public WasmDecoder {
|
||||
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.
|
||||
void DecodeFunctionBody() {
|
||||
TRACE("wasm-decode %p...%p (module+%u, %d bytes) %s\n",
|
||||
@ -808,15 +830,20 @@ class WasmFullDecoder : public WasmDecoder {
|
||||
case kExprRethrow: {
|
||||
// TODO(kschimpf): Implement.
|
||||
CHECK_PROTOTYPE_OPCODE(eh);
|
||||
PROTOTYPE_NOT_FUNCTIONAL(opcode);
|
||||
OPCODE_ERROR(opcode, "not implemented yet");
|
||||
break;
|
||||
}
|
||||
case kExprThrow: {
|
||||
// TODO(kschimpf): Fix to use type signature of exception.
|
||||
CHECK_PROTOTYPE_OPCODE(eh);
|
||||
PROTOTYPE_NOT_FUNCTIONAL(opcode);
|
||||
Value value = Pop(0, kWasmI32);
|
||||
BUILD(Throw, value.node);
|
||||
ExceptionIndexOperand<true> operand(this, pc_);
|
||||
len = 1 + operand.length;
|
||||
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
|
||||
// (reachable) runtime call instead of connecting it directly to
|
||||
// end.
|
||||
@ -838,49 +865,62 @@ class WasmFullDecoder : public WasmDecoder {
|
||||
case kExprCatch: {
|
||||
// TODO(kschimpf): Fix to use type signature of exception.
|
||||
CHECK_PROTOTYPE_OPCODE(eh);
|
||||
PROTOTYPE_NOT_FUNCTIONAL(opcode);
|
||||
LocalIndexOperand<true> operand(this, pc_);
|
||||
ExceptionIndexOperand<true> operand(this, pc_);
|
||||
len = 1 + operand.length;
|
||||
|
||||
if (!Validate(pc_, operand)) break;
|
||||
|
||||
if (control_.empty()) {
|
||||
error("catch does not match any try");
|
||||
break;
|
||||
}
|
||||
|
||||
Control* c = &control_.back();
|
||||
DCHECK_NOT_NULL(c->try_info);
|
||||
|
||||
if (!c->is_try()) {
|
||||
error("catch does not match any try");
|
||||
break;
|
||||
}
|
||||
|
||||
if (c->try_info->catch_env == nullptr) {
|
||||
error(pc_, "catch already present for try with catch");
|
||||
if (c->try_info->catch_count > 0) {
|
||||
OPCODE_ERROR(opcode, "multiple catch blocks not implemented");
|
||||
break;
|
||||
}
|
||||
++c->try_info->catch_count;
|
||||
|
||||
FallThruTo(c);
|
||||
stack_.resize(c->stack_depth);
|
||||
|
||||
DCHECK_NOT_NULL(c->try_info);
|
||||
SsaEnv* catch_env = c->try_info->catch_env;
|
||||
c->try_info->catch_env = nullptr;
|
||||
SetEnv("catch:begin", catch_env);
|
||||
|
||||
current_catch_ = c->previous_catch;
|
||||
|
||||
if (Validate(pc_, operand)) {
|
||||
if (ssa_env_->locals) {
|
||||
TFNode* exception_as_i32 =
|
||||
BUILD(Catch, c->try_info->exception, position());
|
||||
ssa_env_->locals[operand.index] = exception_as_i32;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the exception and see if wanted exception.
|
||||
TFNode* exception_as_i32 =
|
||||
BUILD(Catch, c->try_info->exception, position());
|
||||
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;
|
||||
}
|
||||
case kExprCatchAll: {
|
||||
// TODO(kschimpf): Implement.
|
||||
CHECK_PROTOTYPE_OPCODE(eh);
|
||||
PROTOTYPE_NOT_FUNCTIONAL(opcode);
|
||||
OPCODE_ERROR(opcode, "not implemented yet");
|
||||
break;
|
||||
}
|
||||
case kExprLoop: {
|
||||
@ -969,10 +1009,17 @@ class WasmFullDecoder : public WasmDecoder {
|
||||
name = "try:end";
|
||||
|
||||
// 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");
|
||||
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);
|
||||
SetEnv(name, c->end_env);
|
||||
|
@ -31,9 +31,9 @@ namespace wasm {
|
||||
#endif
|
||||
namespace {
|
||||
|
||||
const char kNameString[] = "name";
|
||||
|
||||
const char kExceptionString[] = "exception";
|
||||
constexpr char kNameString[] = "name";
|
||||
constexpr char kExceptionString[] = "exception";
|
||||
constexpr char kUnknownString[] = "<unknown>";
|
||||
|
||||
template <size_t N>
|
||||
constexpr size_t num_chars(const char (&)[N]) {
|
||||
@ -71,9 +71,10 @@ const char* SectionName(SectionCode code) {
|
||||
case kNameSectionCode:
|
||||
return kNameString;
|
||||
case kExceptionSectionCode:
|
||||
return kExceptionString;
|
||||
if (FLAG_experimental_wasm_eh) return kExceptionString;
|
||||
return kUnknownString;
|
||||
default:
|
||||
return "<unknown>";
|
||||
return kUnknownString;
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,11 +219,6 @@ class WasmSectionIterator {
|
||||
strncmp(reinterpret_cast<const char*>(section_name_start),
|
||||
kNameString, num_chars(kNameString)) == 0) {
|
||||
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)) {
|
||||
decoder_.errorf(decoder_.pc(), "unknown section code #0x%02x",
|
||||
@ -332,9 +328,25 @@ class ModuleDecoder : public Decoder {
|
||||
errorf(pc(), "unexpected section: %s", SectionName(section_code));
|
||||
return;
|
||||
}
|
||||
if (section_code != kUnknownSectionCode) {
|
||||
next_section_ = section_code;
|
||||
++next_section_;
|
||||
|
||||
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_;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (section_code) {
|
||||
@ -377,7 +389,11 @@ class ModuleDecoder : public Decoder {
|
||||
DecodeNameSection();
|
||||
break;
|
||||
case kExceptionSectionCode:
|
||||
DecodeExceptionSection();
|
||||
if (FLAG_experimental_wasm_eh) {
|
||||
DecodeExceptionSection();
|
||||
} else {
|
||||
errorf(pc(), "unexpected section: %s", SectionName(section_code));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
errorf(pc(), "unexpected section: %s", SectionName(section_code));
|
||||
@ -877,6 +893,7 @@ class ModuleDecoder : public Decoder {
|
||||
Counters* counters_ = nullptr;
|
||||
// The type section is the first section in a module.
|
||||
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
|
||||
// increment it. This static_assert should make sure that SectionCode does not
|
||||
// get bigger than uint8_t accidentially.
|
||||
|
@ -34,16 +34,17 @@ enum SectionCode : int8_t {
|
||||
kCodeSectionCode = 10, // Function code
|
||||
kDataSectionCode = 11, // Data segments
|
||||
kNameSectionCode = 12, // Name section (encoded as a string)
|
||||
kExceptionSectionCode = 13, // Exception section (encoded as a string)
|
||||
kExceptionSectionCode = 13, // Exception section
|
||||
|
||||
// Helper values
|
||||
kFirstSectionInModule = kTypeSectionCode,
|
||||
kLastKnownModuleSection = kExceptionSectionCode,
|
||||
};
|
||||
|
||||
enum NameSectionType : uint8_t { kModule = 0, kFunction = 1, kLocal = 2 };
|
||||
|
||||
inline bool IsValidSectionCode(uint8_t byte) {
|
||||
return kTypeSectionCode <= byte && byte <= kDataSectionCode;
|
||||
return kTypeSectionCode <= byte && byte <= kLastKnownModuleSection;
|
||||
}
|
||||
|
||||
const char* SectionName(SectionCode code);
|
||||
|
@ -143,12 +143,17 @@ void wasm::PrintWasmText(const WasmModule *module,
|
||||
}
|
||||
case kExprGetLocal:
|
||||
case kExprSetLocal:
|
||||
case kExprTeeLocal:
|
||||
case kExprCatch: {
|
||||
case kExprTeeLocal: {
|
||||
LocalIndexOperand<false> operand(&i, i.pc());
|
||||
os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
|
||||
break;
|
||||
}
|
||||
case kExprThrow:
|
||||
case kExprCatch: {
|
||||
ExceptionIndexOperand<false> operand(&i, i.pc());
|
||||
os << WasmOpcodes::OpcodeName(opcode) << ' ' << operand.index;
|
||||
break;
|
||||
}
|
||||
case kExprGetGlobal:
|
||||
case kExprSetGlobal: {
|
||||
GlobalIndexOperand<false> operand(&i, i.pc());
|
||||
@ -183,7 +188,6 @@ void wasm::PrintWasmText(const WasmModule *module,
|
||||
case kExprGrowMemory:
|
||||
case kExprDrop:
|
||||
case kExprSelect:
|
||||
case kExprThrow:
|
||||
os << WasmOpcodes::OpcodeName(opcode);
|
||||
break;
|
||||
|
||||
|
@ -177,9 +177,6 @@
|
||||
|
||||
# BUG(v8:6306).
|
||||
'wasm/huge-memory': [SKIP],
|
||||
|
||||
# BUG(v8:6577).
|
||||
'wasm/exceptions': [SKIP],
|
||||
}], # ALWAYS
|
||||
|
||||
['novfp3 == True', {
|
||||
|
@ -7,6 +7,73 @@
|
||||
load("test/mjsunit/wasm/wasm-constants.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.
|
||||
var test_throw = (function () {
|
||||
var builder = new WasmModuleBuilder();
|
||||
@ -229,16 +296,16 @@ var test_catch = (function () {
|
||||
kExprI32Eq,
|
||||
kExprIf, kWasmStmt,
|
||||
kExprGetLocal, 2,
|
||||
kExprI32Const, /*64=*/ 192, 0,
|
||||
kExprI32Const, / *64=* / 192, 0,
|
||||
kExprI32Ior,
|
||||
kExprThrow,
|
||||
kExprUnreachable,
|
||||
kExprEnd,
|
||||
kExprI32Const, /*128=*/ 128, 1,
|
||||
kExprI32Const, / *128=* / 128, 1,
|
||||
kExprI32Ior,
|
||||
kExprCatch, 1,
|
||||
kExprGetLocal, 1,
|
||||
kExprI32Const, /*256=*/ 128, 2,
|
||||
kExprI32Const, / *256=* / 128, 2,
|
||||
kExprI32Ior,
|
||||
kExprEnd,
|
||||
])
|
||||
@ -252,7 +319,7 @@ var test_catch = (function () {
|
||||
kExprTry, kWasmI32,
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, kWasmThrowFunction,
|
||||
kExprI32Const, /*-1=*/ 127,
|
||||
kExprI32Const, / *-1=* / 127,
|
||||
kExprCatch, 1,
|
||||
kExprGetLocal, 1,
|
||||
kExprEnd
|
||||
@ -287,7 +354,7 @@ var test_catch = (function () {
|
||||
kExprGetLocal, 0,
|
||||
kExprI32Const, 0,
|
||||
kExprCallFunction, kFromIndirectCalleeHelper,
|
||||
kExprI32Const, /*-1=*/ 127,
|
||||
kExprI32Const, / *-1=* / 127,
|
||||
kExprCatch, 1,
|
||||
kExprGetLocal, 1,
|
||||
kExprEnd
|
||||
@ -301,7 +368,7 @@ var test_catch = (function () {
|
||||
kExprTry, kWasmI32,
|
||||
kExprGetLocal, 0,
|
||||
kExprCallFunction, kJSThrowI,
|
||||
kExprI32Const, /*-1=*/ 127,
|
||||
kExprI32Const, / *-1=* / 127,
|
||||
kExprCatch, 1,
|
||||
kExprGetLocal, 1,
|
||||
kExprEnd,
|
||||
@ -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.large_from_js, 1e+28);
|
||||
assertThrowsEquals(test_catch.exports.undefined_from_js, undefined);
|
||||
*/
|
||||
|
@ -52,18 +52,19 @@ let kDeclNoLocals = 0;
|
||||
|
||||
// Section declaration constants
|
||||
let kUnknownSectionCode = 0;
|
||||
let kTypeSectionCode = 1; // Function signature declarations
|
||||
let kImportSectionCode = 2; // Import declarations
|
||||
let kFunctionSectionCode = 3; // Function declarations
|
||||
let kTableSectionCode = 4; // Indirect function table and other tables
|
||||
let kMemorySectionCode = 5; // Memory attributes
|
||||
let kGlobalSectionCode = 6; // Global declarations
|
||||
let kExportSectionCode = 7; // Exports
|
||||
let kStartSectionCode = 8; // Start function declaration
|
||||
let kElementSectionCode = 9; // Elements section
|
||||
let kCodeSectionCode = 10; // Function code
|
||||
let kDataSectionCode = 11; // Data segments
|
||||
let kNameSectionCode = 12; // Name section (encoded as string)
|
||||
let kTypeSectionCode = 1; // Function signature declarations
|
||||
let kImportSectionCode = 2; // Import declarations
|
||||
let kFunctionSectionCode = 3; // Function declarations
|
||||
let kTableSectionCode = 4; // Indirect function table and other tables
|
||||
let kMemorySectionCode = 5; // Memory attributes
|
||||
let kGlobalSectionCode = 6; // Global declarations
|
||||
let kExportSectionCode = 7; // Exports
|
||||
let kStartSectionCode = 8; // Start function declaration
|
||||
let kElementSectionCode = 9; // Elements section
|
||||
let kCodeSectionCode = 10; // Function code
|
||||
let kDataSectionCode = 11; // Data segments
|
||||
let kNameSectionCode = 12; // Name section (encoded as string)
|
||||
let kExceptionSectionCode = 13; // Exception section (must appear before code section)
|
||||
|
||||
// Name section types
|
||||
let kModuleNameCode = 0;
|
||||
@ -357,8 +358,7 @@ function assertTraps(trap, code) {
|
||||
throw new MjsUnitAssertionError('Did not trap, expected: ' + kTrapMsgs[trap]);
|
||||
}
|
||||
|
||||
function assertWasmThrows(value, code) {
|
||||
assertEquals('number', typeof value);
|
||||
function assertWasmThrows(values, code) {
|
||||
try {
|
||||
if (typeof code === 'function') {
|
||||
code();
|
||||
@ -366,10 +366,11 @@ function assertWasmThrows(value, code) {
|
||||
eval(code);
|
||||
}
|
||||
} catch (e) {
|
||||
assertEquals('number', typeof e);
|
||||
assertEquals(value, e);
|
||||
// TODO(kschimpf): Extract values from the exception.
|
||||
let e_values = [];
|
||||
assertEquals(values, e_values);
|
||||
// Success.
|
||||
return;
|
||||
}
|
||||
throw new MjsUnitAssertionError('Did not throw, expected: ' + value);
|
||||
throw new MjsUnitAssertionError('Did not throw, expected: ' + values);
|
||||
}
|
||||
|
@ -153,6 +153,7 @@ class WasmModuleBuilder {
|
||||
this.imports = [];
|
||||
this.exports = [];
|
||||
this.globals = [];
|
||||
this.exceptions = [];
|
||||
this.functions = [];
|
||||
this.function_table = [];
|
||||
this.function_table_length = 0;
|
||||
@ -208,6 +209,13 @@ class WasmModuleBuilder {
|
||||
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) {
|
||||
let type_index = (typeof type) == "number" ? type : this.addType(type);
|
||||
let func = new WasmFunctionBuilder(this, name, type_index);
|
||||
@ -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.
|
||||
if (wasm.functions.length > 0) {
|
||||
// emit function bodies
|
||||
|
@ -186,6 +186,9 @@ class FunctionBodyDecoderTest : public TestWithZone {
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr size_t kMaxByteSizedLeb128 = 127;
|
||||
|
||||
// A helper for tests that require a module environment for functions,
|
||||
// globals, or memories.
|
||||
class TestModuleEnv : public ModuleEnv {
|
||||
@ -196,12 +199,12 @@ class TestModuleEnv : public ModuleEnv {
|
||||
}
|
||||
byte AddGlobal(ValueType type, bool mutability = true) {
|
||||
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);
|
||||
}
|
||||
byte AddSignature(FunctionSig* sig) {
|
||||
mod.signatures.push_back(sig);
|
||||
CHECK(mod.signatures.size() <= 127);
|
||||
CHECK(mod.signatures.size() <= kMaxByteSizedLeb128);
|
||||
return static_cast<byte>(mod.signatures.size() - 1);
|
||||
}
|
||||
byte AddFunction(FunctionSig* sig) {
|
||||
@ -212,7 +215,7 @@ class TestModuleEnv : public ModuleEnv {
|
||||
{0, 0}, // code
|
||||
false, // import
|
||||
false}); // export
|
||||
CHECK(mod.functions.size() <= 127);
|
||||
CHECK(mod.functions.size() <= kMaxByteSizedLeb128);
|
||||
return static_cast<byte>(mod.functions.size() - 1);
|
||||
}
|
||||
byte AddImport(FunctionSig* sig) {
|
||||
@ -220,6 +223,11 @@ class TestModuleEnv : public ModuleEnv {
|
||||
mod.functions[result].imported = true;
|
||||
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() {
|
||||
mod.has_memory = true;
|
||||
@ -2233,28 +2241,53 @@ TEST_F(FunctionBodyDecoderTest, Select_TypeCheck) {
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, Throw) {
|
||||
EXPERIMENTAL_FLAG_SCOPE(eh);
|
||||
// TODO(kschimpf): Need to fix throw to use declared exception.
|
||||
EXPECT_FAILURE(v_i, WASM_GET_LOCAL(0), kExprThrow);
|
||||
TestModuleEnv module_env;
|
||||
module = &module_env;
|
||||
|
||||
EXPECT_FAILURE(i_d, WASM_GET_LOCAL(0), kExprThrow, WASM_I32V(0));
|
||||
EXPECT_FAILURE(i_f, WASM_GET_LOCAL(0), kExprThrow, WASM_I32V(0));
|
||||
EXPECT_FAILURE(l_l, WASM_GET_LOCAL(0), kExprThrow, WASM_I64V(0));
|
||||
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);
|
||||
|
||||
// TODO(kschimpf): Add more tests.
|
||||
}
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, ThrowUnreachable) {
|
||||
// TODO(titzer): unreachable code after throw should validate.
|
||||
// EXPERIMENTAL_FLAG_SCOPE(eh);
|
||||
// EXPECT_VERIFIES(v_i, WASM_GET_LOCAL(0), kExprThrow, kExprSetLocal, 0);
|
||||
EXPERIMENTAL_FLAG_SCOPE(eh);
|
||||
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_CATCH(local) kExprCatch, static_cast<byte>(local)
|
||||
#define WASM_CATCH(index) kExprCatch, static_cast<byte>(index)
|
||||
|
||||
TEST_F(FunctionBodyDecoderTest, TryCatch) {
|
||||
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.
|
||||
EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0), kExprEnd);
|
||||
EXPECT_VERIFIES(v_v, WASM_TRY_OP, WASM_CATCH(0), kExprEnd);
|
||||
|
||||
// Missing catch.
|
||||
EXPECT_FAILURE(v_v, WASM_TRY_OP, kExprEnd);
|
||||
@ -2263,7 +2296,8 @@ TEST_F(FunctionBodyDecoderTest, TryCatch) {
|
||||
EXPECT_FAILURE(v_i, WASM_TRY_OP, WASM_CATCH(0));
|
||||
|
||||
// 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) {
|
||||
@ -2416,7 +2450,7 @@ TEST_F(WasmOpcodeLengthTest, Statements) {
|
||||
EXPECT_LENGTH(1, kExprSelect);
|
||||
EXPECT_LENGTH(2, kExprBr);
|
||||
EXPECT_LENGTH(2, kExprBrIf);
|
||||
EXPECT_LENGTH(1, kExprThrow);
|
||||
EXPECT_LENGTH(2, kExprThrow);
|
||||
EXPECT_LENGTH(2, kExprTry);
|
||||
EXPECT_LENGTH(2, kExprCatch);
|
||||
}
|
||||
|
@ -50,10 +50,16 @@ namespace wasm {
|
||||
#define EMPTY_FUNCTION_SIGNATURES_SECTION SECTION(Function, 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_EXCEPTIONS(size) \
|
||||
SECTION(Unknown, size + 10), 9, 'e', 'x', 'c', 'e', 'p', 't', 'i', 'o', 'n'
|
||||
#define SECTION_EXCEPTIONS(size) SECTION(Exception, size)
|
||||
#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 X2(...) __VA_ARGS__, __VA_ARGS__
|
||||
#define X3(...) __VA_ARGS__, __VA_ARGS__, __VA_ARGS__
|
||||
@ -369,92 +375,62 @@ TEST_F(WasmModuleVerifyTest, ZeroExceptions) {
|
||||
static const byte data[] = {
|
||||
SECTION_EXCEPTIONS(1), 0,
|
||||
};
|
||||
FAIL_IF_NO_EXPERIMENTAL_EH(data);
|
||||
|
||||
{
|
||||
// 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());
|
||||
}
|
||||
EXPERIMENTAL_FLAG_SCOPE(eh);
|
||||
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,
|
||||
// except[0] (i32)
|
||||
1, kLocalI32,
|
||||
};
|
||||
FAIL_IF_NO_EXPERIMENTAL_EH(data);
|
||||
|
||||
{
|
||||
// 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());
|
||||
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());
|
||||
}
|
||||
const WasmException& e0 = result.val->exceptions.front();
|
||||
EXPECT_EQ(1u, e0.sig->parameter_count());
|
||||
EXPECT_EQ(MachineRepresentation::kWord32, e0.sig->GetParam(0));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
static const byte data[] = {SECTION_EXCEPTIONS(6), 2,
|
||||
// except[0] (f32, i64)
|
||||
2, kLocalF32, kLocalI64,
|
||||
// except[1] (i32)
|
||||
1, kLocalI32};
|
||||
FAIL_IF_NO_EXPERIMENTAL_EH(data);
|
||||
|
||||
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));
|
||||
const WasmException& e1 = result.val->exceptions.back();
|
||||
EXPECT_EQ(MachineRepresentation::kWord32, e1.sig->GetParam(0));
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, Exception_invalid_type) {
|
||||
static const byte data[] = {SECTION_EXCEPTIONS(3), 1,
|
||||
1, // except[0] (?)
|
||||
64};
|
||||
// except[0] (?)
|
||||
1, 64};
|
||||
FAIL_IF_NO_EXPERIMENTAL_EH(data);
|
||||
|
||||
{
|
||||
// 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());
|
||||
}
|
||||
// Should fail decoding exception section.
|
||||
EXPERIMENTAL_FLAG_SCOPE(eh);
|
||||
ModuleResult result = DecodeModule(data, data + sizeof(data));
|
||||
EXPECT_FALSE(result.ok());
|
||||
}
|
||||
|
||||
TEST_F(WasmModuleVerifyTest, OneSignature) {
|
||||
|
Loading…
Reference in New Issue
Block a user