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:
parent
beebb2360f
commit
0074125486
@ -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(
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
@ -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) \
|
||||
|
@ -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_);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
})();
|
||||
|
||||
|
158
test/mjsunit/wasm/exceptions-shared.js
Normal file
158
test/mjsunit/wasm/exceptions-shared.js
Normal 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());
|
||||
})();
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user