Reland "[wasm] Implement handling of exported/imported exceptions."

This is a reland of a4105a437d

Original change's description:
> [wasm] Implement handling of exported/imported exceptions.
> 
> This implements the proper semantics for matching exported/imported
> exceptions by using the notion of an "exception tag" that is global to
> the system. It can be used to match exceptions in one module against
> exceptions declared and/or thrown in another module (or instance).
> 
> R=clemensh@chromium.org
> TEST=mjsunit/wasm/exceptions-shared
> BUG=v8:8091
> 
> Change-Id: I37586d7be5d5e6169b3418dfbc415b26dd4750dd
> Reviewed-on: https://chromium-review.googlesource.com/1226976
> Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
> Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#55940}

Bug: v8:8091
Change-Id: Ib85f099b26a8323a8a00299b5aaeb05aaff3c3c6
Reviewed-on: https://chromium-review.googlesource.com/1227975
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55959}
This commit is contained in:
Michael Starzinger 2018-09-17 14:19:53 +02:00 committed by Commit Bot
parent beebb2360f
commit 0074125486
15 changed files with 308 additions and 86 deletions

View File

@ -2043,13 +2043,13 @@ uint32_t WasmGraphBuilder::GetExceptionEncodedSize(
return encoded_size;
}
Node* WasmGraphBuilder::Throw(uint32_t tag,
Node* WasmGraphBuilder::Throw(uint32_t exception_index,
const wasm::WasmException* exception,
const Vector<Node*> values) {
SetNeedsStackCheck();
uint32_t encoded_size = GetExceptionEncodedSize(exception);
Node* create_parameters[] = {
BuildChangeUint31ToSmi(ConvertExceptionTagToRuntimeId(tag)),
LoadExceptionTagFromTable(exception_index),
BuildChangeUint31ToSmi(Uint32Constant(encoded_size))};
Node* except_obj =
BuildCallToRuntime(Runtime::kWasmThrowCreate, create_parameters,
@ -2126,16 +2126,22 @@ Node* WasmGraphBuilder::Rethrow(Node* except_obj) {
return result;
}
Node* WasmGraphBuilder::ConvertExceptionTagToRuntimeId(uint32_t tag) {
// TODO(kschimpf): Handle exceptions from different modules, when they are
// linked at runtime.
return Uint32Constant(tag);
Node* WasmGraphBuilder::ExceptionTagEqual(Node* caught_tag,
Node* expected_tag) {
MachineOperatorBuilder* machine = mcgraph()->machine();
return graph()->NewNode(machine->WordEqual(), caught_tag, expected_tag);
}
Node* WasmGraphBuilder::GetExceptionRuntimeId(Node* except_obj) {
Node* WasmGraphBuilder::LoadExceptionTagFromTable(uint32_t exception_index) {
Node* exceptions_table =
LOAD_INSTANCE_FIELD(ExceptionsTable, MachineType::TaggedPointer());
Node* tag = LOAD_FIXED_ARRAY_SLOT(exceptions_table, exception_index);
return tag;
}
Node* WasmGraphBuilder::GetExceptionTag(Node* except_obj) {
SetNeedsStackCheck();
return BuildChangeSmiToInt32(
BuildCallToRuntime(Runtime::kWasmGetExceptionRuntimeId, &except_obj, 1));
return BuildCallToRuntime(Runtime::kWasmExceptionGetTag, &except_obj, 1);
}
Node** WasmGraphBuilder::GetExceptionValues(

View File

@ -160,11 +160,12 @@ class WasmGraphBuilder {
Node* Unop(wasm::WasmOpcode opcode, Node* input,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* GrowMemory(Node* input);
Node* Throw(uint32_t tag, const wasm::WasmException* exception,
Node* Throw(uint32_t exception_index, const wasm::WasmException* exception,
const Vector<Node*> values);
Node* Rethrow(Node* except_obj);
Node* ConvertExceptionTagToRuntimeId(uint32_t tag);
Node* GetExceptionRuntimeId(Node* except_obj);
Node* ExceptionTagEqual(Node* caught_tag, Node* expected_tag);
Node* LoadExceptionTagFromTable(uint32_t exception_index);
Node* GetExceptionTag(Node* except_obj);
Node** GetExceptionValues(Node* except_obj,
const wasm::WasmException* except_decl);
bool IsPhiWithMerge(Node* phi, Node* merge);

View File

@ -288,7 +288,7 @@
V(sealed_symbol) \
V(stack_trace_symbol) \
V(strict_function_transition_symbol) \
V(wasm_exception_runtime_id_symbol) \
V(wasm_exception_tag_symbol) \
V(wasm_exception_values_symbol) \
V(uninitialized_symbol)

View File

@ -844,12 +844,19 @@ RUNTIME_FUNCTION(Runtime_GetWasmRecoveredTrapCount) {
RUNTIME_FUNCTION(Runtime_GetWasmExceptionId) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(JSReceiver, exception, 0);
RETURN_RESULT_OR_FAILURE(
isolate, JSReceiver::GetProperty(
isolate, exception,
isolate->factory()->wasm_exception_runtime_id_symbol()));
CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 1);
Handle<Object> tag;
if (JSReceiver::GetProperty(isolate, exception,
isolate->factory()->wasm_exception_tag_symbol())
.ToHandle(&tag)) {
Handle<FixedArray> exceptions_table(instance->exceptions_table(), isolate);
for (int index = 0; index < exceptions_table->length(); ++index) {
if (exceptions_table->get(index) == *tag) return Smi::FromInt(index);
}
}
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_GetWasmExceptionValues) {

View File

@ -115,16 +115,18 @@ RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
DCHECK_EQ(2, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
CONVERT_ARG_CHECKED(HeapObject, tag_raw, 0);
CONVERT_SMI_ARG_CHECKED(size, 1);
// TODO(mstarzinger): Manually box because parameters are not visited yet.
Handle<Object> tag(tag_raw, isolate);
Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
static_cast<MessageTemplate::Template>(
MessageTemplate::kWasmExceptionError));
CONVERT_ARG_HANDLE_CHECKED(Smi, id, 0);
CHECK(!JSReceiver::SetProperty(
isolate, exception,
isolate->factory()->wasm_exception_runtime_id_symbol(), id,
LanguageMode::kStrict)
.is_null());
CONVERT_SMI_ARG_CHECKED(size, 1);
CHECK(
!JSReceiver::SetProperty(isolate, exception,
isolate->factory()->wasm_exception_tag_symbol(),
tag, LanguageMode::kStrict)
.is_null());
Handle<JSTypedArray> values =
isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size);
CHECK(!JSReceiver::SetProperty(
@ -137,35 +139,33 @@ RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
RUNTIME_FUNCTION(Runtime_WasmThrow) {
// TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
HandleScope scope(isolate);
SealHandleScope shs(isolate);
DCHECK_EQ(1, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
CONVERT_ARG_HANDLE_CHECKED(Object, except_obj, 0);
CHECK(!except_obj.is_null());
return isolate->Throw(*except_obj);
CONVERT_ARG_CHECKED(Object, except_obj, 0);
return isolate->Throw(except_obj);
}
RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId) {
RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag) {
// TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
CONVERT_ARG_HANDLE_CHECKED(Object, except_obj, 0);
CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
// TODO(mstarzinger): Manually box because parameters are not visited yet.
Handle<Object> except_obj(except_obj_raw, isolate);
if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
Handle<Object> tag;
if (JSReceiver::GetProperty(
isolate, exception,
isolate->factory()->wasm_exception_runtime_id_symbol())
if (JSReceiver::GetProperty(isolate, exception,
isolate->factory()->wasm_exception_tag_symbol())
.ToHandle(&tag)) {
if (tag->IsSmi()) {
return *tag;
}
return *tag;
}
}
return Smi::FromInt(wasm::kInvalidExceptionTag);
return ReadOnlyRoots(isolate).undefined_value();
}
RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) {
@ -174,7 +174,9 @@ RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) {
DCHECK_EQ(2, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
CONVERT_ARG_HANDLE_CHECKED(Object, except_obj, 0);
CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
// TODO(mstarzinger): Manually box because parameters are not visited yet.
Handle<Object> except_obj(except_obj_raw, isolate);
if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
Handle<Object> values_obj;
@ -202,7 +204,9 @@ RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement) {
DCHECK_EQ(3, args.length());
DCHECK_NULL(isolate->context());
isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
CONVERT_ARG_HANDLE_CHECKED(Object, except_obj, 0);
CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
// TODO(mstarzinger): Manually box because parameters are not visited yet.
Handle<Object> except_obj(except_obj_raw, isolate);
if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
Handle<Object> values_obj;

View File

@ -473,7 +473,7 @@ namespace internal {
F(GetDeoptCount, 1, 1) \
F(GetOptimizationStatus, -1, 1) \
F(GetUndetectable, 0, 1) \
F(GetWasmExceptionId, 1, 1) \
F(GetWasmExceptionId, 2, 1) \
F(GetWasmExceptionValues, 1, 1) \
F(GetWasmRecoveredTrapCount, 0, 1) \
F(GlobalPrint, 1, 1) \
@ -541,18 +541,18 @@ namespace internal {
F(TypedArraySet, 2, 1) \
F(TypedArraySortFast, 1, 1)
#define FOR_EACH_INTRINSIC_WASM(F) \
F(ThrowWasmError, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmExceptionGetElement, 2, 1) \
F(WasmExceptionSetElement, 3, 1) \
F(WasmGetExceptionRuntimeId, 1, 1) \
F(WasmGrowMemory, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrow, 1, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
#define FOR_EACH_INTRINSIC_WASM(F) \
F(ThrowWasmError, 1, 1) \
F(ThrowWasmStackOverflow, 0, 1) \
F(WasmExceptionGetElement, 2, 1) \
F(WasmExceptionSetElement, 3, 1) \
F(WasmExceptionGetTag, 1, 1) \
F(WasmGrowMemory, 2, 1) \
F(WasmRunInterpreter, 2, 1) \
F(WasmStackGuard, 0, 1) \
F(WasmThrow, 1, 1) \
F(WasmThrowCreate, 2, 1) \
F(WasmThrowTypeError, 0, 1) \
F(WasmCompileLazy, 2, 1)
#define FOR_EACH_INTRINSIC_RETURN_PAIR(F) \

View File

@ -443,11 +443,11 @@ class WasmGraphBuildingInterface {
TFNode* if_catch = nullptr;
TFNode* if_no_catch = nullptr;
if (exception != nullptr) {
// Get the exception and see if wanted exception.
TFNode* caught_tag = BUILD(GetExceptionRuntimeId, exception);
TFNode* exception_tag = BUILD(ConvertExceptionTagToRuntimeId, imm.index);
TFNode* compare_i32 = BUILD(Binop, kExprI32Eq, caught_tag, exception_tag);
BUILD(BranchNoHint, compare_i32, &if_catch, &if_no_catch);
// Get the exception tag and see if it matches the expected one.
TFNode* caught_tag = BUILD(GetExceptionTag, exception);
TFNode* exception_tag = BUILD(LoadExceptionTagFromTable, imm.index);
TFNode* compare = BUILD(ExceptionTagEqual, caught_tag, exception_tag);
BUILD(BranchNoHint, compare, &if_catch, &if_no_catch);
}
SsaEnv* if_no_catch_env = Split(decoder, ssa_env_);

View File

@ -325,6 +325,10 @@ class InstanceBuilder {
void InitializeTables(Handle<WasmInstanceObject> instance);
void LoadTableSegments(Handle<WasmInstanceObject> instance);
// Creates new exception tags for all exceptions. Note that some tags might
// already exist if they were imported, those tags will be re-used.
void InitializeExceptions(Handle<WasmInstanceObject> instance);
};
} // namespace
@ -1075,6 +1079,17 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
instance->set_imported_mutable_globals_buffers(*buffers_array);
}
//--------------------------------------------------------------------------
// Set up the exception table used for exception tag checks.
//--------------------------------------------------------------------------
int exceptions_count = static_cast<int>(module_->exceptions.size());
if (exceptions_count > 0) {
Handle<FixedArray> exception_table =
isolate_->factory()->NewFixedArray(exceptions_count, TENURED);
instance->set_exceptions_table(*exception_table);
exception_wrappers_.resize(exceptions_count);
}
//--------------------------------------------------------------------------
// Reserve the metadata for indirect function tables.
//--------------------------------------------------------------------------
@ -1099,6 +1114,13 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
InitializeTables(instance);
}
//--------------------------------------------------------------------------
// Initialize the exceptions table.
//--------------------------------------------------------------------------
if (exceptions_count > 0) {
InitializeExceptions(instance);
}
//--------------------------------------------------------------------------
// Create the WebAssembly.Memory object.
//--------------------------------------------------------------------------
@ -1740,8 +1762,10 @@ int InstanceBuilder::ProcessImports(Handle<WasmInstanceObject> instance) {
index, module_name, import_name);
return -1;
}
// TODO(mstarzinger): Actually add imported exceptions to the instance
// exception table, making sure to preserve object identity.
Object* exception_tag = imported_exception->exception_tag();
DCHECK(instance->exceptions_table()->get(import.index)->IsUndefined());
instance->exceptions_table()->set(import.index, exception_tag);
exception_wrappers_[import.index] = imported_exception;
break;
}
default:
@ -1861,10 +1885,6 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
}
}
// TODO(mstarzinger): The {exception_wrappers_} table is only needed until we
// have an exception table per instance which can then be used directly.
exception_wrappers_.resize(module_->exceptions.size());
Handle<JSObject> exports_object;
bool is_asm_js = false;
switch (module_->origin) {
@ -2021,7 +2041,11 @@ void InstanceBuilder::ProcessExports(Handle<WasmInstanceObject> instance) {
const WasmException& exception = module_->exceptions[exp.index];
Handle<WasmExceptionObject> wrapper = exception_wrappers_[exp.index];
if (wrapper.is_null()) {
wrapper = WasmExceptionObject::New(isolate_, exception.sig);
Handle<HeapObject> exception_tag(
HeapObject::cast(instance->exceptions_table()->get(exp.index)),
isolate_);
wrapper =
WasmExceptionObject::New(isolate_, exception.sig, exception_tag);
exception_wrappers_[exp.index] = wrapper;
}
desc.set_value(wrapper);
@ -2143,6 +2167,20 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
}
}
void InstanceBuilder::InitializeExceptions(
Handle<WasmInstanceObject> instance) {
Handle<FixedArray> exceptions_table(instance->exceptions_table(), isolate_);
for (int index = 0; index < exceptions_table->length(); ++index) {
if (!exceptions_table->get(index)->IsUndefined(isolate_)) continue;
// TODO(mstarzinger): Tags provide an object identity for each exception,
// using {JSObject} here is gigantic hack and we should use a dedicated
// object with a much lighter footprint for this purpose here.
Handle<HeapObject> exception_tag =
isolate_->factory()->NewJSObjectWithNullProto();
exceptions_table->set(index, *exception_tag);
}
}
AsyncCompileJob::AsyncCompileJob(
Isolate* isolate, const WasmFeatures& enabled,
std::unique_ptr<byte[]> bytes_copy, size_t length, Handle<Context> context,

View File

@ -79,7 +79,6 @@ enum NameSectionKindCode : uint8_t { kModule = 0, kFunction = 1, kLocal = 2 };
constexpr size_t kWasmPageSize = 0x10000;
constexpr uint32_t kWasmPageSizeLog2 = 16;
static_assert(kWasmPageSize == size_t{1} << kWasmPageSizeLog2, "consistency");
constexpr int kInvalidExceptionTag = -1;
// TODO(wasm): Wrap WasmCodePosition in a struct.
using WasmCodePosition = int;

View File

@ -185,6 +185,8 @@ OPTIONAL_ACCESSORS(WasmInstanceObject, indirect_function_table_instances,
FixedArray, kIndirectFunctionTableInstancesOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, managed_native_allocations, Foreign,
kManagedNativeAllocationsOffset)
OPTIONAL_ACCESSORS(WasmInstanceObject, exceptions_table, FixedArray,
kExceptionsTableOffset)
ACCESSORS(WasmInstanceObject, undefined_value, Oddball, kUndefinedValueOffset)
ACCESSORS(WasmInstanceObject, null_value, Oddball, kNullValueOffset)
ACCESSORS(WasmInstanceObject, centry_stub, Code, kCEntryStubOffset)
@ -210,6 +212,7 @@ ImportedFunctionEntry::ImportedFunctionEntry(
// WasmExceptionObject
ACCESSORS(WasmExceptionObject, serialized_signature, PodArray<wasm::ValueType>,
kSerializedSignatureOffset)
ACCESSORS(WasmExceptionObject, exception_tag, HeapObject, kExceptionTagOffset)
// WasmExportedFunctionData
ACCESSORS(WasmExportedFunctionData, wrapper_code, Code, kWrapperCodeOffset)

View File

@ -1309,7 +1309,8 @@ Address WasmInstanceObject::GetCallTarget(uint32_t func_index) {
// static
Handle<WasmExceptionObject> WasmExceptionObject::New(
Isolate* isolate, const wasm::FunctionSig* sig) {
Isolate* isolate, const wasm::FunctionSig* sig,
Handle<HeapObject> exception_tag) {
Handle<JSFunction> exception_cons(
isolate->native_context()->wasm_exception_constructor(), isolate);
Handle<JSObject> exception_object =
@ -1328,6 +1329,7 @@ Handle<WasmExceptionObject> WasmExceptionObject::New(
serialized_sig->set(index++, param);
}
exception->set_serialized_signature(*serialized_sig);
exception->set_exception_tag(*exception_tag);
return exception;
}

View File

@ -387,6 +387,7 @@ class WasmInstanceObject : public JSObject {
DECL_ACCESSORS(imported_function_callables, FixedArray)
DECL_OPTIONAL_ACCESSORS(indirect_function_table_instances, FixedArray)
DECL_OPTIONAL_ACCESSORS(managed_native_allocations, Foreign)
DECL_OPTIONAL_ACCESSORS(exceptions_table, FixedArray)
DECL_ACCESSORS(undefined_value, Oddball)
DECL_ACCESSORS(null_value, Oddball)
DECL_ACCESSORS(centry_stub, Code)
@ -422,6 +423,7 @@ class WasmInstanceObject : public JSObject {
V(kImportedFunctionCallablesOffset, kPointerSize) \
V(kIndirectFunctionTableInstancesOffset, kPointerSize) \
V(kManagedNativeAllocationsOffset, kPointerSize) \
V(kExceptionsTableOffset, kPointerSize) \
V(kUndefinedValueOffset, kPointerSize) \
V(kNullValueOffset, kPointerSize) \
V(kCEntryStubOffset, kPointerSize) \
@ -473,10 +475,12 @@ class WasmExceptionObject : public JSObject {
DECL_CAST(WasmExceptionObject)
DECL_ACCESSORS(serialized_signature, PodArray<wasm::ValueType>)
DECL_ACCESSORS(exception_tag, HeapObject)
// Layout description.
#define WASM_EXCEPTION_OBJECT_FIELDS(V) \
V(kSerializedSignatureOffset, kPointerSize) \
V(kExceptionTagOffset, kPointerSize) \
V(kSize, 0)
DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,
@ -488,7 +492,8 @@ class WasmExceptionObject : public JSObject {
bool IsSignatureEqual(const wasm::FunctionSig* sig);
static Handle<WasmExceptionObject> New(Isolate* isolate,
const wasm::FunctionSig* sig);
const wasm::FunctionSig* sig,
Handle<HeapObject> exception_tag);
};
// A WASM function that is wrapped and exported to JavaScript.

View File

@ -40,8 +40,7 @@ function NewExportedException() {
assertTrue(except1 < except3 && except2 < except3);
assertEquals(undefined, instance.exports.ex1);
// TODO(mstarzinger): Enable once identity of imported exception is preserved.
//assertSame(exported, instance.exports.ex2);
assertSame(exported, instance.exports.ex2);
assertNotSame(exported, instance.exports.ex3);
})();

View File

@ -0,0 +1,158 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-wasm --experimental-wasm-eh
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
// Helper function to return a new exported exception with the {kSig_v_v} type
// signature from an anonymous module. The underlying module is thrown away.
function NewExportedException() {
let builder = new WasmModuleBuilder();
let except = builder.addException(kSig_v_v);
builder.addExportOfKind("ex", kExternalException, except);
let instance = builder.instantiate();
return instance.exports.ex;
}
// Check that an instance matches an exception thrown by itself, even when the
// exception is re-thrown by a regular JavaScript function.
(function TestSingleInstance() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_v_v);
let fun = builder.addImport("m", "f", sig_index);
let except = builder.addException(kSig_v_v);
builder.addFunction("throw", kSig_v_v)
.addBody([
kExprThrow, except
]).exportFunc();
builder.addFunction("catch", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, fun,
kExprCatch, except,
kExprEnd,
]).exportFunc();
let ex_obj = new Error("my exception");
let instance = builder.instantiate({ m: { f: function() { throw ex_obj }}});
assertThrows(() => instance.exports.throw(), WebAssembly.RuntimeError);
assertThrowsEquals(() => instance.exports.catch(), ex_obj);
try {
instance.exports.throw();
} catch (e) {
ex_obj = e;
}
assertDoesNotThrow(() => instance.exports.catch());
})();
// Check that two instances distinguish their individual exceptions if they are
// not shared, even when declared by the same underlying module.
(function TestMultiInstanceNonShared() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_v_v);
let fun = builder.addImport("m", "f", sig_index);
let except = builder.addException(kSig_v_v);
builder.addFunction("throw", kSig_v_v)
.addBody([
kExprThrow, except
]).exportFunc();
builder.addFunction("catch", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, fun,
kExprCatch, except,
kExprEnd,
]).exportFunc();
let ex_obj = new Error("my exception");
let instance1 = builder.instantiate({ m: { f: assertUnreachable }});
let instance2 = builder.instantiate({ m: { f: function() { throw ex_obj }}});
assertThrows(() => instance1.exports.throw(), WebAssembly.RuntimeError);
assertThrowsEquals(() => instance2.exports.catch(), ex_obj);
try {
instance1.exports.throw();
} catch (e) {
ex_obj = e;
}
assertThrowsEquals(() => instance2.exports.catch(), ex_obj);
})();
// Check that two instances match their exceptions if they are shared properly,
// even if the local exception index of export and import is different.
(function TestMultiInstanceShared() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_index = builder.addType(kSig_v_v);
let fun = builder.addImport("m", "f", sig_index);
let except1 = builder.addImportedException("m", "ex1", kSig_v_v);
let except2 = builder.addException(kSig_v_v);
builder.addExportOfKind("ex2", kExternalException, except2);
builder.addFunction("throw", kSig_v_v)
.addBody([
kExprThrow, except2
]).exportFunc();
builder.addFunction("catch", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, fun,
kExprCatch, except1,
kExprEnd,
]).exportFunc();
let ex_obj = new Error("my exception");
let instance1 = builder.instantiate({ m: { f: assertUnreachable,
ex1: NewExportedException() }});
let instance2 = builder.instantiate({ m: { f: function() { throw ex_obj },
ex1: instance1.exports.ex2 }});
assertThrows(() => instance1.exports.throw(), WebAssembly.RuntimeError);
assertThrowsEquals(() => instance2.exports.catch(), ex_obj);
try {
instance1.exports.throw();
} catch (e) {
ex_obj = e;
}
assertDoesNotThrow(() => instance2.exports.catch());
})();
// Check that two instances based on different modules match their exceptions if
// they are shared properly, even if the local exception index is different.
(function TestMultiModuleShared() {
print(arguments.callee.name);
let builder1 = new WasmModuleBuilder();
let except1 = builder1.addException(kSig_v_v);
let except2 = builder1.addException(kSig_v_v);
builder1.addExportOfKind("ex", kExternalException, except2);
builder1.addFunction("throw", kSig_v_v)
.addBody([
kExprThrow, except2
]).exportFunc();
let builder2 = new WasmModuleBuilder();
let sig_index = builder2.addType(kSig_v_v);
let fun = builder2.addImport("m", "f", sig_index);
let except = builder2.addImportedException("m", "ex", kSig_v_v);
builder2.addFunction("catch", kSig_v_v)
.addBody([
kExprTry, kWasmStmt,
kExprCallFunction, fun,
kExprCatch, except,
kExprEnd,
]).exportFunc();
let ex_obj = new Error("my exception");
let instance1 = builder1.instantiate();
let instance2 = builder2.instantiate({ m: { f: function() { throw ex_obj },
ex: instance1.exports.ex }});
assertThrows(() => instance1.exports.throw(), WebAssembly.RuntimeError);
assertThrowsEquals(() => instance2.exports.catch(), ex_obj);
try {
instance1.exports.throw();
} catch (e) {
ex_obj = e;
}
assertDoesNotThrow(() => instance2.exports.catch());
})();

View File

@ -7,7 +7,7 @@
load("test/mjsunit/wasm/wasm-constants.js");
load("test/mjsunit/wasm/wasm-module-builder.js");
function assertWasmThrows(runtime_id, values, code) {
function assertWasmThrows(instance, runtime_id, values, code) {
try {
if (typeof code === 'function') {
code();
@ -16,7 +16,7 @@ function assertWasmThrows(runtime_id, values, code) {
}
} catch (e) {
assertInstanceof(e, WebAssembly.RuntimeError);
var e_runtime_id = %GetWasmExceptionId(e);
var e_runtime_id = %GetWasmExceptionId(e, instance);
assertTrue(Number.isInteger(e_runtime_id));
assertEquals(e_runtime_id, runtime_id);
var e_values = %GetWasmExceptionValues(e);
@ -57,8 +57,8 @@ function assertWasmThrows(runtime_id, values, code) {
let instance = builder.instantiate();
assertEquals(1, instance.exports.throw_if_param_not_zero(0));
assertWasmThrows(except, [], () => instance.exports.throw_if_param_not_zero(10));
assertWasmThrows(except, [], () => instance.exports.throw_if_param_not_zero(-1));
assertWasmThrows(instance, except, [], () => instance.exports.throw_if_param_not_zero(10));
assertWasmThrows(instance, except, [], () => instance.exports.throw_if_param_not_zero(-1));
})();
// Test that empty try/catch blocks work.
@ -135,7 +135,7 @@ function assertWasmThrows(runtime_id, values, code) {
assertEquals(3, instance.exports.catch_different_exceptions(0));
assertEquals(4, instance.exports.catch_different_exceptions(1));
assertWasmThrows(except3, [], () => instance.exports.catch_different_exceptions(2));
assertWasmThrows(instance, except3, [], () => instance.exports.catch_different_exceptions(2));
})();
// Test throwing an exception with multiple values.
@ -150,7 +150,7 @@ function assertWasmThrows(runtime_id, values, code) {
]).exportFunc();
let instance = builder.instantiate();
assertWasmThrows(except, [0, 1, 0, 2], () => instance.exports.throw_1_2());
assertWasmThrows(instance, except, [0, 1, 0, 2], () => instance.exports.throw_1_2());
})();
// Test throwing/catching the i32 parameter value.
@ -185,8 +185,8 @@ function assertWasmThrows(runtime_id, values, code) {
]).exportFunc();
let instance = builder.instantiate();
assertWasmThrows(except, [0, 5], () => instance.exports.throw_param(5));
assertWasmThrows(except, [6, 31026], () => instance.exports.throw_param(424242));
assertWasmThrows(instance, except, [0, 5], () => instance.exports.throw_param(5));
assertWasmThrows(instance, except, [6, 31026], () => instance.exports.throw_param(424242));
})();
// Test throwing/catching the f32 parameter value.
@ -220,8 +220,8 @@ function assertWasmThrows(runtime_id, values, code) {
]).exportFunc();
let instance = builder.instantiate();
assertWasmThrows(except, [16544, 0], () => instance.exports.throw_param(5.0));
assertWasmThrows(except, [16680, 0], () => instance.exports.throw_param(10.5));
assertWasmThrows(instance, except, [16544, 0], () => instance.exports.throw_param(5.0));
assertWasmThrows(instance, except, [16680, 0], () => instance.exports.throw_param(10.5));
})();
// Test throwing/catching an I64 value
@ -273,8 +273,8 @@ function assertWasmThrows(runtime_id, values, code) {
]).exportFunc();
let instance = builder.instantiate();
assertWasmThrows(except, [0, 10, 0, 5], () => instance.exports.throw_param(10, 5));
assertWasmThrows(except, [65535, 65535, 0, 13], () => instance.exports.throw_param(-1, 13));
assertWasmThrows(instance, except, [0, 10, 0, 5], () => instance.exports.throw_param(10, 5));
assertWasmThrows(instance, except, [65535, 65535, 0, 13], () => instance.exports.throw_param(-1, 13));
})();
// Test throwing/catching the F64 parameter value
@ -309,8 +309,8 @@ function assertWasmThrows(runtime_id, values, code) {
]).exportFunc();
let instance = builder.instantiate();
assertWasmThrows(except, [16404, 0, 0, 0], () => instance.exports.throw_param(5.0));
assertWasmThrows(except, [16739, 4816, 0, 0], () => instance.exports.throw_param(10000000.5));
assertWasmThrows(instance, except, [16404, 0, 0, 0], () => instance.exports.throw_param(5.0));
assertWasmThrows(instance, except, [16739, 4816, 0, 0], () => instance.exports.throw_param(10000000.5));
})();
// Test the encoding of a computed parameter value.
@ -334,8 +334,8 @@ function assertWasmThrows(runtime_id, values, code) {
]).exportFunc()
let instance = builder.instantiate();
assertWasmThrows(except, [65535, 65536-8], () => instance.exports.throw_expr_with_params(1.5, 2.5, 4));
assertWasmThrows(except, [0, 12], () => instance.exports.throw_expr_with_params(5.7, 2.5, 4));
assertWasmThrows(instance, except, [65535, 65536-8], () => instance.exports.throw_expr_with_params(1.5, 2.5, 4));
assertWasmThrows(instance, except, [0, 12], () => instance.exports.throw_expr_with_params(5.7, 2.5, 4));
})();
// Now that we know catching works locally, we test catching exceptions that