[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}
diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc
index a97cce7..7b14f94 100644
--- a/src/compiler/wasm-compiler.cc
+++ b/src/compiler/wasm-compiler.cc
@@ -2043,13 +2043,13 @@
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 @@
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(
diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h
index 0968abe..5599ed6 100644
--- a/src/compiler/wasm-compiler.h
+++ b/src/compiler/wasm-compiler.h
@@ -160,11 +160,12 @@
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);
diff --git a/src/heap-symbols.h b/src/heap-symbols.h
index 8cdd8ff..4403192 100644
--- a/src/heap-symbols.h
+++ b/src/heap-symbols.h
@@ -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)
diff --git a/src/runtime/runtime-test.cc b/src/runtime/runtime-test.cc
index 9e436ff..f00d8ba 100644
--- a/src/runtime/runtime-test.cc
+++ b/src/runtime/runtime-test.cc
@@ -844,12 +844,19 @@
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) {
diff --git a/src/runtime/runtime-wasm.cc b/src/runtime/runtime-wasm.cc
index afda688..36d90ee 100644
--- a/src/runtime/runtime-wasm.cc
+++ b/src/runtime/runtime-wasm.cc
@@ -118,12 +118,12 @@
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_ARG_HANDLE_CHECKED(HeapObject, tag, 0);
+ CHECK(
+ !JSReceiver::SetProperty(isolate, exception,
+ isolate->factory()->wasm_exception_tag_symbol(),
+ tag, LanguageMode::kStrict)
+ .is_null());
CONVERT_SMI_ARG_CHECKED(size, 1);
Handle<JSTypedArray> values =
isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size);
@@ -146,7 +146,7 @@
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());
@@ -156,16 +156,13 @@
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) {
diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h
index 8c55302..3417906 100644
--- a/src/runtime/runtime.h
+++ b/src/runtime/runtime.h
@@ -473,7 +473,7 @@
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 @@
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) \
diff --git a/src/wasm/function-body-decoder.cc b/src/wasm/function-body-decoder.cc
index 3e27fef..2c5ea46 100644
--- a/src/wasm/function-body-decoder.cc
+++ b/src/wasm/function-body-decoder.cc
@@ -443,11 +443,11 @@
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_);
diff --git a/src/wasm/module-compiler.cc b/src/wasm/module-compiler.cc
index f74b5fe..9e26411 100644
--- a/src/wasm/module-compiler.cc
+++ b/src/wasm/module-compiler.cc
@@ -325,6 +325,10 @@
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
@@ -1076,6 +1080,17 @@
}
//--------------------------------------------------------------------------
+ // 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.
//--------------------------------------------------------------------------
int table_count = static_cast<int>(module_->tables.size());
@@ -1100,6 +1115,13 @@
}
//--------------------------------------------------------------------------
+ // Initialize the exceptions table.
+ //--------------------------------------------------------------------------
+ if (exceptions_count > 0) {
+ InitializeExceptions(instance);
+ }
+
+ //--------------------------------------------------------------------------
// Create the WebAssembly.Memory object.
//--------------------------------------------------------------------------
if (module_->has_memory) {
@@ -1740,8 +1762,10 @@
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 @@
}
}
- // 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 @@
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::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,
diff --git a/src/wasm/wasm-constants.h b/src/wasm/wasm-constants.h
index 2fd30f0..7a3fb35 100644
--- a/src/wasm/wasm-constants.h
+++ b/src/wasm/wasm-constants.h
@@ -79,7 +79,6 @@
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;
diff --git a/src/wasm/wasm-objects-inl.h b/src/wasm/wasm-objects-inl.h
index 4c079ff..0144b8a 100644
--- a/src/wasm/wasm-objects-inl.h
+++ b/src/wasm/wasm-objects-inl.h
@@ -185,6 +185,8 @@
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 @@
// WasmExceptionObject
ACCESSORS(WasmExceptionObject, serialized_signature, PodArray<wasm::ValueType>,
kSerializedSignatureOffset)
+ACCESSORS(WasmExceptionObject, exception_tag, HeapObject, kExceptionTagOffset)
// WasmExportedFunctionData
ACCESSORS(WasmExportedFunctionData, wrapper_code, Code, kWrapperCodeOffset)
diff --git a/src/wasm/wasm-objects.cc b/src/wasm/wasm-objects.cc
index 4547a5b9..9d0e20a 100644
--- a/src/wasm/wasm-objects.cc
+++ b/src/wasm/wasm-objects.cc
@@ -1309,7 +1309,8 @@
// 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 @@
serialized_sig->set(index++, param);
}
exception->set_serialized_signature(*serialized_sig);
+ exception->set_exception_tag(*exception_tag);
return exception;
}
diff --git a/src/wasm/wasm-objects.h b/src/wasm/wasm-objects.h
index fba0e88..360d8ed 100644
--- a/src/wasm/wasm-objects.h
+++ b/src/wasm/wasm-objects.h
@@ -387,6 +387,7 @@
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 @@
V(kImportedFunctionCallablesOffset, kPointerSize) \
V(kIndirectFunctionTableInstancesOffset, kPointerSize) \
V(kManagedNativeAllocationsOffset, kPointerSize) \
+ V(kExceptionsTableOffset, kPointerSize) \
V(kUndefinedValueOffset, kPointerSize) \
V(kNullValueOffset, kPointerSize) \
V(kCEntryStubOffset, kPointerSize) \
@@ -475,10 +477,12 @@
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,
@@ -490,7 +494,8 @@
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.
diff --git a/test/mjsunit/wasm/exceptions-import.js b/test/mjsunit/wasm/exceptions-import.js
index b123c70..b527672 100644
--- a/test/mjsunit/wasm/exceptions-import.js
+++ b/test/mjsunit/wasm/exceptions-import.js
@@ -40,8 +40,7 @@
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);
})();
diff --git a/test/mjsunit/wasm/exceptions-shared.js b/test/mjsunit/wasm/exceptions-shared.js
new file mode 100644
index 0000000..f2a5b56
--- /dev/null
+++ b/test/mjsunit/wasm/exceptions-shared.js
@@ -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());
+})();
diff --git a/test/mjsunit/wasm/exceptions.js b/test/mjsunit/wasm/exceptions.js
index 1979b46..68631f4 100644
--- a/test/mjsunit/wasm/exceptions.js
+++ b/test/mjsunit/wasm/exceptions.js
@@ -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 @@
}
} 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 @@
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 @@
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 @@
]).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 @@
]).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 @@
]).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 @@
]).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 @@
]).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 @@
]).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