Commit 28620d19 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

Revert "Add capability of throwing values in WASM"

This reverts commit 7b5a4022.

Reason for revert: GC stress-test failures exposed by 7742e534
https://build.chromium.org/p/client.v8/builders/V8%20Linux64%20GC%20Stress%20-%20custom%20snapshot/builds/15110/steps/Mjsunit/logs/exceptions


Original change's description:
> Add capability of throwing values in WASM
> 
> Extends the current implementation of WASM exceptions to be able to
> throw exceptions with values (not just tags).
> 
> An JS typed array (uint_16) is used to hold thrown values, so that the
> thrown values can be inspected in JS.
> 
> Bug: v8:6577
> Change-Id: I1007e79ceaffd64386b62562919cfbb920fc10c5
> Reviewed-on: https://chromium-review.googlesource.com/633866
> Commit-Queue: Karl Schimpf <kschimpf@chromium.org>
> Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
> Reviewed-by: Eric Holk <eholk@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#48001}

TBR=bbudge@chromium.org,mtrofin@chromium.org,eholk@chromium.org,clemensh@chromium.org,kschimpf@chromium.org

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: v8:6577
Change-Id: I8f545183c2d2abb1bf4a0b3ee23379f3754ffd55
Reviewed-on: https://chromium-review.googlesource.com/667019Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarMircea Trofin <mtrofin@chromium.org>
Commit-Queue: Bill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48050}
parent 483e353d
...@@ -55,8 +55,6 @@ namespace compiler { ...@@ -55,8 +55,6 @@ namespace compiler {
namespace { namespace {
constexpr uint32_t kBytesPerExceptionValuesArrayElement = 2;
void MergeControlToEnd(JSGraph* jsgraph, Node* node) { void MergeControlToEnd(JSGraph* jsgraph, Node* node) {
Graph* g = jsgraph->graph(); Graph* g = jsgraph->graph();
if (g->end()) { if (g->end()) {
...@@ -1810,97 +1808,11 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) { ...@@ -1810,97 +1808,11 @@ Node* WasmGraphBuilder::GrowMemory(Node* input) {
return result; return result;
} }
uint32_t WasmGraphBuilder::GetExceptionEncodedSize( Node* WasmGraphBuilder::Throw(Node* input) {
const wasm::WasmException* exception) const {
const wasm::WasmExceptionSig* sig = exception->sig;
uint32_t encoded_size = 0;
for (size_t i = 0; i < sig->parameter_count(); ++i) {
size_t byte_size = size_t(1) << ElementSizeLog2Of(sig->GetParam(i));
DCHECK_EQ(byte_size % kBytesPerExceptionValuesArrayElement, 0);
DCHECK_LE(1, byte_size / kBytesPerExceptionValuesArrayElement);
encoded_size += byte_size / kBytesPerExceptionValuesArrayElement;
}
return encoded_size;
}
Node* WasmGraphBuilder::Throw(uint32_t tag,
const wasm::WasmException* exception,
const Vector<Node*> values) {
SetNeedsStackCheck(); SetNeedsStackCheck();
uint32_t encoded_size = GetExceptionEncodedSize(exception); Node* parameters[] = {BuildChangeInt32ToSmi(input)};
Node* create_parameters[] = { return BuildCallToRuntime(Runtime::kWasmThrow, parameters,
BuildChangeUint32ToSmi(ConvertExceptionTagToRuntimeId(tag)), arraysize(parameters));
BuildChangeUint32ToSmi(Uint32Constant(encoded_size))};
Node* except =
BuildCallToRuntime(Runtime::kWasmThrowCreate, create_parameters,
arraysize(create_parameters));
uint32_t index = 0;
const wasm::WasmExceptionSig* sig = exception->sig;
MachineOperatorBuilder* m = jsgraph()->machine();
for (size_t i = 0; i < sig->parameter_count(); ++i) {
Node* value = values[i];
switch (sig->GetParam(i)) {
case wasm::kWasmF32:
value = graph()->NewNode(m->BitcastFloat32ToInt32(), value);
// Intentionally fall to next case.
case wasm::kWasmI32:
except = BuildEncodeException32BitValue(except, &index, value);
break;
case wasm::kWasmF64:
value = graph()->NewNode(m->BitcastFloat64ToInt64(), value);
// Intentionally fall to next case.
case wasm::kWasmI64: {
Node* upper32 = graph()->NewNode(
m->TruncateInt64ToInt32(),
Binop(wasm::kExprI64ShrU, value, Int64Constant(32)));
except = BuildEncodeException32BitValue(except, &index, upper32);
Node* lower32 = graph()->NewNode(m->TruncateInt64ToInt32(), value);
except = BuildEncodeException32BitValue(except, &index, lower32);
break;
}
default:
CHECK(false);
break;
}
}
DCHECK_EQ(encoded_size, index);
Node* throw_parameters[] = {except};
return BuildCallToRuntime(Runtime::kWasmThrow, throw_parameters,
arraysize(throw_parameters));
}
Node* WasmGraphBuilder::BuildEncodeException32BitValue(Node* except,
uint32_t* index,
Node* value) {
MachineOperatorBuilder* machine = jsgraph()->machine();
Node* upper_parameters[] = {
except, BuildChangeUint32ToSmi(Int32Constant(*index)),
BuildChangeUint32ToSmi(
graph()->NewNode(machine->Word32Shr(), value, Int32Constant(16))),
};
(*index)++;
except = BuildCallToRuntime(Runtime::kWasmExceptionSetElement,
upper_parameters, arraysize(upper_parameters));
Node* lower_parameters[] = {
except, BuildChangeUint32ToSmi(Int32Constant(*index)),
BuildChangeUint32ToSmi(graph()->NewNode(machine->Word32And(), value,
Int32Constant(0xFFFFu))),
};
(*index)++;
return BuildCallToRuntime(Runtime::kWasmExceptionSetElement, lower_parameters,
arraysize(lower_parameters));
}
Node* WasmGraphBuilder::BuildDecodeException32BitValue(Node* const* values,
uint32_t* index) {
MachineOperatorBuilder* machine = jsgraph()->machine();
Node* upper = BuildChangeSmiToInt32(values[*index]);
(*index)++;
upper = graph()->NewNode(machine->Word32Shl(), upper, Int32Constant(16));
Node* lower = BuildChangeSmiToInt32(values[*index]);
(*index)++;
Node* value = graph()->NewNode(machine->Word32Or(), upper, lower);
return value;
} }
Node* WasmGraphBuilder::Rethrow() { Node* WasmGraphBuilder::Rethrow() {
...@@ -1909,80 +1821,15 @@ Node* WasmGraphBuilder::Rethrow() { ...@@ -1909,80 +1821,15 @@ Node* WasmGraphBuilder::Rethrow() {
return result; return result;
} }
Node* WasmGraphBuilder::Catch(Node* input) { Node* WasmGraphBuilder::Catch(Node* input, wasm::WasmCodePosition position) {
SetNeedsStackCheck(); SetNeedsStackCheck();
Node* parameters[] = {input}; Node* parameters[] = {input}; // caught value
return BuildCallToRuntime(Runtime::kWasmSetCaughtExceptionValue, parameters, Node* value = BuildCallToRuntime(Runtime::kWasmSetCaughtExceptionValue,
arraysize(parameters));
}
Node* WasmGraphBuilder::ConvertExceptionTagToRuntimeId(uint32_t tag) {
// TODO(kschimpf): Handle exceptions from different modules, when they are
// linked at runtime.
return Uint32Constant(tag);
}
Node* WasmGraphBuilder::GetExceptionRuntimeId(Node* exception) {
SetNeedsStackCheck();
Node* parameters[] = {exception};
return BuildChangeSmiToInt32(BuildCallToRuntime(
Runtime::kWasmGetExceptionRuntimeId, parameters, arraysize(parameters)));
}
Node** WasmGraphBuilder::GetExceptionValues(
const wasm::WasmException* except_decl, Node* exception,
wasm::WasmCodePosition position) {
// TODO(kschimpf): We need to move this code to the function-body-decoder.cc
// in order to build landing-pad (exception) edges in case the runtime
// call causes an exception.
// Start by getting the encoded values from the exception.
Node* parameters[] = {exception};
Node* enc_values = BuildCallToRuntime(Runtime::kWasmGetExceptionValuesArray,
parameters, arraysize(parameters));
uint32_t encoded_size = GetExceptionEncodedSize(except_decl);
Node** values = Buffer(encoded_size);
for (uint32_t i = 0; i < encoded_size; ++i) {
Node* parameters[] = {enc_values,
BuildChangeUint32ToSmi(Uint32Constant(i))};
values[i] = BuildCallToRuntime(Runtime::kWasmExceptionGetElement,
parameters, arraysize(parameters)); parameters, arraysize(parameters));
} parameters[0] = value;
value = BuildCallToRuntime(Runtime::kWasmGetExceptionTag, parameters,
// Now convert the leading entries to the corresponding parameter values. arraysize(parameters));
uint32_t index = 0; return BuildChangeSmiToInt32(value);
const wasm::WasmExceptionSig* sig = except_decl->sig;
for (size_t i = 0; i < sig->parameter_count(); ++i) {
Node* value = BuildDecodeException32BitValue(values, &index);
switch (wasm::ValueType type = sig->GetParam(i)) {
case wasm::kWasmF32: {
value = Unop(wasm::kExprF32ReinterpretI32, value);
break;
}
case wasm::kWasmI32:
break;
case wasm::kWasmF64:
case wasm::kWasmI64: {
Node* upper = Binop(wasm::kExprI64Shl,
Unop(wasm::kExprI64UConvertI32, value, position),
Int64Constant(32), position);
Node* lower =
Unop(wasm::kExprI64UConvertI32,
BuildDecodeException32BitValue(values, &index), position);
value = Binop(wasm::kExprI64Ior, upper, lower);
if (type == wasm::kWasmF64) {
value = Unop(wasm::kExprF64ReinterpretI64, value);
}
break;
}
default:
CHECK(false);
break;
}
values[i] = value;
}
DCHECK_EQ(index, encoded_size);
return values;
} }
Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right, Node* WasmGraphBuilder::BuildI32DivS(Node* left, Node* right,
...@@ -3256,7 +3103,7 @@ Node* WasmGraphBuilder::BuildCallToRuntimeWithContext(Runtime::FunctionId f, ...@@ -3256,7 +3103,7 @@ Node* WasmGraphBuilder::BuildCallToRuntimeWithContext(Runtime::FunctionId f,
inputs[count++] = jsgraph()->ExternalConstant( inputs[count++] = jsgraph()->ExternalConstant(
ExternalReference(f, jsgraph()->isolate())); // ref ExternalReference(f, jsgraph()->isolate())); // ref
inputs[count++] = jsgraph()->Int32Constant(fun->nargs); // arity inputs[count++] = jsgraph()->Int32Constant(fun->nargs); // arity
inputs[count++] = context; inputs[count++] = context; // context
inputs[count++] = *effect_; inputs[count++] = *effect_;
inputs[count++] = *control_; inputs[count++] = *control_;
......
...@@ -212,15 +212,9 @@ class WasmGraphBuilder { ...@@ -212,15 +212,9 @@ class WasmGraphBuilder {
Node* Unop(wasm::WasmOpcode opcode, Node* input, Node* Unop(wasm::WasmOpcode opcode, Node* input,
wasm::WasmCodePosition position = wasm::kNoCodePosition); wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* GrowMemory(Node* input); Node* GrowMemory(Node* input);
Node* Throw(uint32_t tag, const wasm::WasmException* exception, Node* Throw(Node* input);
const Vector<Node*> values);
Node* Rethrow(); Node* Rethrow();
Node* Catch(Node* input); Node* Catch(Node* input, wasm::WasmCodePosition position);
Node* ConvertExceptionTagToRuntimeId(uint32_t tag);
Node* GetExceptionRuntimeId(Node* exception);
Node** GetExceptionValues(
const wasm::WasmException* except_decl, Node* exception,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
unsigned InputCount(Node* node); unsigned InputCount(Node* node);
bool IsPhiWithMerge(Node* phi, Node* merge); bool IsPhiWithMerge(Node* phi, Node* merge);
bool ThrowsException(Node* node, Node** if_success, Node** if_exception); bool ThrowsException(Node* node, Node** if_success, Node** if_exception);
...@@ -475,11 +469,6 @@ class WasmGraphBuilder { ...@@ -475,11 +469,6 @@ class WasmGraphBuilder {
Node* BuildAsmjsLoadMem(MachineType type, Node* index); Node* BuildAsmjsLoadMem(MachineType type, Node* index);
Node* BuildAsmjsStoreMem(MachineType type, Node* index, Node* val); Node* BuildAsmjsStoreMem(MachineType type, Node* index, Node* val);
uint32_t GetExceptionEncodedSize(const wasm::WasmException* exception) const;
Node* BuildEncodeException32BitValue(Node* except, uint32_t* index,
Node* value);
Node* BuildDecodeException32BitValue(Node* const* values, uint32_t* index);
Node** Realloc(Node** buffer, size_t old_count, size_t new_count) { Node** Realloc(Node** buffer, size_t old_count, size_t new_count) {
Node** buf = Buffer(new_count); Node** buf = Buffer(new_count);
if (buf != buffer) memcpy(buf, buffer, old_count * sizeof(Node*)); if (buf != buffer) memcpy(buf, buffer, old_count * sizeof(Node*));
......
...@@ -202,7 +202,8 @@ ...@@ -202,7 +202,8 @@
V(will_handle_string, "willHandle") \ V(will_handle_string, "willHandle") \
V(writable_string, "writable") \ V(writable_string, "writable") \
V(year_string, "year") \ V(year_string, "year") \
V(zero_string, "0") V(zero_string, "0") \
V(WasmExceptionTag_string, "WasmExceptionTag")
#define PRIVATE_SYMBOL_LIST(V) \ #define PRIVATE_SYMBOL_LIST(V) \
V(array_iteration_kind_symbol) \ V(array_iteration_kind_symbol) \
......
...@@ -92,9 +92,8 @@ bool Isolate::is_catchable_by_wasm(Object* exception) { ...@@ -92,9 +92,8 @@ bool Isolate::is_catchable_by_wasm(Object* exception) {
return false; return false;
HandleScope scope(this); HandleScope scope(this);
Handle<Object> exception_handle(exception, this); Handle<Object> exception_handle(exception, this);
return JSReceiver::HasProperty( return JSReceiver::HasProperty(Handle<JSReceiver>::cast(exception_handle),
Handle<JSReceiver>::cast(exception_handle), factory()->WasmExceptionTag_string())
factory()->InternalizeUtf8String("WasmExceptionRuntimeId"))
.IsJust(); .IsJust();
} }
......
...@@ -26,9 +26,6 @@ namespace { ...@@ -26,9 +26,6 @@ namespace {
constexpr int kInvalidExceptionTag = -1; constexpr int kInvalidExceptionTag = -1;
constexpr const char* WasmExceptionRuntimeIdStr = "WasmExceptionRuntimeId";
constexpr const char* WasmExceptionValuesStr = "WasmExceptionValues";
WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) { WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
DisallowHeapAllocation no_allocation; DisallowHeapAllocation no_allocation;
const Address entry = Isolate::c_entry_fp(isolate->thread_local_top()); const Address entry = Isolate::c_entry_fp(isolate->thread_local_top());
...@@ -147,134 +144,55 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) { ...@@ -147,134 +144,55 @@ RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError)); isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
} }
RUNTIME_FUNCTION(Runtime_WasmThrowCreate) { RUNTIME_FUNCTION(Runtime_WasmThrow) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_NULL(isolate->context()); DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate)); isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(2, args.length());
Handle<Object> exception = isolate->factory()->NewWasmRuntimeError( DCHECK_EQ(1, args.length());
Handle<Object> tag = args.at(0);
Handle<Object> except = isolate->factory()->NewWasmRuntimeError(
static_cast<MessageTemplate::Template>( static_cast<MessageTemplate::Template>(
MessageTemplate::kWasmExceptionError)); MessageTemplate::kWasmExceptionError));
CONVERT_ARG_HANDLE_CHECKED(Smi, id, 0); DCHECK(tag->IsSmi());
CHECK(!JSReceiver::SetProperty(exception,
isolate->factory()->InternalizeUtf8String(
WasmExceptionRuntimeIdStr),
id, STRICT)
.is_null());
CONVERT_SMI_ARG_CHECKED(size, 1);
Handle<JSTypedArray> values =
isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size);
CHECK(!JSReceiver::SetProperty( CHECK(!JSReceiver::SetProperty(
exception, except, isolate->factory()->WasmExceptionTag_string(), tag, STRICT)
isolate->factory()->InternalizeUtf8String(WasmExceptionValuesStr),
values, STRICT)
.is_null()); .is_null());
return *exception; return isolate->Throw(*except);
}
RUNTIME_FUNCTION(Runtime_WasmThrow) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
return isolate->Throw(*exception);
} }
RUNTIME_FUNCTION(Runtime_WasmRethrow) { RUNTIME_FUNCTION(Runtime_WasmRethrow) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(0, args.length()); DCHECK_EQ(0, args.length());
Handle<Object> exception(isolate->get_wasm_caught_exception(), isolate); Object* exception = isolate->get_wasm_caught_exception();
isolate->clear_wasm_caught_exception(); isolate->clear_wasm_caught_exception();
return isolate->Throw(*exception); return isolate->Throw(exception);
} }
RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId) { RUNTIME_FUNCTION(Runtime_WasmGetExceptionTag) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0); Object* exception = args[0];
DCHECK(isolate->is_catchable_by_wasm(*exception)); DCHECK(isolate->is_catchable_by_wasm(exception));
Handle<Object> tag; Handle<Object> exception_handle(exception, isolate);
if (JSReceiver::GetProperty( Handle<Object> tag_handle;
exception, if (JSReceiver::GetProperty(Handle<JSReceiver>::cast(exception_handle),
isolate->factory()->InternalizeUtf8String(WasmExceptionRuntimeIdStr)) isolate->factory()->WasmExceptionTag_string())
.ToHandle(&tag)) { .ToHandle(&tag_handle)) {
if (tag->IsSmi()) { if (tag_handle->IsSmi()) return *tag_handle;
return *tag;
}
} }
return Smi::FromInt(kInvalidExceptionTag); return Smi::FromInt(kInvalidExceptionTag);
} }
RUNTIME_FUNCTION(Runtime_WasmGetExceptionValuesArray) {
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
Handle<Object> values;
if (JSReceiver::GetProperty(
exception,
isolate->factory()->InternalizeUtf8String(WasmExceptionValuesStr))
.ToHandle(&values)) {
return *values;
}
return *isolate->factory()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) {
// TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(2, args.length());
CONVERT_ARG_CHECKED(JSTypedArray, values, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
CHECK_LT(index, Smi::ToInt(values->length()));
CHECK_EQ(values->type(), kExternalUint16Array);
auto* vals =
reinterpret_cast<uint16_t*>(values->GetBuffer()->allocation_base());
return Smi::FromInt(vals[index]);
}
RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement) {
// TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
CONVERT_ARG_HANDLE_CHECKED(JSObject, exception, 0);
CONVERT_SMI_ARG_CHECKED(index, 1);
CONVERT_SMI_ARG_CHECKED(value, 2);
Handle<Object> values_obj;
CHECK(JSReceiver::GetProperty(
exception,
isolate->factory()->InternalizeUtf8String(WasmExceptionValuesStr))
.ToHandle(&values_obj));
Handle<JSTypedArray> values = Handle<JSTypedArray>::cast(values_obj);
CHECK_LT(index, Smi::ToInt(values->length()));
CHECK_EQ(values->type(), kExternalUint16Array);
auto* vals =
reinterpret_cast<uint16_t*>(values->GetBuffer()->allocation_base());
vals[index] = static_cast<uint16_t>(value);
return *exception;
}
RUNTIME_FUNCTION(Runtime_WasmSetCaughtExceptionValue) { RUNTIME_FUNCTION(Runtime_WasmSetCaughtExceptionValue) {
// TODO(kschimpf): Implement stack of caught exceptions, rather than // TODO(kschimpf): Implement stack of caught exceptions, rather than
// just innermost. // just innermost.
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_NULL(isolate->context());
isolate->set_context(GetWasmContextOnStackTop(isolate));
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, exception, 0); Object* exception = args[0];
DCHECK(isolate->is_catchable_by_wasm(*exception)); DCHECK(isolate->is_catchable_by_wasm(exception));
isolate->set_wasm_caught_exception(*exception); isolate->set_wasm_caught_exception(exception);
return *exception; return exception;
} }
RUNTIME_FUNCTION(Runtime_SetThreadInWasm) { RUNTIME_FUNCTION(Runtime_SetThreadInWasm) {
......
...@@ -643,14 +643,10 @@ namespace internal { ...@@ -643,14 +643,10 @@ namespace internal {
F(ThrowWasmErrorFromTrapIf, 1, 1) \ F(ThrowWasmErrorFromTrapIf, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \ F(ThrowWasmStackOverflow, 0, 1) \
F(WasmThrowTypeError, 0, 1) \ F(WasmThrowTypeError, 0, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrow, 1, 1) \ F(WasmThrow, 1, 1) \
F(WasmRethrow, 0, 1) \ F(WasmRethrow, 0, 1) \
F(WasmGetExceptionRuntimeId, 1, 1) \ F(WasmGetExceptionTag, 1, 1) \
F(WasmGetExceptionValuesArray, 1, 1) \
F(WasmSetCaughtExceptionValue, 1, 1) \ F(WasmSetCaughtExceptionValue, 1, 1) \
F(WasmExceptionSetElement, 3, 1) \
F(WasmExceptionGetElement, 2, 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) \
......
...@@ -539,11 +539,8 @@ struct ControlWithNamedConstructors : public ControlBase<Value> { ...@@ -539,11 +539,8 @@ struct ControlWithNamedConstructors : public ControlBase<Value> {
const Value& input, Value* result) \ const Value& input, Value* result) \
F(Simd8x16ShuffleOp, const Simd8x16ShuffleOperand<validate>& operand, \ F(Simd8x16ShuffleOp, const Simd8x16ShuffleOperand<validate>& operand, \
const Value& input0, const Value& input1, Value* result) \ const Value& input0, const Value& input1, Value* result) \
F(Throw, const ExceptionIndexOperand<validate>&, Control* block, \ F(Throw, const ExceptionIndexOperand<validate>&) \
const Vector<Value>& args) \ F(Catch, const ExceptionIndexOperand<validate>& operand, Control* block) \
F(CatchException, const ExceptionIndexOperand<validate>& operand, \
Control* block) \
F(SetCaughtValue, Value* value, size_t index) \
F(AtomicOp, WasmOpcode opcode, Vector<Value> args, Value* result) F(AtomicOp, WasmOpcode opcode, Vector<Value> args, Value* result)
// Generic Wasm bytecode decoder with utilities for decoding operands, // Generic Wasm bytecode decoder with utilities for decoding operands,
...@@ -1224,9 +1221,16 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1224,9 +1221,16 @@ class WasmFullDecoder : public WasmDecoder<validate> {
ExceptionIndexOperand<true> operand(this, this->pc_); ExceptionIndexOperand<true> operand(this, this->pc_);
len = 1 + operand.length; len = 1 + operand.length;
if (!this->Validate(this->pc_, operand)) break; if (!this->Validate(this->pc_, operand)) break;
std::vector<Value> args; if (operand.exception->sig->parameter_count() > 0) {
PopArgs(operand.exception->ToFunctionSig(), &args); // TODO(kschimpf): Fix to pull values off stack and build throw.
interface_.Throw(this, operand, &control_.back(), vec2vec(args)); OPCODE_ERROR(opcode, "can't handle exceptions with values yet");
break;
}
interface_.Throw(this, operand);
// TODO(titzer): Throw should end control, but currently we build a
// (reachable) runtime call instead of connecting it directly to
// end.
// EndControl();
break; break;
} }
case kExprTry: { case kExprTry: {
...@@ -1264,12 +1268,8 @@ class WasmFullDecoder : public WasmDecoder<validate> { ...@@ -1264,12 +1268,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
c->kind = kControlTryCatch; c->kind = kControlTryCatch;
FallThruTo(c); FallThruTo(c);
stack_.resize(c->stack_depth); stack_.resize(c->stack_depth);
interface_.CatchException(this, operand, c);
const WasmExceptionSig* sig = operand.exception->sig; interface_.Catch(this, operand, c);
for (size_t i = 0, e = sig->parameter_count(); i < e; ++i) {
auto* value = Push(sig->GetParam(i));
interface_.SetCaughtValue(this, value, i);
}
break; break;
} }
case kExprCatchAll: { case kExprCatchAll: {
......
...@@ -27,11 +27,6 @@ namespace wasm { ...@@ -27,11 +27,6 @@ namespace wasm {
namespace { namespace {
template <typename T>
Vector<T> vec2vec(ZoneVector<T>& vec) {
return Vector<T>(vec.data(), vec.size());
}
// 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.
// It maintains a control state that tracks whether the environment // It maintains a control state that tracks whether the environment
...@@ -145,11 +140,8 @@ class WasmGraphBuildingInterface { ...@@ -145,11 +140,8 @@ class WasmGraphBuildingInterface {
void Try(Decoder* decoder, Control* block) { void Try(Decoder* decoder, Control* block) {
SsaEnv* outer_env = ssa_env_; SsaEnv* outer_env = ssa_env_;
SsaEnv* catch_env = Split(decoder, outer_env);
// Mark catch environment as unreachable, since only accessable
// through catch unwinding (i.e. landing pads).
catch_env->state = SsaEnv::kUnreachable;
SsaEnv* try_env = Steal(decoder->zone(), outer_env); SsaEnv* try_env = Steal(decoder->zone(), outer_env);
SsaEnv* catch_env = UnreachableEnv(decoder->zone());
SetEnv(try_env); SetEnv(try_env);
TryInfo* try_info = new (decoder->zone()) TryInfo(catch_env); TryInfo* try_info = new (decoder->zone()) TryInfo(catch_env);
block->end_env = outer_env; block->end_env = outer_env;
...@@ -377,92 +369,40 @@ class WasmGraphBuildingInterface { ...@@ -377,92 +369,40 @@ class WasmGraphBuildingInterface {
return BUILD(Int32Constant, operand.index); return BUILD(Int32Constant, operand.index);
} }
void Throw(Decoder* decoder, const ExceptionIndexOperand<true>& operand, void Throw(Decoder* decoder, const ExceptionIndexOperand<true>& operand) {
Control* block, const Vector<Value>& value_args) { BUILD(Throw, GetExceptionTag(decoder, operand));
int count = value_args.length();
ZoneVector<TFNode*> args(count, decoder->zone());
for (int i = 0; i < count; ++i) {
args[i] = value_args[i].node;
}
BUILD(Throw, operand.index, operand.exception, vec2vec(args));
Unreachable(decoder);
EndControl(decoder, block);
} }
void CatchException(Decoder* decoder, void Catch(Decoder* decoder, const ExceptionIndexOperand<true>& operand,
const ExceptionIndexOperand<true>& operand,
Control* block) { Control* block) {
DCHECK(block->is_try_catch()); DCHECK(block->is_try_catch());
current_catch_ = block->previous_catch; current_catch_ = block->previous_catch;
SsaEnv* catch_env = block->try_info->catch_env; SsaEnv* catch_env = block->try_info->catch_env;
SetEnv(catch_env); SetEnv(catch_env);
TFNode* exception = nullptr;
TFNode* compare_i32 = nullptr;
if (block->try_info->exception == nullptr) {
// Catch not applicable, no possible throws in the try
// block. Create dummy code so that body of catch still
// compiles. Note: This only happens because the current
// implementation only builds a landing pad if some node in the
// try block can (possibly) throw.
//
// TODO(kschimpf): Always generate a landing pad for a try block.
compare_i32 = exception = BUILD(Int32Constant, 0);
caught_values_ = nullptr;
} else {
// Get the exception and see if wanted exception. // Get the exception and see if wanted exception.
exception = BUILD(Catch, block->try_info->exception); TFNode* exception_as_i32 =
TFNode* caught_tag = BUILD(GetExceptionRuntimeId, exception); BUILD(Catch, block->try_info->exception, decoder->position());
TFNode* exception_tag = TFNode* exception_tag = GetExceptionTag(decoder, operand);
BUILD(ConvertExceptionTagToRuntimeId, operand.index); TFNode* compare_i32 = BUILD(Binop, kExprI32Eq, exception_as_i32,
compare_i32 = BUILD(Binop, kExprI32Eq, caught_tag, exception_tag); exception_tag, decoder->position());
// TODO(kschimpf): Can't use BUILD() here, GetExceptionValues() returns TFNode* if_true = nullptr;
// TFNode** rather than TFNode*. Fix to add landing pads. TFNode* if_false = nullptr;
caught_values_ = BUILD(BranchNoHint, compare_i32, &if_true, &if_false);
builder_->GetExceptionValues(operand.exception, exception); SsaEnv* false_env = Split(decoder, catch_env);
} false_env->control = if_false;
SsaEnv* true_env = Steal(decoder->zone(), catch_env);
TFNode* if_catch = nullptr; true_env->control = if_true;
TFNode* if_no_catch = nullptr; block->try_info->catch_env = false_env;
BUILD(BranchNoHint, compare_i32, &if_catch, &if_no_catch);
SsaEnv* if_no_catch_env = Split(decoder, ssa_env_);
if_no_catch_env->control = if_no_catch;
SsaEnv* if_catch_env = Steal(decoder->zone(), ssa_env_);
if_catch_env->control = if_catch;
// TODO(kschimpf): Generalize to allow more catches. Will force
// moving no_catch code to END opcode.
SetEnv(if_no_catch_env);
BUILD(Rethrow);
Unreachable(decoder);
EndControl(decoder, block);
SetEnv(if_catch_env); // Generate code to re-throw the exception.
} DCHECK_NOT_NULL(block->try_info->catch_env);
SetEnv(false_env);
BUILD(Rethrow);
FallThruTo(decoder, block);
void SetCaughtValue(Decoder* decoder, Value* value, size_t index) { SetEnv(true_env);
if (caught_values_) { // TODO(kschimpf): Add code to pop caught exception from isolate.
value->node = caught_values_[index];
return;
}
// No caught value, make up filler node so that catch block still compiles.
switch (value->type) {
case kWasmI32:
I32Const(decoder, value, 0);
break;
case kWasmI64:
I64Const(decoder, value, 0);
break;
case kWasmF32:
F32Const(decoder, value, 0.0);
break;
case kWasmF64:
F64Const(decoder, value, 0.0);
break;
default:
CHECK(false); // Should not happen.
}
} }
void AtomicOp(Decoder* decoder, WasmOpcode opcode, Vector<Value> args, void AtomicOp(Decoder* decoder, WasmOpcode opcode, Vector<Value> args,
...@@ -476,7 +416,6 @@ class WasmGraphBuildingInterface { ...@@ -476,7 +416,6 @@ class WasmGraphBuildingInterface {
SsaEnv* ssa_env_; SsaEnv* ssa_env_;
TFBuilder* builder_; TFBuilder* builder_;
uint32_t current_catch_ = kNullCatch; uint32_t current_catch_ = kNullCatch;
TFNode** caught_values_ = nullptr;
bool build(Decoder* decoder) { return ssa_env_->go() && decoder->ok(); } bool build(Decoder* decoder) { return ssa_env_->go() && decoder->ok(); }
......
...@@ -89,7 +89,6 @@ typedef FunctionSig WasmExceptionSig; ...@@ -89,7 +89,6 @@ typedef FunctionSig WasmExceptionSig;
struct WasmException { struct WasmException {
explicit WasmException(const WasmExceptionSig* sig = &empty_sig_) explicit WasmException(const WasmExceptionSig* sig = &empty_sig_)
: sig(sig) {} : sig(sig) {}
FunctionSig* ToFunctionSig() const { return const_cast<FunctionSig*>(sig); }
const WasmExceptionSig* sig; // type signature of the exception. const WasmExceptionSig* sig; // type signature of the exception.
......
...@@ -21,10 +21,8 @@ class FlagScope { ...@@ -21,10 +21,8 @@ class FlagScope {
T previous_value_; T previous_value_;
}; };
#define FLAG_SCOPE(flag) \ #define EXPERIMENTAL_FLAG_SCOPE(flag) \
FlagScope<bool> __scope_##flag##__LINE__(&FLAG_##flag, true) FlagScope<bool> __scope_##__LINE__(&FLAG_experimental_wasm_##flag, true)
#define EXPERIMENTAL_FLAG_SCOPE(flag) FLAG_SCOPE(experimental_wasm_##flag)
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8
......
...@@ -36,8 +36,8 @@ assertEquals("function", typeof test_throw.exports.throw_if_param_not_zero); ...@@ -36,8 +36,8 @@ assertEquals("function", typeof test_throw.exports.throw_if_param_not_zero);
// Test expected behavior of throws // Test expected behavior of throws
assertEquals(1, test_throw.exports.throw_if_param_not_zero(0)); assertEquals(1, test_throw.exports.throw_if_param_not_zero(0));
assertWasmThrows(0, [], function() { test_throw.exports.throw_if_param_not_zero(10) }); assertWasmThrows([], function() { test_throw.exports.throw_if_param_not_zero(10) });
assertWasmThrows(0, [], function() { test_throw.exports.throw_if_param_not_zero(-1) }); assertWasmThrows([], function() { test_throw.exports.throw_if_param_not_zero(-1) });
// Now that we know throwing works, we test catching the exceptions we raise. // Now that we know throwing works, we test catching the exceptions we raise.
var test_catch = (function () { var test_catch = (function () {
...@@ -72,314 +72,31 @@ assertEquals("function", typeof test_catch.exports.simple_throw_catch_to_0_1); ...@@ -72,314 +72,31 @@ assertEquals("function", typeof test_catch.exports.simple_throw_catch_to_0_1);
assertEquals(0, test_catch.exports.simple_throw_catch_to_0_1(0)); assertEquals(0, test_catch.exports.simple_throw_catch_to_0_1(0));
assertEquals(1, test_catch.exports.simple_throw_catch_to_0_1(1)); assertEquals(1, test_catch.exports.simple_throw_catch_to_0_1(1));
// Test that we can distinguish which exception was thrown. /* TODO(kschimpf) Convert these tests to work for the proposed exceptions.
var test_catch_2 = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_v);
builder.addException(kSig_v_v);
builder.addException(kSig_v_v);
builder.addFunction("catch_different_exceptions", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprI32Eqz,
kExprIf, kWasmStmt,
kExprThrow, 0,
kExprElse,
kExprGetLocal, 0,
kExprI32Const, 1,
kExprI32Eq,
kExprIf, kWasmStmt,
kExprThrow, 1,
kExprElse,
kExprThrow, 2,
kExprEnd,
kExprEnd,
kExprI32Const, 2,
kExprCatch, 0,
kExprI32Const, 3,
kExprEnd,
kExprCatch, 1,
kExprI32Const, 4,
kExprEnd
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_catch_2 === undefined);
assertFalse(test_catch_2 === null);
assertFalse(test_catch_2 === 0);
assertEquals("object", typeof test_catch_2.exports);
assertEquals("function", typeof test_catch_2.exports.catch_different_exceptions);
assertEquals(3, test_catch_2.exports.catch_different_exceptions(0));
assertEquals(4, test_catch_2.exports.catch_different_exceptions(1));
assertWasmThrows(2, [], function() { test_catch_2.exports.catch_different_exceptions(2) });
// Test throwing an exception with multiple values.
var test_throw_1_2 = (function() {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_ii);
builder.addFunction("throw_1_2", kSig_v_v)
.addBody([
kExprI32Const, 1,
kExprI32Const, 2,
kExprThrow, 0,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_1_2 === undefined);
assertFalse(test_throw_1_2 === null);
assertFalse(test_throw_1_2 === 0);
assertEquals("object", typeof test_throw_1_2.exports);
assertEquals("function", typeof test_throw_1_2.exports.throw_1_2);
assertWasmThrows(0, [0, 1, 0, 2], function() { test_throw_1_2.exports.throw_1_2(); });
// Test throwing/catching the i32 parameter value.
var test_throw_catch_param_i = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_i);
builder.addFunction("throw_catch_param", kSig_i_i)
.addBody([
kExprTry, kWasmI32,
kExprGetLocal, 0,
kExprThrow, 0,
kExprI32Const, 2,
kExprCatch, 0,
kExprReturn,
kExprEnd,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_i === undefined);
assertFalse(test_throw_catch_param_i === null);
assertFalse(test_throw_catch_param_i === 0);
assertEquals("object", typeof test_throw_catch_param_i.exports);
assertEquals("function",
typeof test_throw_catch_param_i.exports.throw_catch_param);
assertEquals(0, test_throw_catch_param_i.exports.throw_catch_param(0));
assertEquals(1, test_throw_catch_param_i.exports.throw_catch_param(1));
assertEquals(10, test_throw_catch_param_i.exports.throw_catch_param(10));
// Test the encoding of a thrown exception with an integer exception.
var test_throw_param_i = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_i);
builder.addFunction("throw_param", kSig_v_i)
.addBody([
kExprGetLocal, 0,
kExprThrow, 0,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_i === undefined);
assertFalse(test_throw_param_i === null);
assertFalse(test_throw_param_i === 0);
assertEquals("object", typeof test_throw_param_i.exports);
assertEquals("function",
typeof test_throw_param_i.exports.throw_param);
assertWasmThrows(0, [0, 5], function() { test_throw_param_i.exports.throw_param(5); });
assertWasmThrows(0, [6, 31026],
function() { test_throw_param_i.exports.throw_param(424242); });
// Test throwing/catching the f32 parameter value.
var test_throw_catch_param_f = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_f);
builder.addFunction("throw_catch_param", kSig_f_f)
.addBody([
kExprTry, kWasmF32,
kExprGetLocal, 0,
kExprThrow, 0,
kExprF32Const, 0, 0, 0, 0,
kExprCatch, 0,
kExprReturn,
kExprEnd,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_f === undefined);
assertFalse(test_throw_catch_param_f === null);
assertFalse(test_throw_catch_param_f === 0);
assertEquals("object", typeof test_throw_catch_param_f.exports);
assertEquals("function",
typeof test_throw_catch_param_f.exports.throw_catch_param);
assertEquals(5.0, test_throw_catch_param_f.exports.throw_catch_param(5.0));
assertEquals(10.5, test_throw_catch_param_f.exports.throw_catch_param(10.5));
// Test the encoding of a thrown exception with a float value.
var test_throw_param_f = (function () { // The following methods do not attempt to catch the exception they raise.
var test_throw = (function () {
var builder = new WasmModuleBuilder(); var builder = new WasmModuleBuilder();
builder.addException(kSig_v_f);
builder.addFunction("throw_param", kSig_v_f)
.addBody([
kExprGetLocal, 0,
kExprThrow, 0,
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_f === undefined);
assertFalse(test_throw_param_f === null);
assertFalse(test_throw_param_f === 0);
assertEquals("object", typeof test_throw_param_f.exports);
assertEquals("function",
typeof test_throw_param_f.exports.throw_param);
assertWasmThrows(0, [16544, 0], builder.addFunction("throw_param_if_not_zero", kSig_i_i)
function() { test_throw_param_f.exports.throw_param(5.0); });
assertWasmThrows(0, [16680, 0],
function() { test_throw_param_f.exports.throw_param(10.5); });
// Test throwing/catching an I64 value
var test_throw_catch_param_l = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_l);
builder.addFunction("throw_catch_param", kSig_i_i)
.addBody([ .addBody([
kExprGetLocal, 0, kExprGetLocal, 0,
kExprI64UConvertI32,
kExprSetLocal, 1,
kExprTry, kWasmI32,
kExprGetLocal, 1,
kExprThrow, 0,
kExprI32Const, 2,
kExprCatch, 0,
kExprGetLocal, 1,
kExprI64Eq,
kExprIf, kWasmI32,
kExprI32Const, 1,
kExprElse,
kExprI32Const, 0, kExprI32Const, 0,
kExprEnd, kExprI32Ne,
// TODO(kschimpf): Why is this return necessary? kExprIf, kWasmStmt,
kExprReturn,
kExprEnd,
]).addLocals({i64_count: 1}).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_catch_param_l === undefined);
assertFalse(test_throw_catch_param_l === null);
assertFalse(test_throw_catch_param_l === 0);
assertEquals("object", typeof test_throw_catch_param_l.exports);
assertEquals("function",
typeof test_throw_catch_param_l.exports.throw_catch_param);
assertEquals(1, test_throw_catch_param_l.exports.throw_catch_param(5));
assertEquals(1, test_throw_catch_param_l.exports.throw_catch_param(0));
assertEquals(1, test_throw_catch_param_l.exports.throw_catch_param(-1));
// Test the encoding of a thrown exception with an I64 value.
var test_throw_param_l = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_l);
builder.addFunction("throw_param", kSig_v_ii)
.addBody([
kExprGetLocal, 0,
kExprI64UConvertI32,
kExprI64Const, 32,
kExprI64Shl,
kExprGetLocal, 1,
kExprI64UConvertI32,
kExprI64Ior,
kExprThrow, 0
]).exportFunc();
return builder.instantiate();
})();
assertFalse(test_throw_param_l === undefined);
assertFalse(test_throw_param_l === null);
assertFalse(test_throw_param_l === 0);
assertEquals("object", typeof test_throw_param_l.exports);
assertEquals("function",
typeof test_throw_param_l.exports.throw_param);
assertWasmThrows(0, [0, 10, 0, 5],
function() { test_throw_param_l.exports.throw_param(10, 5); });
assertWasmThrows(0, [65535, 65535, 0, 13],
function() { test_throw_param_l.exports.throw_param(-1, 13); });
// Test throwing/catching the F64 parameter value
var test_throw_catch_param_d = (function () {
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_d);
builder.addFunction("throw_catch_param", kSig_d_d)
.addBody([
kExprTry, kWasmF64,
kExprGetLocal, 0, kExprGetLocal, 0,
kExprThrow, 0, kExprThrow,
kExprF64Const, 0, 0, 0, 0, 0, 0, 0, 0,
kExprCatch, 0,
kExprReturn,
kExprEnd, kExprEnd,
]).exportFunc(); kExprI32Const, 1
])
return builder.instantiate(); .exportFunc()
})();
assertFalse(test_throw_catch_param_d === undefined);
assertFalse(test_throw_catch_param_d === null);
assertFalse(test_throw_catch_param_d === 0);
assertEquals("object", typeof test_throw_catch_param_d.exports);
assertEquals("function",
typeof test_throw_catch_param_d.exports.throw_catch_param);
assertEquals(5.0, test_throw_catch_param_d.exports.throw_catch_param(5.0));
assertEquals(10.5, test_throw_catch_param_d.exports.throw_catch_param(10.5));
// Test the encoding of a thrown exception with an f64 value.
var test_throw_param_d = (function () { builder.addFunction("throw_20", kSig_v_v)
var builder = new WasmModuleBuilder();
builder.addException(kSig_v_d);
builder.addFunction("throw_param", kSig_v_f)
.addBody([ .addBody([
kExprGetLocal, 0, kExprI32Const, 20,
kExprF64ConvertF32, kExprThrow,
kExprThrow, 0 ])
]).exportFunc(); .exportFunc()
return builder.instantiate();
})();
assertFalse(test_throw_param_d === undefined);
assertFalse(test_throw_param_d === null);
assertFalse(test_throw_param_d === 0);
assertEquals("object", typeof test_throw_param_d.exports);
assertEquals("function",
typeof test_throw_param_d.exports.throw_param);
assertWasmThrows(0, [16404, 0, 0, 0],
function() { test_throw_param_d.exports.throw_param(5.0); });
assertWasmThrows(0, [16739, 4816, 0, 0],
function() { test_throw_param_d.exports.throw_param(10000000.5); });
/* 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();
builder.addFunction("throw_expr_with_params", kSig_v_ddi) builder.addFunction("throw_expr_with_params", kSig_v_ddi)
.addBody([ .addBody([
...@@ -406,9 +123,14 @@ assertFalse(test_throw === undefined); ...@@ -406,9 +123,14 @@ assertFalse(test_throw === undefined);
assertFalse(test_throw === null); assertFalse(test_throw === null);
assertFalse(test_throw === 0); assertFalse(test_throw === 0);
assertEquals("object", typeof test_throw.exports); assertEquals("object", typeof test_throw.exports);
assertEquals("function", typeof test_throw.exports.throw_param_if_not_zero);
assertEquals("function", typeof test_throw.exports.throw_20);
assertEquals("function", typeof test_throw.exports.throw_expr_with_params); assertEquals("function", typeof test_throw.exports.throw_expr_with_params);
assertEquals(1, test_throw.exports.throw_param_if_not_zero(0)); assertEquals(1, test_throw.exports.throw_param_if_not_zero(0));
assertWasmThrows(10, function() { test_throw.exports.throw_param_if_not_zero(10) });
assertWasmThrows(-1, function() { test_throw.exports.throw_param_if_not_zero(-1) });
assertWasmThrows(20, test_throw.exports.throw_20);
assertWasmThrows( assertWasmThrows(
-8, function() { test_throw.exports.throw_expr_with_params(1.5, 2.5, 4); }); -8, function() { test_throw.exports.throw_expr_with_params(1.5, 2.5, 4); });
assertWasmThrows( assertWasmThrows(
......
...@@ -124,10 +124,6 @@ let kSig_v_d = makeSig([kWasmF64], []); ...@@ -124,10 +124,6 @@ let kSig_v_d = makeSig([kWasmF64], []);
let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []); let kSig_v_dd = makeSig([kWasmF64, kWasmF64], []);
let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []); let kSig_v_ddi = makeSig([kWasmF64, kWasmF64, kWasmI32], []);
let kSig_v_f = makeSig([kWasmF32], []);
let kSig_f_f = makeSig([kWasmF32], [kWasmF32]);
let kSig_d_d = makeSig([kWasmF64], [kWasmF64]);
function makeSig(params, results) { function makeSig(params, results) {
return {params: params, results: results}; return {params: params, results: results};
} }
...@@ -392,7 +388,7 @@ function assertTraps(trap, code) { ...@@ -392,7 +388,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(runtime_id, values, code) { function assertWasmThrows(values, code) {
try { try {
if (typeof code === 'function') { if (typeof code === 'function') {
code(); code();
...@@ -401,16 +397,13 @@ function assertWasmThrows(runtime_id, values, code) { ...@@ -401,16 +397,13 @@ function assertWasmThrows(runtime_id, values, code) {
} }
} catch (e) { } catch (e) {
assertTrue(e instanceof WebAssembly.RuntimeError); assertTrue(e instanceof WebAssembly.RuntimeError);
var e_runtime_id = e['WasmExceptionRuntimeId']; assertNotEquals(e['WasmExceptionTag'], undefined);
assertEquals(e_runtime_id, runtime_id); assertTrue(Number.isInteger(e['WasmExceptionTag']));
assertTrue(Number.isInteger(e_runtime_id)); // TODO(kschimpf): Extract values from the exception.
var e_values = e['WasmExceptionValues']; let e_values = [];
assertEquals(values.length, e_values.length); assertEquals(values, e_values);
for (i = 0; i < values.length; ++i) {
assertEquals(values[i], e_values[i]);
}
// Success. // Success.
return; return;
} }
throw new MjsUnitAssertionError('Did not throw expected: ' + runtime_id + values); throw new MjsUnitAssertionError('Did not throw, expected: ' + values);
} }
...@@ -523,7 +523,7 @@ class WasmModuleBuilder { ...@@ -523,7 +523,7 @@ class WasmModuleBuilder {
for (let type of wasm.exceptions) { for (let type of wasm.exceptions) {
section.emit_u32v(type.params.length); section.emit_u32v(type.params.length);
for (let param of type.params) { for (let param of type.params) {
section.emit_u8(param); section.enit_u8(param);
} }
} }
}); });
......
...@@ -2273,7 +2273,8 @@ TEST_F(FunctionBodyDecoderTest, Throw) { ...@@ -2273,7 +2273,8 @@ TEST_F(FunctionBodyDecoderTest, Throw) {
// exception index out of range. // exception index out of range.
EXPECT_FAILURE(v_v, kExprThrow, 2); EXPECT_FAILURE(v_v, kExprThrow, 2);
EXPECT_VERIFIES(v_v, WASM_I32V(0), kExprThrow, 1); // TODO(kschimpf): Fix when we can create exceptions with values.
EXPECT_FAILURE(v_v, WASM_I32V(0), kExprThrow, 1);
// TODO(kschimpf): Add more tests. // TODO(kschimpf): Add more tests.
} }
......
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