diff --git a/common.gypi b/common.gypi index b7d84c3ead830f..0ffa5b13719515 100644 --- a/common.gypi +++ b/common.gypi @@ -38,7 +38,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.39', + 'v8_embedder_string': '-node.56', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/AUTHORS b/deps/v8/AUTHORS index 93b3d9d5b2320c..394c7deedb0717 100644 --- a/deps/v8/AUTHORS +++ b/deps/v8/AUTHORS @@ -258,6 +258,7 @@ Sander Mathijs van Veen Sandro Santilli Sanjoy Das Sam James +Sébastien Doeraene Seo Sanghyeon Shawn Anastasio Shawn Presser diff --git a/deps/v8/src/builtins/wasm.tq b/deps/v8/src/builtins/wasm.tq index 2317066b742547..ae034ce9622035 100644 --- a/deps/v8/src/builtins/wasm.tq +++ b/deps/v8/src/builtins/wasm.tq @@ -373,7 +373,12 @@ builtin WasmThrow(tag: Object, values: FixedArray): JSAny { } builtin WasmRethrow(exception: Object): JSAny { - if (exception == Null) tail ThrowWasmTrapRethrowNull(); + dcheck(exception != kWasmNull); + tail runtime::WasmReThrow(LoadContextFromFrame(), exception); +} + +builtin WasmThrowRef(exception: Object): JSAny { + if (exception == kWasmNull) tail ThrowWasmTrapRethrowNull(); tail runtime::WasmReThrow(LoadContextFromFrame(), exception); } diff --git a/deps/v8/src/compiler/turboshaft/wasm-lowering-reducer.h b/deps/v8/src/compiler/turboshaft/wasm-lowering-reducer.h index 82c3356fcd93d2..4c38825c507b13 100644 --- a/deps/v8/src/compiler/turboshaft/wasm-lowering-reducer.h +++ b/deps/v8/src/compiler/turboshaft/wasm-lowering-reducer.h @@ -46,8 +46,7 @@ class WasmLoweringReducer : public Next { #if V8_STATIC_ROOTS_BOOL // TODO(14616): Extend this for shared types. const bool is_wasm_null = - !wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) && - !wasm::IsSubtypeOf(type, wasm::kWasmExnRef, module_); + !wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_); OpIndex null_value = __ UintPtrConstant(is_wasm_null ? StaticReadOnlyRoot::kWasmNull : StaticReadOnlyRoot::kNullValue); @@ -68,8 +67,7 @@ class WasmLoweringReducer : public Next { // (3) the object might be a JS object. if (null_check_strategy_ == NullCheckStrategy::kExplicit || wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), type, module_) || - wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) || - wasm::IsSubtypeOf(type, wasm::kWasmExnRef, module_)) { + wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_)) { __ TrapIf(__ IsNull(object, type), OpIndex::Invalid(), trap_id); } else { // Otherwise, load the word after the map word. @@ -556,7 +554,8 @@ class WasmLoweringReducer : public Next { // The none-types only perform a null check. They need no control flow. if (to_rep == wasm::HeapType::kNone || to_rep == wasm::HeapType::kNoExtern || - to_rep == wasm::HeapType::kNoFunc) { + to_rep == wasm::HeapType::kNoFunc || + to_rep == wasm::HeapType::kNoExn) { result = __ IsNull(object, config.from); break; } @@ -628,7 +627,8 @@ class WasmLoweringReducer : public Next { // The none-types only perform a null check. if (to_rep == wasm::HeapType::kNone || to_rep == wasm::HeapType::kNoExtern || - to_rep == wasm::HeapType::kNoFunc) { + to_rep == wasm::HeapType::kNoFunc || + to_rep == wasm::HeapType::kNoExn) { __ TrapIfNot(__ IsNull(object, config.from), OpIndex::Invalid(), TrapId::kTrapIllegalCast); break; @@ -917,11 +917,9 @@ class WasmLoweringReducer : public Next { OpIndex Null(wasm::ValueType type) { OpIndex roots = __ LoadRootRegister(); - RootIndex index = - wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) || - wasm::IsSubtypeOf(type, wasm::kWasmExnRef, module_) - ? RootIndex::kNullValue - : RootIndex::kWasmNull; + RootIndex index = wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) + ? RootIndex::kNullValue + : RootIndex::kWasmNull; // We load WasmNull as a pointer here and not as a TaggedPointer because // WasmNull is stored uncompressed in the IsolateData, and a load of a // TaggedPointer loads compressed pointers. We do not bitcast the WasmNull diff --git a/deps/v8/src/compiler/wasm-compiler.cc b/deps/v8/src/compiler/wasm-compiler.cc index 16f1f1470b782c..bd3dd3b27bb9ee 100644 --- a/deps/v8/src/compiler/wasm-compiler.cc +++ b/deps/v8/src/compiler/wasm-compiler.cc @@ -391,10 +391,8 @@ Node* WasmGraphBuilder::EffectPhi(unsigned count, Node** effects_and_control) { Node* WasmGraphBuilder::RefNull(wasm::ValueType type) { // We immediately lower null in wrappers, as they do not go through a lowering // phase. - // TODO(thibaudm): Can we use wasm null for exnref? return parameter_mode_ == kInstanceParameterMode ? gasm_->Null(type) - : (type == wasm::kWasmExternRef || type == wasm::kWasmNullExternRef || - type == wasm::kWasmExnRef || type == wasm::kWasmNullExnRef) + : (type == wasm::kWasmExternRef || type == wasm::kWasmNullExternRef) ? LOAD_ROOT(NullValue, null_value) : LOAD_ROOT(WasmNull, wasm_null); } @@ -2372,6 +2370,14 @@ Node* WasmGraphBuilder::Rethrow(Node* except_obj) { Builtin::kWasmRethrow, Operator::kNoProperties, except_obj); } +Node* WasmGraphBuilder::ThrowRef(Node* except_obj) { + // TODO(v8:8091): Currently the message of the original exception is not being + // preserved when rethrown to the console. The pending message will need to be + // saved when caught and restored here while being rethrown. + return gasm_->CallBuiltinThroughJumptable( + Builtin::kWasmThrowRef, Operator::kNoProperties, except_obj); +} + Node* WasmGraphBuilder::IsExceptionTagUndefined(Node* tag) { return gasm_->TaggedEqual(tag, UndefinedValue()); } @@ -7325,6 +7331,8 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { // TODO(14034): Add more fast paths? case wasm::HeapType::kExtern: case wasm::HeapType::kNoExtern: + case wasm::HeapType::kExn: + case wasm::HeapType::kNoExn: if (type.kind() == wasm::kRef) { Node* null_value = gasm_->LoadImmutable( MachineType::Pointer(), gasm_->LoadRootRegister(), @@ -7345,9 +7353,6 @@ class WasmWrapperGraphBuilder : public WasmGraphBuilder { return input; case wasm::HeapType::kString: return BuildCheckString(input, js_context, type); - case wasm::HeapType::kExn: - case wasm::HeapType::kNoExn: - return input; case wasm::HeapType::kNone: case wasm::HeapType::kNoFunc: case wasm::HeapType::kI31: diff --git a/deps/v8/src/compiler/wasm-compiler.h b/deps/v8/src/compiler/wasm-compiler.h index fcff46c449768a..352051898264c2 100644 --- a/deps/v8/src/compiler/wasm-compiler.h +++ b/deps/v8/src/compiler/wasm-compiler.h @@ -233,6 +233,7 @@ class WasmGraphBuilder { const base::Vector values, wasm::WasmCodePosition position); Node* Rethrow(Node* except_obj); + Node* ThrowRef(Node* except_obj); Node* IsExceptionTagUndefined(Node* tag); Node* LoadJSTag(); Node* ExceptionTagEqual(Node* caught_tag, Node* expected_tag); diff --git a/deps/v8/src/compiler/wasm-gc-lowering.cc b/deps/v8/src/compiler/wasm-gc-lowering.cc index 1bcb07abd638da..b99e4b1d4617f1 100644 --- a/deps/v8/src/compiler/wasm-gc-lowering.cc +++ b/deps/v8/src/compiler/wasm-gc-lowering.cc @@ -95,9 +95,7 @@ Reduction WasmGCLowering::Reduce(Node* node) { } Node* WasmGCLowering::Null(wasm::ValueType type) { - // TODO(thibaudm): Can we use wasm null for exnref? - RootIndex index = wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) || - wasm::IsSubtypeOf(type, wasm::kWasmExnRef, module_) + RootIndex index = wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) ? RootIndex::kNullValue : RootIndex::kWasmNull; return gasm_.LoadImmutable(MachineType::Pointer(), gasm_.LoadRootRegister(), @@ -108,8 +106,7 @@ Node* WasmGCLowering::IsNull(Node* object, wasm::ValueType type) { #if V8_STATIC_ROOTS_BOOL // TODO(14616): Extend this for shared types. const bool is_wasm_null = - !wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_) && - !wasm::IsSubtypeOf(type, wasm::kWasmExnRef, module_); + !wasm::IsSubtypeOf(type, wasm::kWasmExternRef, module_); Node* null_value = gasm_.UintPtrConstant(is_wasm_null ? StaticReadOnlyRoot::kWasmNull : StaticReadOnlyRoot::kNullValue); @@ -224,7 +221,7 @@ Reduction WasmGCLowering::ReduceWasmTypeCheckAbstract(Node* node) { // The none-types only perform a null check. They need no control flow. if (to_rep == wasm::HeapType::kNone || to_rep == wasm::HeapType::kNoExtern || - to_rep == wasm::HeapType::kNoFunc) { + to_rep == wasm::HeapType::kNoFunc || to_rep == wasm::HeapType::kNoExn) { result = IsNull(object, config.from); break; } @@ -398,7 +395,7 @@ Reduction WasmGCLowering::ReduceWasmTypeCastAbstract(Node* node) { // The none-types only perform a null check. if (to_rep == wasm::HeapType::kNone || to_rep == wasm::HeapType::kNoExtern || - to_rep == wasm::HeapType::kNoFunc) { + to_rep == wasm::HeapType::kNoFunc || to_rep == wasm::HeapType::kNoExn) { gasm_.TrapUnless(IsNull(object, config.from), TrapId::kTrapIllegalCast); UpdateSourcePosition(gasm_.effect(), node); break; @@ -488,8 +485,7 @@ Reduction WasmGCLowering::ReduceAssertNotNull(Node* node) { if (null_check_strategy_ == NullCheckStrategy::kExplicit || wasm::IsSubtypeOf(wasm::kWasmI31Ref.AsNonNull(), op_parameter.type, module_) || - wasm::IsSubtypeOf(op_parameter.type, wasm::kWasmExternRef, module_) || - wasm::IsSubtypeOf(op_parameter.type, wasm::kWasmExnRef, module_)) { + wasm::IsSubtypeOf(op_parameter.type, wasm::kWasmExternRef, module_)) { gasm_.TrapIf(IsNull(object, op_parameter.type), op_parameter.trap_id); UpdateSourcePosition(gasm_.effect(), node); } else { diff --git a/deps/v8/src/runtime/runtime-wasm.cc b/deps/v8/src/runtime/runtime-wasm.cc index d13eb0a5ba4b03..f822fec8fb4dba 100644 --- a/deps/v8/src/runtime/runtime-wasm.cc +++ b/deps/v8/src/runtime/runtime-wasm.cc @@ -354,12 +354,18 @@ RUNTIME_FUNCTION(Runtime_WasmThrow) { ClearThreadInWasmScope clear_wasm_flag(isolate); HandleScope scope(isolate); DCHECK_EQ(2, args.length()); - isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate)); + Tagged context = GetNativeContextFromWasmInstanceOnStackTop(isolate); + isolate->set_context(context); Handle tag(WasmExceptionTag::cast(args[0]), isolate); Handle values(FixedArray::cast(args[1]), isolate); - Handle exception = - WasmExceptionPackage::New(isolate, tag, values); - return isolate->Throw(*exception); + auto js_tag = WasmTagObject::cast(context->wasm_js_tag()); + if (*tag == js_tag->tag()) { + return isolate->Throw(values->get(0)); + } else { + Handle exception = + WasmExceptionPackage::New(isolate, tag, values); + return isolate->Throw(*exception); + } } RUNTIME_FUNCTION(Runtime_WasmReThrow) { diff --git a/deps/v8/src/wasm/baseline/liftoff-assembler.cc b/deps/v8/src/wasm/baseline/liftoff-assembler.cc index 821b6b80495849..e1ca7bebdc8408 100644 --- a/deps/v8/src/wasm/baseline/liftoff-assembler.cc +++ b/deps/v8/src/wasm/baseline/liftoff-assembler.cc @@ -430,12 +430,13 @@ void LiftoffAssembler::DropExceptionValueAtOffset(int offset) { slot != end; ++slot) { *slot = *(slot + 1); stack_offset = NextSpillOffset(slot->kind(), stack_offset); - // Padding could allow us to exit early. - if (slot->offset() == stack_offset) break; - if (slot->is_stack()) { - MoveStackValue(stack_offset, slot->offset(), slot->kind()); + // Padding could cause some spill offsets to remain the same. + if (slot->offset() != stack_offset) { + if (slot->is_stack()) { + MoveStackValue(stack_offset, slot->offset(), slot->kind()); + } + slot->set_offset(stack_offset); } - slot->set_offset(stack_offset); } cache_state_.stack_state.pop_back(); } diff --git a/deps/v8/src/wasm/baseline/liftoff-compiler.cc b/deps/v8/src/wasm/baseline/liftoff-compiler.cc index c185d7273629fd..f0238c2c5fec81 100644 --- a/deps/v8/src/wasm/baseline/liftoff-compiler.cc +++ b/deps/v8/src/wasm/baseline/liftoff-compiler.cc @@ -931,8 +931,7 @@ class LiftoffCompiler { ValueType type = decoder->local_types_[local_index]; if (type.is_reference()) { __ Spill(__ cache_state()->stack_state[local_index].offset(), - IsSubtypeOf(type, kWasmExternRef, decoder->module_) || - IsSubtypeOf(type, kWasmExnRef, decoder->module_) + IsSubtypeOf(type, kWasmExternRef, decoder->module_) ? LiftoffRegister(null_ref_reg) : LiftoffRegister(wasm_null_ref_reg), type.kind()); @@ -1548,23 +1547,104 @@ class LiftoffCompiler { wasm::ObjectAccess::ElementOffsetInTaggedFixedArray( catch_case.maybe_tag.tag_imm.index)); + VarState exn = __ cache_state() -> stack_state.back(); + CODE_COMMENT("compare tags"); - { - FREEZE_STATE(frozen); - Label caught; - __ emit_cond_jump(kEqual, &caught, kRefNull, imm_tag, caught_tag.gp(), - frozen); - // The tags don't match, merge the current state into the catch state - // and jump to the next handler. - __ MergeFullStackWith(block->try_info->catch_state); - __ emit_jump(&block->try_info->catch_label); - __ bind(&caught); + if (catch_case.maybe_tag.tag_imm.tag->sig->parameter_count() == 1 && + catch_case.maybe_tag.tag_imm.tag->sig->GetParam(0) == kWasmExternRef) { + // Check for the special case where the tag is WebAssembly.JSTag and the + // exception is not a WebAssembly.Exception. In this case the exception is + // caught and pushed on the operand stack. + // Only perform this check if the tag signature is the same as + // the JSTag signature, i.e. a single externref, otherwise we know + // statically that it cannot be the JSTag. + LiftoffRegister undefined = + pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + __ LoadFullPointer( + undefined.gp(), kRootRegister, + IsolateData::root_slot_offset(RootIndex::kUndefinedValue)); + LiftoffRegister js_tag = pinned.set(__ GetUnusedRegister(kGpReg, pinned)); + LOAD_TAGGED_PTR_INSTANCE_FIELD(js_tag.gp(), NativeContext, pinned); + __ LoadTaggedPointer( + js_tag.gp(), js_tag.gp(), no_reg, + NativeContext::SlotOffset(Context::WASM_JS_TAG_INDEX)); + __ LoadTaggedPointer( + js_tag.gp(), js_tag.gp(), no_reg, + wasm::ObjectAccess::ToTagged(WasmTagObject::kTagOffset)); + { + LiftoffAssembler::CacheState initial_state(zone_); + LiftoffAssembler::CacheState end_state(zone_); + Label js_exception; + Label done; + Label uncaught; + initial_state.Split(*__ cache_state()); + { + FREEZE_STATE(state_merged_explicitly); + // If the tag is undefined, this is not a wasm exception. Go to a + // different block to process the JS exception. Otherwise compare it + // with the expected tag. + __ emit_cond_jump(kEqual, &js_exception, kRefNull, caught_tag.gp(), + undefined.gp(), state_merged_explicitly); + __ emit_cond_jump(kNotEqual, &uncaught, kRefNull, imm_tag, + caught_tag.gp(), state_merged_explicitly); + } + // Case 1: A wasm exception with a matching tag. + CODE_COMMENT("unpack exception"); + GetExceptionValues(decoder, __ cache_state()->stack_state.back(), + catch_case.maybe_tag.tag_imm.tag); + // GetExceptionValues modified the cache state. Remember the new state + // to merge the end state of case 2 into it. + end_state.Steal(*__ cache_state()); + __ emit_jump(&done); + + __ bind(&js_exception); + __ cache_state() -> Split(initial_state); + { + FREEZE_STATE(state_merged_explicitly); + __ emit_cond_jump(kNotEqual, &uncaught, kRefNull, imm_tag, + js_tag.gp(), state_merged_explicitly); + } + // Case 2: A JS exception, and the expected tag is JSTag. + // TODO(thibaudm): Can we avoid some state splitting/stealing by + // reserving this register earlier and not modifying the state in this + // block? + CODE_COMMENT("JS exception caught by JSTag"); + LiftoffRegister exception = __ PeekToRegister(0, pinned); + __ PushRegister(kRefNull, exception); + // The exception is now on the stack twice: once as an implicit operand + // for rethrow, and once as the "unpacked" value. + __ MergeFullStackWith(end_state); + __ emit_jump(&done); + + // Case 3: Either a wasm exception with a mismatching tag, or a JS + // exception but the expected tag is not JSTag. + __ bind(&uncaught); + __ cache_state() -> Steal(initial_state); + __ MergeFullStackWith(block->try_info->catch_state); + __ emit_jump(&block->try_info->catch_label); + + __ bind(&done); + __ cache_state() -> Steal(end_state); + } + } else { + { + FREEZE_STATE(frozen); + Label caught; + __ emit_cond_jump(kEqual, &caught, kRefNull, imm_tag, caught_tag.gp(), + frozen); + // The tags don't match, merge the current state into the catch state + // and jump to the next handler. + __ MergeFullStackWith(block->try_info->catch_state); + __ emit_jump(&block->try_info->catch_label); + __ bind(&caught); + } + + CODE_COMMENT("unpack exception"); + pinned = {}; + GetExceptionValues(decoder, __ cache_state()->stack_state.back(), + catch_case.maybe_tag.tag_imm.tag); } - CODE_COMMENT("unpack exception"); - pinned = {}; - VarState exn = __ cache_state()->stack_state.back(); - GetExceptionValues(decoder, exn, catch_case.maybe_tag.tag_imm.tag); if (catch_case.kind == kCatchRef) { // Append the exception on the operand stack. DCHECK(exn.is_stack()); @@ -1587,7 +1667,7 @@ class LiftoffCompiler { void ThrowRef(FullDecoder* decoder, Value*) { // Like Rethrow, but pops the exception from the stack. VarState exn = __ PopVarState(); - CallBuiltin(Builtin::kWasmRethrow, MakeSig::Params(kRef), {exn}, + CallBuiltin(Builtin::kWasmThrowRef, MakeSig::Params(kRef), {exn}, decoder->position()); int pc_offset = __ pc_offset(); MaybeOSR(); @@ -2541,8 +2621,7 @@ class LiftoffCompiler { LiftoffRegister obj = pinned.set(__ PopToRegister(pinned)); if (null_check_strategy_ == compiler::NullCheckStrategy::kExplicit || IsSubtypeOf(kWasmI31Ref.AsNonNull(), arg.type, decoder->module_) || - IsSubtypeOf(arg.type, kWasmExternRef, decoder->module_) || - IsSubtypeOf(arg.type, kWasmExnRef, decoder->module_)) { + IsSubtypeOf(arg.type, kWasmExternRef, decoder->module_)) { // Use an explicit null check if // (1) we cannot use trap handler or // (2) the object might be a Smi or @@ -6580,6 +6659,7 @@ class LiftoffCompiler { case HeapType::kNone: case HeapType::kNoExtern: case HeapType::kNoFunc: + case HeapType::kNoExn: DCHECK(null_succeeds); return AssertNullTypecheck(decoder, obj, result_val); case HeapType::kAny: @@ -6674,6 +6754,7 @@ class LiftoffCompiler { case HeapType::kNone: case HeapType::kNoExtern: case HeapType::kNoFunc: + case HeapType::kNoExn: DCHECK(null_succeeds); return BrOnNull(decoder, obj, depth, /*pass_null_along_branch*/ true, nullptr); @@ -6707,6 +6788,7 @@ class LiftoffCompiler { case HeapType::kNone: case HeapType::kNoExtern: case HeapType::kNoFunc: + case HeapType::kNoExn: DCHECK(null_succeeds); return BrOnNonNull(decoder, obj, nullptr, depth, /*drop_null_on_fallthrough*/ false); @@ -8169,11 +8251,9 @@ class LiftoffCompiler { } void LoadNullValue(Register null, ValueType type) { - // TODO(thibaudm): Can we use wasm null for exnref? __ LoadFullPointer( null, kRootRegister, - type == kWasmExternRef || type == kWasmNullExternRef || - type == kWasmExnRef || type == kWasmNullExnRef + type == kWasmExternRef || type == kWasmNullExternRef ? IsolateData::root_slot_offset(RootIndex::kNullValue) : IsolateData::root_slot_offset(RootIndex::kWasmNull)); } @@ -8186,8 +8266,7 @@ class LiftoffCompiler { ValueType type) { #if V8_STATIC_ROOTS_BOOL // TODO(14616): Extend this for shared types. - bool is_wasm_null = type != kWasmExternRef && type != kWasmNullExternRef && - type != kWasmExnRef && type != kWasmNullExnRef; + bool is_wasm_null = type != kWasmExternRef && type != kWasmNullExternRef; uint32_t value = is_wasm_null ? StaticReadOnlyRoot::kWasmNull : StaticReadOnlyRoot::kNullValue; __ LoadConstant(LiftoffRegister(null), diff --git a/deps/v8/src/wasm/constant-expression-interface.cc b/deps/v8/src/wasm/constant-expression-interface.cc index c09bd2c1b2fe82..da2db6744a6348 100644 --- a/deps/v8/src/wasm/constant-expression-interface.cc +++ b/deps/v8/src/wasm/constant-expression-interface.cc @@ -105,7 +105,7 @@ void ConstantExpressionInterface::RefNull(FullDecoder* decoder, ValueType type, Value* result) { if (!generate_value()) return; result->runtime_value = - WasmValue(type == kWasmExternRef || type == kWasmNullExternRef + WasmValue(IsSubtypeOf(type, kWasmExternRef, decoder->module_) ? Handle::cast(isolate_->factory()->null_value()) : Handle::cast(isolate_->factory()->wasm_null()), type); @@ -200,8 +200,7 @@ WasmValue DefaultValueForType(ValueType type, Isolate* isolate) { return WasmValue(Simd128()); case kRefNull: return WasmValue( - type == kWasmExternRef || type == kWasmNullExternRef || - type == kWasmExnRef + type == kWasmExternRef || type == kWasmNullExternRef ? Handle::cast(isolate->factory()->null_value()) : Handle::cast(isolate->factory()->wasm_null()), type); diff --git a/deps/v8/src/wasm/constant-expression.cc b/deps/v8/src/wasm/constant-expression.cc index f78517ffeb4615..046ceedaa07c57 100644 --- a/deps/v8/src/wasm/constant-expression.cc +++ b/deps/v8/src/wasm/constant-expression.cc @@ -36,8 +36,7 @@ ValueOrError EvaluateConstantExpression( return WasmValue(expr.i32_value()); case ConstantExpression::kRefNull: return WasmValue( - expected == kWasmExternRef || expected == kWasmNullExternRef || - expected == kWasmNullExnRef || expected == kWasmExnRef + expected == kWasmExternRef || expected == kWasmNullExternRef ? Handle::cast(isolate->factory()->null_value()) : Handle::cast(isolate->factory()->wasm_null()), ValueType::RefNull(expr.repr())); diff --git a/deps/v8/src/wasm/function-body-decoder-impl.h b/deps/v8/src/wasm/function-body-decoder-impl.h index 4fccfd08c220dc..290660104a5936 100644 --- a/deps/v8/src/wasm/function-body-decoder-impl.h +++ b/deps/v8/src/wasm/function-body-decoder-impl.h @@ -3280,7 +3280,7 @@ class WasmFullDecoder : public WasmDecoder { } if (catch_case.kind == kCatchRef || catch_case.kind == kCatchAllRef) { stack_.EnsureMoreCapacity(1, this->zone_); - Push(kWasmExnRef); + Push(ValueType::Ref(HeapType::kExn)); push_count += 1; } Control* target = control_at(catch_case.br_imm.depth); @@ -3306,14 +3306,7 @@ class WasmFullDecoder : public WasmDecoder { DECODE(ThrowRef) { this->detected_->Add(kFeature_exnref); - Value value = Pop(); - if (!VALIDATE( - (value.type.kind() == kRef || value.type.kind() == kRefNull) && - value.type.heap_type() == HeapType::kExn)) { - this->DecodeError("invalid type for throw_ref: expected exnref, found %s", - value.type.name().c_str()); - return 0; - } + Value value = Pop(kWasmExnRef); CALL_INTERFACE_IF_OK_AND_REACHABLE(ThrowRef, &value); MarkMightThrow(); EndControl(); @@ -3490,7 +3483,11 @@ class WasmFullDecoder : public WasmDecoder { if (!VALIDATE(TypeCheckOneArmedIf(c))) return 0; } if (c->is_try_table()) { - current_catch_ = c->previous_catch; + // "Pop" the {current_catch_} index. We did not push it if the block has + // no handler, so also skip it here in this case. + if (c->catch_cases.size() > 0) { + current_catch_ = c->previous_catch; + } FallThrough(); // Temporarily set the reachability for the catch handlers, and restore // it before we actually exit the try block. @@ -3509,7 +3506,7 @@ class WasmFullDecoder : public WasmDecoder { } if (catch_case.kind == kCatchRef || catch_case.kind == kCatchAllRef) { stack_.EnsureMoreCapacity(1, this->zone_); - Push(kWasmExnRef); + Push(ValueType::Ref(HeapType::kExn)); push_count += 1; } base::Vector values( @@ -4659,7 +4656,8 @@ class WasmFullDecoder : public WasmDecoder { ((!null_succeeds || !obj.type.is_nullable()) && (expected_type.representation() == HeapType::kNone || expected_type.representation() == HeapType::kNoFunc || - expected_type.representation() == HeapType::kNoExtern)); + expected_type.representation() == HeapType::kNoExtern || + expected_type.representation() == HeapType::kNoExn)); } bool TypeCheckAlwaysFails(Value obj, uint32_t ref_index) { // All old casts / checks treat null as failure. diff --git a/deps/v8/src/wasm/graph-builder-interface.cc b/deps/v8/src/wasm/graph-builder-interface.cc index f55d1fbd28e43a..0e98427f07a144 100644 --- a/deps/v8/src/wasm/graph-builder-interface.cc +++ b/deps/v8/src/wasm/graph-builder-interface.cc @@ -1406,7 +1406,7 @@ class WasmGraphBuildingInterface { if (catch_case.kind == kCatchAll || catch_case.kind == kCatchAllRef) { if (catch_case.kind == kCatchAllRef) { - DCHECK_EQ(values[0].type, kWasmExnRef); + DCHECK_EQ(values[0].type, ValueType::Ref(HeapType::kExn)); values[0].node = block->try_info->exception; } BrOrRet(decoder, catch_case.br_imm.depth); @@ -1420,11 +1420,60 @@ class WasmGraphBuildingInterface { base::Vector values_without_exnref = catch_case.kind == kCatch ? values : values.SubVector(0, values.size() - 1); - CatchAndUnpackWasmException(decoder, block, exception, - catch_case.maybe_tag.tag_imm.tag, caught_tag, - expected_tag, values_without_exnref); + + if (catch_case.maybe_tag.tag_imm.tag->sig->parameter_count() == 1 && + catch_case.maybe_tag.tag_imm.tag->sig->GetParam(0) == kWasmExternRef) { + // Check for the special case where the tag is WebAssembly.JSTag and the + // exception is not a WebAssembly.Exception. In this case the exception is + // caught and pushed on the operand stack. + // Only perform this check if the tag signature is the same as + // the JSTag signature, i.e. a single externref, otherwise + // we know statically that it cannot be the JSTag. + + TFNode* is_js_exn = builder_->IsExceptionTagUndefined(caught_tag); + auto [exn_is_js, exn_is_wasm] = builder_->BranchExpectFalse(is_js_exn); + SsaEnv* exn_is_js_env = Split(decoder->zone(), ssa_env_); + exn_is_js_env->control = exn_is_js; + SsaEnv* exn_is_wasm_env = Steal(decoder->zone(), ssa_env_); + exn_is_wasm_env->control = exn_is_wasm; + + // Case 1: A wasm exception. + SetEnv(exn_is_wasm_env); + CatchAndUnpackWasmException(decoder, block, exception, + catch_case.maybe_tag.tag_imm.tag, caught_tag, + expected_tag, values_without_exnref); + + // Case 2: A JS exception. + SetEnv(exn_is_js_env); + TFNode* js_tag = builder_->LoadJSTag(); + TFNode* compare = builder_->ExceptionTagEqual(expected_tag, js_tag); + auto [if_catch, if_no_catch] = builder_->BranchNoHint(compare); + // Merge the wasm no-catch and JS no-catch paths. + SsaEnv* if_no_catch_env = Split(decoder->zone(), ssa_env_); + if_no_catch_env->control = if_no_catch; + SetEnv(if_no_catch_env); + Goto(decoder, block->try_info->catch_env); + // Merge the wasm catch and JS catch paths. + SsaEnv* if_catch_env = Steal(decoder->zone(), ssa_env_); + if_catch_env->control = if_catch; + SetEnv(if_catch_env); + Goto(decoder, block->block_env); + + // The final env is a merge of case 1 and 2. The unpacked value is a Phi + // of the unpacked value (case 1) and the exception itself (case 2). + SetEnv(block->block_env); + TFNode* phi_inputs[] = {values[0].node, exception, + block->block_env->control}; + TFNode* ref = builder_->Phi(wasm::kWasmExternRef, 2, phi_inputs); + SetAndTypeNode(&values[0], ref); + } else { + CatchAndUnpackWasmException(decoder, block, exception, + catch_case.maybe_tag.tag_imm.tag, caught_tag, + expected_tag, values_without_exnref); + } + if (catch_case.kind == kCatchRef) { - DCHECK_EQ(values.last().type, kWasmExnRef); + DCHECK_EQ(values.last().type, ValueType::Ref(HeapType::kExn)); values.last().node = block->try_info->exception; } BrOrRet(decoder, catch_case.br_imm.depth); @@ -1803,6 +1852,7 @@ class WasmGraphBuildingInterface { case HeapType::kNone: case HeapType::kNoExtern: case HeapType::kNoFunc: + case HeapType::kNoExn: DCHECK(null_succeeds); // This is needed for BrOnNull. {value_on_branch} is on the value stack // and BrOnNull interacts with the values on the stack. @@ -1841,6 +1891,7 @@ class WasmGraphBuildingInterface { case HeapType::kNone: case HeapType::kNoExtern: case HeapType::kNoFunc: + case HeapType::kNoExn: DCHECK(null_succeeds); // We need to store a node in the stack where the decoder so far only // pushed a value and expects the `BrOnCastFailAbstract` to set it. @@ -2748,7 +2799,7 @@ class WasmGraphBuildingInterface { void ThrowRef(FullDecoder* decoder, TFNode* exception) { DCHECK_NOT_NULL(exception); - CheckForException(decoder, builder_->Rethrow(exception), false); + CheckForException(decoder, builder_->ThrowRef(exception), false); builder_->TerminateThrow(effect(), control()); } }; diff --git a/deps/v8/src/wasm/module-instantiate.cc b/deps/v8/src/wasm/module-instantiate.cc index c74dc117ee21b9..51b9d1e7492444 100644 --- a/deps/v8/src/wasm/module-instantiate.cc +++ b/deps/v8/src/wasm/module-instantiate.cc @@ -2655,8 +2655,10 @@ void InstanceBuilder::ProcessExports( isolate_); uint32_t canonical_sig_index = module_->isorecursive_canonical_type_ids[tag.sig_index]; + Handle instance = + handle(trusted_instance_data->instance_object(), isolate_); wrapper = WasmTagObject::New(isolate_, tag.sig, canonical_sig_index, - tag_object); + tag_object, instance); tags_wrappers_[exp.index] = wrapper; } value = wrapper; diff --git a/deps/v8/src/wasm/turboshaft-graph-interface.cc b/deps/v8/src/wasm/turboshaft-graph-interface.cc index d63d5e36e81409..f431b0fdb94a7e 100644 --- a/deps/v8/src/wasm/turboshaft-graph-interface.cc +++ b/deps/v8/src/wasm/turboshaft-graph-interface.cc @@ -3010,9 +3010,10 @@ class TurboshaftGraphBuildingInterface : public WasmGraphBuilderBase { BrOrRet(decoder, catch_case.br_imm.depth); return; } + V native_context = instance_cache_.native_context(); V caught_tag = V::Cast( CallBuiltinThroughJumptable( - decoder, instance_cache_.native_context(), + decoder, native_context, {block->exception, LOAD_ROOT(wasm_exception_tag_symbol)})); V instance_tags = LOAD_IMMUTABLE_INSTANCE_FIELD(trusted_instance_data(), TagsTable, @@ -3022,17 +3023,70 @@ class TurboshaftGraphBuildingInterface : public WasmGraphBuilderBase { TSBlock* if_catch = __ NewBlock(); TSBlock* if_no_catch = NewBlockWithPhis(decoder, nullptr); SetupControlFlowEdge(decoder, if_no_catch); + + // If the tags don't match we continue with the next tag by setting the + // no-catch environment as the new {block->false_or_loop_or_catch_block} + // here. block->false_or_loop_or_catch_block = if_no_catch; - __ Branch(ConditionWithHint(__ TaggedEqual(caught_tag, expected_tag)), - if_catch, block->false_or_loop_or_catch_block); - __ Bind(if_catch); - if (catch_case.kind == kCatchRef) { - UnpackWasmException(decoder, block->exception, - values.SubVector(0, values.size() - 1)); - values.last().op = block->exception; + + if (catch_case.maybe_tag.tag_imm.tag->sig->parameter_count() == 1 && + catch_case.maybe_tag.tag_imm.tag->sig->GetParam(0) == kWasmExternRef) { + // Check for the special case where the tag is WebAssembly.JSTag and the + // exception is not a WebAssembly.Exception. In this case the exception is + // caught and pushed on the operand stack. + // Only perform this check if the tag signature is the same as + // the JSTag signature, i.e. a single externref, otherwise + // we know statically that it cannot be the JSTag. + V caught_tag_undefined = + __ TaggedEqual(caught_tag, LOAD_ROOT(UndefinedValue)); + Label if_catch(&asm_); + Label<> no_catch_merge(&asm_); + + IF (UNLIKELY(caught_tag_undefined)) { + V tag_object = __ Load( + native_context, LoadOp::Kind::TaggedBase(), + MemoryRepresentation::TaggedPointer(), + NativeContext::OffsetOfElementAt(Context::WASM_JS_TAG_INDEX)); + V js_tag = __ Load(tag_object, LoadOp::Kind::TaggedBase(), + MemoryRepresentation::TaggedPointer(), + WasmTagObject::kTagOffset); + GOTO_IF(__ TaggedEqual(expected_tag, js_tag), if_catch, + block->exception); + GOTO(no_catch_merge); + } ELSE { + IF (__ TaggedEqual(caught_tag, expected_tag)) { + if (catch_case.kind == kCatchRef) { + UnpackWasmException(decoder, block->exception, + values.SubVector(0, values.size() - 1)); + values.last().op = block->exception; + } else { + UnpackWasmException(decoder, block->exception, values); + } + GOTO(if_catch, values[0].op); + } + GOTO(no_catch_merge); + } + + BIND(no_catch_merge); + __ Goto(if_no_catch); + + BIND(if_catch, caught_exception); + // The first unpacked value is the exception itself in the case of a JS + // exception. + values[0].op = caught_exception; } else { - UnpackWasmException(decoder, block->exception, values); + __ Branch(ConditionWithHint(__ TaggedEqual(caught_tag, expected_tag)), + if_catch, if_no_catch); + __ Bind(if_catch); + if (catch_case.kind == kCatchRef) { + UnpackWasmException(decoder, block->exception, + values.SubVector(0, values.size() - 1)); + values.last().op = block->exception; + } else { + UnpackWasmException(decoder, block->exception, values); + } } + BrOrRet(decoder, catch_case.br_imm.depth); bool is_last = &catch_case == &block->catch_cases.last(); diff --git a/deps/v8/src/wasm/wasm-builtin-list.h b/deps/v8/src/wasm/wasm-builtin-list.h index 15b8754bb4ace3..1d976b5b012dff 100644 --- a/deps/v8/src/wasm/wasm-builtin-list.h +++ b/deps/v8/src/wasm/wasm-builtin-list.h @@ -51,6 +51,7 @@ namespace v8::internal::wasm { V(WasmAllocateFixedArray) \ V(WasmThrow) \ V(WasmRethrow) \ + V(WasmThrowRef) \ V(WasmRethrowExplicitContext) \ V(WasmTraceEnter) \ V(WasmTraceExit) \ diff --git a/deps/v8/src/wasm/wasm-js.cc b/deps/v8/src/wasm/wasm-js.cc index c6d20d790c8504..35f34db216a5b1 100644 --- a/deps/v8/src/wasm/wasm-js.cc +++ b/deps/v8/src/wasm/wasm-js.cc @@ -1281,9 +1281,12 @@ i::Handle DefaultReferenceValue(i::Isolate* isolate, DCHECK(type.is_object_reference()); // Use undefined for JS type (externref) but null for wasm types as wasm does // not know undefined. - if (type.heap_representation() == i::wasm::HeapType::kExtern || - type.heap_representation() == i::wasm::HeapType::kNoExtern) { + if (type.heap_representation() == i::wasm::HeapType::kExtern) { return isolate->factory()->undefined_value(); + } else if (type.heap_representation() == i::wasm::HeapType::kNoExtern || + type.heap_representation() == i::wasm::HeapType::kExn || + type.heap_representation() == i::wasm::HeapType::kNoExn) { + return isolate->factory()->null_value(); } return isolate->factory()->wasm_null(); } @@ -1852,7 +1855,8 @@ void WebAssemblyTagImpl(const v8::FunctionCallbackInfo& info) { i::wasm::GetWasmEngine()->type_canonicalizer()->AddRecursiveGroup(&sig); i::Handle tag_object = - i::WasmTagObject::New(i_isolate, &sig, canonical_type_index, tag); + i::WasmTagObject::New(i_isolate, &sig, canonical_type_index, tag, + i_isolate->factory()->undefined_value()); info.GetReturnValue().Set(Utils::ToLocal(tag_object)); } @@ -1898,6 +1902,7 @@ uint32_t GetEncodedSize(i::Handle tag_object) { void EncodeExceptionValues(v8::Isolate* isolate, i::Handle> signature, + i::Handle tag_object, const Local& arg, ErrorThrower* thrower, i::Handle values_out) { Local context = isolate->GetCurrentContext(); @@ -1955,6 +1960,19 @@ void EncodeExceptionValues(v8::Isolate* isolate, case i::wasm::kRefNull: { const char* error_message; i::Handle value_handle = Utils::OpenHandle(*value); + + if (type.has_index()) { + // Canonicalize the type using the tag's original module. + i::Tagged maybe_instance = tag_object->instance(); + CHECK(!i::IsUndefined(maybe_instance)); + auto instance = i::WasmInstanceObject::cast(maybe_instance); + const i::wasm::WasmModule* module = instance->module(); + uint32_t canonical_index = + module->isorecursive_canonical_type_ids[type.ref_index()]; + type = i::wasm::ValueType::RefMaybeNull(canonical_index, + type.nullability()); + } + if (!internal::wasm::JSToWasmObject(i_isolate, value_handle, type, &error_message) .ToHandle(&value_handle)) { @@ -1964,12 +1982,14 @@ void EncodeExceptionValues(v8::Isolate* isolate, values_out->set(index++, *value_handle); break; } + case i::wasm::kS128: + thrower->TypeError("Invalid type v128"); + return; case i::wasm::kRtt: case i::wasm::kI8: case i::wasm::kI16: case i::wasm::kVoid: case i::wasm::kBottom: - case i::wasm::kS128: UNREACHABLE(); } } @@ -2001,6 +2021,11 @@ void WebAssemblyExceptionImpl(const v8::FunctionCallbackInfo& info) { i::Handle::cast(arg0); i::Handle tag( i::WasmExceptionTag::cast(tag_object->tag()), i_isolate); + auto js_tag = i::WasmTagObject::cast(i_isolate->context()->wasm_js_tag()); + if (*tag == js_tag->tag()) { + thrower.TypeError("Argument 0 cannot be WebAssembly.JSTag"); + return; + } uint32_t size = GetEncodedSize(tag_object); i::Handle runtime_exception = i::WasmExceptionPackage::New(i_isolate, tag, size); @@ -2010,7 +2035,8 @@ void WebAssemblyExceptionImpl(const v8::FunctionCallbackInfo& info) { runtime_exception)); i::Handle> signature( tag_object->serialized_signature(), i_isolate); - EncodeExceptionValues(isolate, signature, info[1], &thrower, values); + EncodeExceptionValues(isolate, signature, tag_object, info[1], &thrower, + values); if (thrower.error()) return; // Third argument: optional ExceptionOption ({traceStack: }). @@ -2022,11 +2048,12 @@ void WebAssemblyExceptionImpl(const v8::FunctionCallbackInfo& info) { Local context = isolate->GetCurrentContext(); Local trace_stack_obj = Local::Cast(info[2]); Local trace_stack_key = v8_str(isolate, "traceStack"); - v8::MaybeLocal maybe_trace_stack = - trace_stack_obj->Get(context, trace_stack_key); v8::Local trace_stack_value; - if (maybe_trace_stack.ToLocal(&trace_stack_value) && - trace_stack_value->BooleanValue(isolate)) { + if (!trace_stack_obj->Get(context, trace_stack_key) + .ToLocal(&trace_stack_value)) { + return; + } + if (trace_stack_value->BooleanValue(isolate)) { auto caller = Utils::OpenHandle(*info.NewTarget()); i::Handle capture_result; @@ -2445,9 +2472,9 @@ void WebAssemblyTableGrowImpl(const v8::FunctionCallbackInfo& info) { namespace { void WasmObjectToJSReturnValue(v8::ReturnValue& return_value, i::Handle value, - i::wasm::HeapType type, i::Isolate* isolate, + i::wasm::ValueType type, i::Isolate* isolate, ErrorThrower* thrower) { - switch (type.representation()) { + switch (type.heap_type().representation()) { case internal::wasm::HeapType::kStringViewWtf8: thrower->TypeError("%s", "stringview_wtf8 has no JS representation"); break; @@ -2457,6 +2484,10 @@ void WasmObjectToJSReturnValue(v8::ReturnValue& return_value, case internal::wasm::HeapType::kStringViewIter: thrower->TypeError("%s", "stringview_iter has no JS representation"); break; + case internal::wasm::HeapType::kExn: + case internal::wasm::HeapType::kNoExn: + thrower->TypeError("invalid type %s", type.name().c_str()); + break; default: { return_value.Set(Utils::ToLocal(i::wasm::WasmToJSObject(isolate, value))); break; @@ -2490,8 +2521,8 @@ void WebAssemblyTableGetImpl(const v8::FunctionCallbackInfo& info) { i::WasmTableObject::Get(i_isolate, receiver, index); v8::ReturnValue return_value = info.GetReturnValue(); - WasmObjectToJSReturnValue(return_value, result, receiver->type().heap_type(), - i_isolate, &thrower); + WasmObjectToJSReturnValue(return_value, result, receiver->type(), i_isolate, + &thrower); } // WebAssembly.Table.set(num, any) @@ -2723,12 +2754,14 @@ void WebAssemblyExceptionGetArgImpl( case i::wasm::kRefNull: decode_index++; break; + case i::wasm::kS128: + decode_index += 8; + break; case i::wasm::kRtt: case i::wasm::kI8: case i::wasm::kI16: case i::wasm::kVoid: case i::wasm::kBottom: - case i::wasm::kS128: UNREACHABLE(); } } @@ -2767,16 +2800,17 @@ void WebAssemblyExceptionGetArgImpl( case i::wasm::kRefNull: { i::Handle obj = handle(values->get(decode_index), i_isolate); ReturnValue return_value = info.GetReturnValue(); - return WasmObjectToJSReturnValue(return_value, obj, - signature->get(index).heap_type(), + return WasmObjectToJSReturnValue(return_value, obj, signature->get(index), i_isolate, &thrower); } + case i::wasm::kS128: + thrower.TypeError("Invalid type v128"); + return; case i::wasm::kRtt: case i::wasm::kI8: case i::wasm::kI16: case i::wasm::kVoid: case i::wasm::kBottom: - case i::wasm::kS128: UNREACHABLE(); } info.GetReturnValue().Set(result); @@ -2836,8 +2870,7 @@ void WebAssemblyGlobalGetValueCommon( case i::wasm::kRef: case i::wasm::kRefNull: { WasmObjectToJSReturnValue(return_value, receiver->GetRef(), - receiver->type().heap_type(), i_isolate, - &thrower); + receiver->type(), i_isolate, &thrower); break; } case i::wasm::kRtt: @@ -3224,9 +3257,9 @@ void WasmJs::PrepareForSnapshot(Isolate* isolate) { // Note the canonical_type_index is reset in WasmJs::Install s.t. // type_canonicalizer bookkeeping remains valid. static constexpr uint32_t kInitialCanonicalTypeIndex = 0; - Handle js_tag_object = - WasmTagObject::New(isolate, &kWasmExceptionTagSignature, - kInitialCanonicalTypeIndex, js_tag); + Handle js_tag_object = WasmTagObject::New( + isolate, &kWasmExceptionTagSignature, kInitialCanonicalTypeIndex, + js_tag, isolate->factory()->undefined_value()); native_context->set_wasm_js_tag(*js_tag_object); JSObject::AddProperty(isolate, webassembly, "JSTag", js_tag_object, ro_attributes); diff --git a/deps/v8/src/wasm/wasm-objects.cc b/deps/v8/src/wasm/wasm-objects.cc index ab1e4841a9f156..771fd4dfcfa354 100644 --- a/deps/v8/src/wasm/wasm-objects.cc +++ b/deps/v8/src/wasm/wasm-objects.cc @@ -1785,7 +1785,8 @@ void WasmArray::SetTaggedElement(uint32_t index, Handle value, Handle WasmTagObject::New(Isolate* isolate, const wasm::FunctionSig* sig, uint32_t canonical_type_index, - Handle tag) { + Handle tag, + Handle instance) { Handle tag_cons(isolate->native_context()->wasm_tag_constructor(), isolate); @@ -1806,6 +1807,7 @@ Handle WasmTagObject::New(Isolate* isolate, tag_wrapper->set_serialized_signature(*serialized_sig); tag_wrapper->set_canonical_type_index(canonical_type_index); tag_wrapper->set_tag(*tag); + tag_wrapper->set_instance(*instance); return tag_wrapper; } @@ -2547,6 +2549,12 @@ MaybeHandle JSToWasmObject(Isolate* isolate, Handle value, case HeapType::kStringViewIter: *error_message = "stringview_iter has no JS representation"; return {}; + case HeapType::kExn: + *error_message = "invalid type (ref null exn)"; + return {}; + case HeapType::kNoExn: + *error_message = "invalid type (ref null noexn)"; + return {}; default: { HeapType::Representation repr = expected_canonical.heap_representation_non_shared(); @@ -2589,8 +2597,7 @@ MaybeHandle JSToWasmObject(Isolate* isolate, Handle value, return {}; } case HeapType::kExn: - if (!IsNull(*value, isolate)) return value; - *error_message = "null is not allowed for (ref exn)"; + *error_message = "invalid type (ref exn)"; return {}; case HeapType::kStruct: { if (IsWasmStruct(*value)) { diff --git a/deps/v8/src/wasm/wasm-objects.h b/deps/v8/src/wasm/wasm-objects.h index 341e97040b69f9..a7495d3cbb394e 100644 --- a/deps/v8/src/wasm/wasm-objects.h +++ b/deps/v8/src/wasm/wasm-objects.h @@ -605,7 +605,8 @@ class WasmTagObject static Handle New(Isolate* isolate, const wasm::FunctionSig* sig, uint32_t canonical_type_index, - Handle tag); + Handle tag, + Handle instance); TQ_OBJECT_CONSTRUCTORS(WasmTagObject) }; diff --git a/deps/v8/src/wasm/wasm-objects.tq b/deps/v8/src/wasm/wasm-objects.tq index dadb0342d08a94..7d937ec308f734 100644 --- a/deps/v8/src/wasm/wasm-objects.tq +++ b/deps/v8/src/wasm/wasm-objects.tq @@ -211,6 +211,7 @@ extern class WasmGlobalObject extends JSObject { extern class WasmTagObject extends JSObject { serialized_signature: PodArrayOfWasmValueType; tag: HeapObject; + instance: WasmInstanceObject|Undefined; canonical_type_index: Smi; } diff --git a/deps/v8/src/wasm/wasm-opcodes.cc b/deps/v8/src/wasm/wasm-opcodes.cc index 61a7f8414d6db9..a4d09e35264376 100644 --- a/deps/v8/src/wasm/wasm-opcodes.cc +++ b/deps/v8/src/wasm/wasm-opcodes.cc @@ -38,6 +38,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig) { case HeapType::kStringViewWtf8: case HeapType::kStringViewWtf16: case HeapType::kStringViewIter: + case HeapType::kExn: + case HeapType::kNoExn: return false; default: break; diff --git a/deps/v8/src/wasm/wrappers.cc b/deps/v8/src/wasm/wrappers.cc index 279779aa1df112..9746edd205cbb9 100644 --- a/deps/v8/src/wasm/wrappers.cc +++ b/deps/v8/src/wasm/wrappers.cc @@ -857,6 +857,8 @@ class WasmWrapperTSGraphBuilder : public WasmGraphBuilderBase { // TODO(14034): Add more fast paths? case wasm::HeapType::kExtern: case wasm::HeapType::kNoExtern: + case wasm::HeapType::kExn: + case wasm::HeapType::kNoExn: if (type.kind() == wasm::kRef) { IF (__ TaggedEqual(input, LOAD_ROOT(NullValue))) { CallRuntime(__ phase_zone(), Runtime::kWasmThrowJSTypeError, {}, @@ -867,9 +869,6 @@ class WasmWrapperTSGraphBuilder : public WasmGraphBuilderBase { return input; case wasm::HeapType::kString: return BuildCheckString(input, context, type); - case wasm::HeapType::kExn: - case wasm::HeapType::kNoExn: - return input; case wasm::HeapType::kNone: case wasm::HeapType::kNoFunc: case wasm::HeapType::kI31: diff --git a/deps/v8/test/mjsunit/regress/wasm/regress-372261626.js b/deps/v8/test/mjsunit/regress/wasm/regress-372261626.js new file mode 100644 index 00000000000000..23f49a64102dff --- /dev/null +++ b/deps/v8/test/mjsunit/regress/wasm/regress-372261626.js @@ -0,0 +1,21 @@ +// Copyright 2024 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: --experimental-wasm-exnref + +d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); + +let builder = new WasmModuleBuilder(); +let tag = builder.addTag(kSig_v_v); +builder.addFunction("main", kSig_v_v) + .addBody([ + kExprTry, kWasmVoid, + kExprTryTable, kWasmVoid, 0, + kExprEnd, + kExprThrow, tag, + kExprCatchAll, + kExprEnd + ]).exportFunc(); +let instance = builder.instantiate(); +assertDoesNotThrow(instance.exports.main); diff --git a/deps/v8/test/mjsunit/regress/wasm/regress-372993873.js b/deps/v8/test/mjsunit/regress/wasm/regress-372993873.js new file mode 100644 index 00000000000000..b216191872f784 --- /dev/null +++ b/deps/v8/test/mjsunit/regress/wasm/regress-372993873.js @@ -0,0 +1,12 @@ +// Copyright 2024 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. + +let tag = new WebAssembly.Tag({'parameters': []}); +let proxy = new Proxy({}, { + 'get': () => { + throw new Error('boom') + } +}); + +assertThrows(() => new WebAssembly.Exception(tag, [], proxy), Error, 'boom'); diff --git a/deps/v8/test/mjsunit/wasm/exceptions-api.js b/deps/v8/test/mjsunit/wasm/exceptions-api.js index 14a0b78a288051..36f48c374c1cdc 100644 --- a/deps/v8/test/mjsunit/wasm/exceptions-api.js +++ b/deps/v8/test/mjsunit/wasm/exceptions-api.js @@ -295,11 +295,11 @@ function TestGetArgHelper(types_str, types, values) { }}); let obj = {}; + // Creating a WA.Exception with the JSTag explicitly is not allowed. + assertThrows(() => new WebAssembly.Exception(WebAssembly.JSTag, [obj]), TypeError); // Catch with implicit wrapping. assertSame(obj, instance.exports.test(obj)); - // Catch with explicit wrapping. - assertSame(obj, instance.exports.test(new WebAssembly.Exception(WebAssembly.JSTag, [obj]))); // Don't catch with explicit wrapping. let not_js_tag = new WebAssembly.Tag({parameters:['externref']}); let exn = new WebAssembly.Exception(not_js_tag, [obj]); @@ -316,11 +316,6 @@ function TestGetArgHelper(types_str, types, values) { // Catch with explicit wrapping. assertSame(obj, instance.exports.test(new WebAssembly.Exception(not_js_tag, [obj]))); - // Don't catch with explicit wrapping. - exn = new WebAssembly.Exception(WebAssembly.JSTag, [obj]); - // TODO(thibaudm): Should the exception get implicitly unwrapped when it - // bubbles up from wasm to JS, even though it was wrapped explicitly? - assertThrowsEquals(() => instance.exports.test(exn), exn); // Don't catch with implicit wrapping. assertThrowsEquals(() => instance.exports.test(obj), obj); })(); diff --git a/deps/v8/test/mjsunit/wasm/exnref-api.js b/deps/v8/test/mjsunit/wasm/exnref-api.js new file mode 100644 index 00000000000000..8774ebfaeec4db --- /dev/null +++ b/deps/v8/test/mjsunit/wasm/exnref-api.js @@ -0,0 +1,205 @@ +// Copyright 2023 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: --allow-natives-syntax --experimental-wasm-exnref --turboshaft-wasm + +// This file is for the most parts a direct port of +// test/mjsunit/wasm/exceptions-api.js using the new exception handling +// proposal. + +d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); +d8.file.execute("test/mjsunit/wasm/exceptions-utils.js"); + +(function TestCatchJSTag() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let js_tag = builder.addImportedTag("", "tag", kSig_v_r); + + // Throw a JS object and check that we can catch it and unpack it using + // WebAssembly.JSTag in try_table. + function throw_ref(x) { + throw x; + } + let kJSThrowRef = builder.addImport("", "throw_ref", kSig_r_r); + let try_sig_index = builder.addType(kSig_r_v); + builder.addFunction("test", kSig_r_r) + .addBody([ + kExprBlock, try_sig_index, + kExprTryTable, try_sig_index, 1, + kCatchNoRef, js_tag, 0, + kExprLocalGet, 0, + kExprCallFunction, kJSThrowRef, + kExprEnd, + kExprEnd, + ]) + .exportFunc(); + + let instance = builder.instantiate({"": { + throw_ref: throw_ref, + tag: WebAssembly.JSTag, + }}); + + let obj = {}; + // Creating a WA.Exception with the JSTag explicitly is not allowed. + assertThrows(() => new WebAssembly.Exception(WebAssembly.JSTag, [obj]), TypeError); + + // Catch with implicit wrapping. + assertSame(obj, instance.exports.test(obj)); + // Don't catch with explicit wrapping. + let not_js_tag = new WebAssembly.Tag({parameters:['externref']}); + let exn = new WebAssembly.Exception(not_js_tag, [obj]); + assertThrowsEquals(() => instance.exports.test(exn), exn); + + + // There is a separate code path for tags with externref type, so also check + // that everything still works when the tag is *not* the JSTag. + + instance = builder.instantiate({"": { + throw_ref: throw_ref, + tag: not_js_tag + }}); + + // Catch with explicit wrapping. + assertSame(obj, instance.exports.test(new WebAssembly.Exception(not_js_tag, [obj]))); + // Don't catch with explicit wrapping. + // Don't catch with implicit wrapping. + assertThrowsEquals(() => instance.exports.test(obj), obj); +})(); + +(function TestCatchRefJSTag() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let js_tag = builder.addImportedTag("", "tag", kSig_v_r); + + // Throw a JS object and check that we can catch it and unpack it using + // WebAssembly.JSTag in try_table. + function throw_ref(x) { + throw x; + } + let kJSThrowRef = builder.addImport("", "throw_ref", kSig_r_r); + let try_sig_index = builder.addType(kSig_r_v); + let catch_sig_index = builder.addType(makeSig([], [kWasmExternRef, kWasmExnRef])); + builder.addFunction("test", kSig_r_r) + .addBody([ + kExprBlock, catch_sig_index, + kExprTryTable, try_sig_index, 1, + kCatchRef, js_tag, 0, + kExprLocalGet, 0, + kExprCallFunction, kJSThrowRef, + kExprEnd, + kExprReturn, + kExprEnd, + kExprDrop, + ]) + .exportFunc(); + + let instance = builder.instantiate({"": { + throw_ref: throw_ref, + tag: WebAssembly.JSTag, + }}); + + let obj = {}; + + // Catch with implicit wrapping. + assertSame(obj, instance.exports.test(obj)); + // Don't catch with explicit wrapping. + let not_js_tag = new WebAssembly.Tag({parameters:['externref']}); + let exn = new WebAssembly.Exception(not_js_tag, [obj]); + assertThrowsEquals(() => instance.exports.test(exn), exn); + + + // There is a separate code path for tags with externref type, so also check + // that everything still works when the tag is *not* the JSTag. + + instance = builder.instantiate({"": { + throw_ref: throw_ref, + tag: not_js_tag + }}); + + // Catch with explicit wrapping. + assertSame(obj, instance.exports.test(new WebAssembly.Exception(not_js_tag, [obj]))); + // Don't catch with implicit wrapping. + assertThrowsEquals(() => instance.exports.test(obj), obj); +})(); + +(function TestCatchRefThrowRefJSTag() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let js_tag = builder.addImportedTag("", "tag", kSig_v_r); + + // Throw a JS object and check that we can catch it and unpack it using + // WebAssembly.JSTag in try_table, then rethrow it as a JS object with + // throw_ref. + function throw_ref(x) { + throw x; + } + let kJSThrowRef = builder.addImport("", "throw_ref", kSig_r_r); + let try_sig_index = builder.addType(kSig_r_v); + let catch_sig_index = builder.addType(makeSig([], [kWasmExternRef, kWasmExnRef])); + builder.addFunction("test", kSig_r_r) + .addBody([ + kExprBlock, catch_sig_index, + kExprTryTable, try_sig_index, 1, + kCatchRef, js_tag, 0, + kExprLocalGet, 0, + kExprCallFunction, kJSThrowRef, + kExprEnd, + kExprReturn, + kExprEnd, + kExprThrowRef, + ]) + .exportFunc(); + + let instance = builder.instantiate({"": { + throw_ref: throw_ref, + tag: WebAssembly.JSTag, + }}); + + let obj = {}; + + // Catch and rethrown with implicit wrapping. + assertThrowsEquals(() => instance.exports.test(obj), obj); + // Don't catch with explicit wrapping. + let not_js_tag = new WebAssembly.Tag({parameters:['externref']}); + exn = new WebAssembly.Exception(not_js_tag, [obj]); + assertThrowsEquals(() => instance.exports.test(exn), exn); + + + // There is a separate code path for tags with externref type, so also check + // that everything still works when the tag is *not* the JSTag. + + instance = builder.instantiate({"": { + throw_ref: throw_ref, + tag: not_js_tag + }}); + + // Catch and rethrow with explicit wrapping -> not unwrapped in this case. + exn = new WebAssembly.Exception(not_js_tag, [obj]); + assertThrowsEquals(() => instance.exports.test(exn), exn); + // Don't catch with implicit wrapping. + assertThrowsEquals(() => instance.exports.test(obj), obj); +})(); + +(function TestThrowJSTag() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let js_tag = builder.addImportedTag("", "tag", kSig_v_r); + + // Throw a JS object with WebAssembly.JSTag and check that we can catch + // it as-is from JavaScript. + builder.addFunction("test", kSig_v_r) + .addBody([ + kExprLocalGet, 0, + kExprThrow, js_tag, + ]) + .exportFunc(); + + let instance = builder.instantiate({"": { + tag: WebAssembly.JSTag, + }}); + + let obj = {}; + assertThrowsEquals(() => instance.exports.test(obj), obj); + assertThrowsEquals(() => instance.exports.test(5), 5); +})(); diff --git a/deps/v8/test/mjsunit/wasm/exnref-global.js b/deps/v8/test/mjsunit/wasm/exnref-global.js index 8584ac8126c6dc..3f9b1e1aa30021 100644 --- a/deps/v8/test/mjsunit/wasm/exnref-global.js +++ b/deps/v8/test/mjsunit/wasm/exnref-global.js @@ -28,12 +28,15 @@ let kSig_e_v = makeSig([], [kWasmExnRef]); print(arguments.callee.name); let builder = new WasmModuleBuilder(); let g = builder.addGlobal(kWasmExnRef, false, false); - builder.addFunction('push_and_return_exnref', kSig_e_v) - .addBody([kExprGlobalGet, g.index]) + builder.addFunction('push_and_check_exnref', kSig_i_v) + .addBody([ + kExprGlobalGet, g.index, + kExprRefIsNull, kExnRefCode, + ]) .exportFunc(); let instance = builder.instantiate(); - assertEquals(null, instance.exports.push_and_return_exnref()); + assertEquals(1, instance.exports.push_and_check_exnref()); })(); // Test custom initialization index for a global "exnref" variable. @@ -44,8 +47,16 @@ let kSig_e_v = makeSig([], [kWasmExnRef]); builder.addFunction('push_and_return_exnref', kSig_e_v) .addBody([kExprGlobalGet, g_index]) .exportFunc(); - let exception = { x: "my fancy exception" }; - let instance = builder.instantiate({ "m": { "exn": exception }}); + assertThrows(() => builder.instantiate({ "m": { "exn": {} }}), WebAssembly.LinkError); + assertThrows(() => builder.instantiate({ "m": { "exn": null }}), WebAssembly.LinkError); +})(); - assertSame(exception, instance.exports.push_and_return_exnref()); +(function TestGlobalExnRefJsApi() { + print(arguments.callee.name); + + let builder = new WasmModuleBuilder(); + let g_index = builder.addGlobal(kWasmExnRef, true, false).exportAs('g'); + let instance = builder.instantiate(); + assertThrows(() => new WebAssembly.Global({value: "exnref", mutable: true}, null), TypeError); + assertThrows(() => { instance.exports.g.value; }, TypeError); })(); diff --git a/deps/v8/test/mjsunit/wasm/exnref.js b/deps/v8/test/mjsunit/wasm/exnref.js index 68030588e68838..401b426d381a62 100644 --- a/deps/v8/test/mjsunit/wasm/exnref.js +++ b/deps/v8/test/mjsunit/wasm/exnref.js @@ -9,7 +9,7 @@ // Tests that are independent of the version of the proposal are not included // (e.g. tests that only use the `throw` instruction), and some exnref-specific // tests are added. -// See also exnref-rethrow.js and exnref-global.js. +// See also exnref-rethrow.js, exnref-global.js and exnref-api.js. d8.file.execute("test/mjsunit/wasm/wasm-module-builder.js"); d8.file.execute("test/mjsunit/wasm/exceptions-utils.js"); @@ -611,12 +611,11 @@ d8.file.execute("test/mjsunit/wasm/exceptions-utils.js"); kExprEnd, kExprReturn, kExprEnd, - kExprGlobalSet, g.index, + kExprThrowRef ]).exportFunc(); let instance = builder.instantiate(); - instance.exports.catch_all_ref(); - assertTrue(instance.exports.g.value instanceof WebAssembly.Exception); + assertThrows(instance.exports.catch_all_ref, WebAssembly.Exception); })(); (function TestCatchRefTwoParams() { diff --git a/deps/v8/test/mjsunit/wasm/gc-casts-exnref.js b/deps/v8/test/mjsunit/wasm/gc-casts-exnref.js new file mode 100644 index 00000000000000..632d3a6e0aa7ae --- /dev/null +++ b/deps/v8/test/mjsunit/wasm/gc-casts-exnref.js @@ -0,0 +1,339 @@ +// Copyright 2024 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: --experimental-wasm-exnref --no-experimental-wasm-inlining + +d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); + +let getExnRef = function() { + let tag = new WebAssembly.Tag({parameters: []}); + return new WebAssembly.Exception(tag, []); +} + +// Helper module to produce an exnref or convert a JS value to an exnref. +let helper = (function () { + let builder = new WasmModuleBuilder(); + let tag_index = builder.addTag(kSig_v_v); + let throw_index = builder.addImport('m', 'import', kSig_v_r); + builder.addFunction('get_exnref', makeSig([], [kWasmExnRef])) + .addBody([ + kExprTryTable, kWasmVoid, 1, + kCatchAllRef, 0, + kExprThrow, tag_index, + kExprEnd, + kExprUnreachable, + ]).exportFunc(); + builder.addFunction('to_exnref', makeSig([kWasmExternRef], [kWasmExnRef])) + .addBody([ + kExprTryTable, kWasmVoid, 1, + kCatchAllRef, 0, + kExprLocalGet, 0, + kExprCallFunction, throw_index, + kExprEnd, + kExprUnreachable, + ]).exportFunc(); + function throw_js(r) { throw r; } + let instance = builder.instantiate({m: {import: throw_js}}); + return instance; +})(); + +(function RefTestExnRef() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + + let get_exnref = builder.addImport('m', 'get_exnref', makeSig([], [kWasmExnRef])); + builder.addFunction('testExnRef', + makeSig([], [kWasmI32, kWasmI32, kWasmI32, kWasmI32])) + .addLocals(kWasmExnRef, 1) + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprRefTest, kExnRefCode, + kExprLocalGet, 0, + kGCPrefix, kExprRefTest, kNullExnRefCode, + kExprCallFunction, get_exnref, + kGCPrefix, kExprRefTest, kExnRefCode, + kExprCallFunction, get_exnref, + kGCPrefix, kExprRefTest, kNullExnRefCode, + ]).exportFunc(); + + builder.addFunction('testNullExnRef', + makeSig([], [kWasmI32, kWasmI32, kWasmI32, kWasmI32])) + .addLocals(kWasmExnRef, 1) + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprRefTestNull, kExnRefCode, + kExprLocalGet, 0, + kGCPrefix, kExprRefTestNull, kNullExnRefCode, + kExprCallFunction, get_exnref, + kGCPrefix, kExprRefTestNull, kExnRefCode, + kExprCallFunction, get_exnref, + kGCPrefix, kExprRefTestNull, kNullExnRefCode, + ]).exportFunc(); + + + let instance = builder.instantiate({m: {get_exnref: helper.exports.get_exnref}}); + let wasm = instance.exports; + assertEquals([0, 0, 1, 0], wasm.testExnRef()); + assertEquals([1, 1, 1, 0], wasm.testNullExnRef()); +})(); + +(function RefCastExnRef() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let to_exnref = builder.addImport('m', 'to_exnref', makeSig([kWasmExternRef], [kWasmExnRef])); + + builder.addFunction('castToExnRef', + makeSig([kWasmExternRef], [])) + .addBody([ + kExprLocalGet, 0, + kExprCallFunction, to_exnref, + kGCPrefix, kExprRefCast, kExnRefCode, + kExprThrowRef]) + .exportFunc(); + builder.addFunction('castToNullExnRef', + makeSig([kWasmExternRef], [])) + .addBody([ + kExprLocalGet, 0, + kExprCallFunction, to_exnref, + kGCPrefix, kExprRefCast, kNullExnRefCode, + kExprDrop]) + .exportFunc(); + builder.addFunction('castNullToExnRef', + makeSig([kWasmExternRef], [])) + .addBody([ + kExprLocalGet, 0, + kExprCallFunction, to_exnref, + kGCPrefix, kExprRefCastNull, kExnRefCode, + kExprThrowRef]) + .exportFunc(); + builder.addFunction('castNullToNullExnRef', + makeSig([kWasmExternRef], [])) + .addBody([ + kExprLocalGet, 0, + kExprCallFunction, to_exnref, + kGCPrefix, kExprRefCastNull, kNullExnRefCode, + kGCPrefix, kExprRefCastNull, kExnRefCode, + kExprThrowRef]) + .exportFunc(); + + builder.addFunction('nullCastToExnRef', kSig_v_v) + .addLocals(kWasmExnRef, 1) + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprRefCast, kExnRefCode, + kExprThrowRef]) + .exportFunc(); + builder.addFunction('nullCastToNullExnRef', kSig_v_v) + .addLocals(kWasmExnRef, 1) + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprRefCast, kNullExnRefCode, + kExprDrop]) + .exportFunc(); + builder.addFunction('nullCastNullToExnRef', kSig_v_v) + .addLocals(kWasmExnRef, 1) + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprRefCastNull, kExnRefCode, + kExprThrowRef]) + .exportFunc(); + builder.addFunction('nullCastNullToNullExnRef', kSig_v_v) + .addLocals(kWasmExnRef, 1) + .addBody([ + kExprLocalGet, 0, + kGCPrefix, kExprRefCastNull, kNullExnRefCode, + kGCPrefix, kExprRefCastNull, kExnRefCode, + kExprThrowRef]) + .exportFunc(); + + let instance = builder.instantiate({m: {to_exnref: helper.exports.to_exnref}}); + let wasm = instance.exports; + + let obj = {}; + assertTraps(kTrapIllegalCast, wasm.nullCastToExnRef); + assertThrowsEquals(() => wasm.castToExnRef(obj), obj); + assertTraps(kTrapIllegalCast, wasm.nullCastToNullExnRef); + assertTraps(kTrapIllegalCast, () => wasm.castToNullExnRef(obj)); + + assertThrows(wasm.nullCastNullToExnRef, Error, /rethrowing null value/); + assertThrowsEquals(() => wasm.castNullToExnRef(obj), obj); + assertThrows(wasm.nullCastNullToNullExnRef, Error, /rethrowing null value/); + assertTraps(kTrapIllegalCast, () => wasm.castNullToNullExnRef(obj)); +})(); + +(function BrOnCastExnRef() { + print(arguments.callee.name); + let builder = new WasmModuleBuilder(); + let get_exnref = builder.addImport('m', 'get_exnref', makeSig([], [kWasmExnRef])); + + let get_exnref_if = builder.addFunction('getExnRefOrNull', + makeSig([kWasmI32], [kWasmExnRef])) + .addBody([ + kExprLocalGet, 0, + kExprIf, kExnRefCode, + kExprCallFunction, get_exnref, + kExprElse, + kExprRefNull, kExnRefCode, + kExprEnd, + ]).index; + builder.addFunction('castToExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRef, kExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCast( + 0, wasmRefNullType(kWasmExnRef), wasmRefType(kWasmExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + builder.addFunction('castToNullExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRef, kNullExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCast( + 0, wasmRefNullType(kWasmExnRef), wasmRefType(kWasmNullExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + + builder.addFunction('castNullToExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRefNull, kExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCast(0, + wasmRefNullType(kWasmExnRef), wasmRefNullType(kWasmExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + builder.addFunction('castNullToNullExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRefNull, kNullExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCast(0, + wasmRefNullType(kWasmExnRef), wasmRefNullType(kWasmNullExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + + builder.addFunction('castFailToExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRefNull, kExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCastFail(0, + wasmRefNullType(kWasmExnRef), wasmRefType(kWasmExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + builder.addFunction('castFailToNullExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRefNull, kExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCastFail(0, + wasmRefNullType(kWasmExnRef), wasmRefType(kWasmNullExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + + builder.addFunction('castFailNullToExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRef, kExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCastFail(0, + wasmRefNullType(kWasmExnRef), wasmRefNullType(kWasmExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + builder.addFunction('castFailNullToNullExnRef', + makeSig([kWasmI32], [kWasmI32])) + .addBody([ + kExprBlock, kWasmRef, kExnRefCode, + kExprLocalGet, 0, + kExprCallFunction, get_exnref_if, + ...wasmBrOnCastFail(0, + wasmRefNullType(kWasmExnRef), wasmRefNullType(kWasmNullExnRef)), + kExprI32Const, 0, + kExprReturn, + kExprEnd, + kExprDrop, + kExprI32Const, 1, + kExprReturn, + ]) + .exportFunc(); + + let instance = builder.instantiate({m: {get_exnref: helper.exports.get_exnref}}); + let wasm = instance.exports; + let exnRef = getExnRef(); + + assertEquals(0, wasm.castToExnRef(0)); + assertEquals(1, wasm.castToExnRef(1)); + + assertEquals(0, wasm.castToNullExnRef(0)); + assertEquals(0, wasm.castToNullExnRef(1)); + + assertEquals(1, wasm.castNullToExnRef(0)); + assertEquals(1, wasm.castNullToExnRef(1)); + + assertEquals(1, wasm.castNullToNullExnRef(0)); + assertEquals(0, wasm.castNullToNullExnRef(1)); + + assertEquals(1, wasm.castFailToExnRef(0)); + assertEquals(0, wasm.castFailToExnRef(1)); + + assertEquals(1, wasm.castFailToNullExnRef(0)); + assertEquals(1, wasm.castFailToNullExnRef(1)); + + assertEquals(0, wasm.castFailNullToExnRef(0)); + assertEquals(0, wasm.castFailNullToExnRef(1)); + + assertEquals(0, wasm.castFailNullToNullExnRef(0)); + assertEquals(1, wasm.castFailNullToNullExnRef(1)); +})(); diff --git a/deps/v8/test/mjsunit/wasm/gc-casts-invalid.js b/deps/v8/test/mjsunit/wasm/gc-casts-invalid.js index 3d2f25905e229d..dcb12615e9a14b 100644 --- a/deps/v8/test/mjsunit/wasm/gc-casts-invalid.js +++ b/deps/v8/test/mjsunit/wasm/gc-casts-invalid.js @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// Flags: --experimental-wasm-exnref + d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); (function TestRefTestInvalid() { @@ -20,6 +22,11 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); [wasmRefType(sig), kExternRefCode], [kWasmAnyRef, kExternRefCode], [kWasmAnyRef, kFuncRefCode], + [kWasmAnyRef, kExnRefCode], + [wasmRefType(sig), kExnRefCode], + [kWasmNullExternRef, kExnRefCode], + [wasmRefType(array), kNullExnRefCode], + [kWasmNullFuncRef, kNullExnRefCode], ]; let casts = [ kExprRefTest, diff --git a/deps/v8/test/mjsunit/wasm/js-wrapper-typechecks.js b/deps/v8/test/mjsunit/wasm/js-wrapper-typechecks.js index 3a5c626732eddd..04a9581547dee0 100644 --- a/deps/v8/test/mjsunit/wasm/js-wrapper-typechecks.js +++ b/deps/v8/test/mjsunit/wasm/js-wrapper-typechecks.js @@ -2,8 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); +// Flags: --experimental-wasm-exnref +d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); (function testNonNullRefWrapperNullCheck() { print(arguments.callee.name); @@ -12,6 +13,7 @@ d8.file.execute('test/mjsunit/wasm/wasm-module-builder.js'); 'any': kWasmAnyRef, 'func': kWasmFuncRef, 'array': kWasmArrayRef, + 'exn': kWasmExnRef, })) { print(`- ${name}`); let builder = new WasmModuleBuilder(); diff --git a/deps/v8/test/mjsunit/wasm/wasm-module-builder.js b/deps/v8/test/mjsunit/wasm/wasm-module-builder.js index cdd67a31d33852..271b6a583ebff0 100644 --- a/deps/v8/test/mjsunit/wasm/wasm-module-builder.js +++ b/deps/v8/test/mjsunit/wasm/wasm-module-builder.js @@ -133,6 +133,7 @@ let kWasmI31Ref = -0x14; let kWasmStructRef = -0x15; let kWasmArrayRef = -0x16; let kWasmExnRef = -0x17; +let kWasmNullExnRef = -0x0c; let kWasmStringRef = -0x19; let kWasmStringViewWtf8 = -0x1a; let kWasmStringViewWtf16 = -0x1e; @@ -151,6 +152,7 @@ let kNullFuncRefCode = kWasmNullFuncRef & kLeb128Mask; let kStructRefCode = kWasmStructRef & kLeb128Mask; let kArrayRefCode = kWasmArrayRef & kLeb128Mask; let kExnRefCode = kWasmExnRef & kLeb128Mask; +let kNullExnRefCode = kWasmNullExnRef & kLeb128Mask; let kNullRefCode = kWasmNullRef & kLeb128Mask; let kStringRefCode = kWasmStringRef & kLeb128Mask; let kStringViewWtf8Code = kWasmStringViewWtf8 & kLeb128Mask; diff --git a/deps/v8/test/unittests/wasm/function-body-decoder-unittest.cc b/deps/v8/test/unittests/wasm/function-body-decoder-unittest.cc index 873b855bb4d0bc..2e7979a4e9f662 100644 --- a/deps/v8/test/unittests/wasm/function-body-decoder-unittest.cc +++ b/deps/v8/test/unittests/wasm/function-body-decoder-unittest.cc @@ -2994,7 +2994,7 @@ TEST_F(FunctionBodyDecoderTest, TryTable) { U32V_1(1), CatchKind::kCatchAllRef, U32V_1(0), kExprEnd, kExprUnreachable, kExprEnd, kExprDrop}, kAppendEnd); - // // Duplicate catch-all. + // Duplicate catch-all. ExpectValidates( sigs.v_v(), {kExprBlock, kExnRefCode, WASM_TRY_TABLE_OP, U32V_1(4), @@ -3002,12 +3002,21 @@ TEST_F(FunctionBodyDecoderTest, TryTable) { CatchKind::kCatchAllRef, U32V_1(0), CatchKind::kCatchAllRef, U32V_1(0), kExprEnd, kExprUnreachable, kExprEnd, kExprDrop}, kAppendEnd); - // // Catch-all before catch. + // Catch-all before catch. ExpectValidates( sigs.v_v(), {WASM_TRY_TABLE_OP, U32V_1(2), CatchKind::kCatchAll, U32V_1(0), CatchKind::kCatch, ex, U32V_1(0), kExprEnd, kExprUnreachable}, kAppendEnd); + // Non-nullable exnref. + ValueType kNonNullableExnRef = ValueType::Ref(HeapType::kExn); + auto sig = FixedSizeSignature::Returns(kNonNullableExnRef); + uint8_t sig_id = builder.AddSignature(&sig); + ExpectValidates( + sigs.v_v(), + {kExprBlock, sig_id, WASM_TRY_TABLE_OP, U32V_1(1), CatchKind::kCatchRef, + ex, U32V_1(0), kExprEnd, kExprUnreachable, kExprEnd, kExprDrop}, + kAppendEnd); constexpr uint8_t kInvalidCatchKind = kLastCatchKind + 1; ExpectFailure(sigs.v_v(), diff --git a/deps/v8/test/wasm-spec-tests/wasm-spec-tests.status b/deps/v8/test/wasm-spec-tests/wasm-spec-tests.status index 84faab6193c8ac..717ba82a536a8a 100644 --- a/deps/v8/test/wasm-spec-tests/wasm-spec-tests.status +++ b/deps/v8/test/wasm-spec-tests/wasm-spec-tests.status @@ -85,6 +85,10 @@ 'proposals/tail-call/binary': [FAIL], 'proposals/exception-handling/binary': [FAIL], 'proposals/exception-handling/imports': [FAIL], + + # The test is incorrect as it exports a function with an exnref in the + # signature, which is a runtime type error. + 'proposals/exception-handling/ref_null': [FAIL] }], # ALWAYS ['arch == arm and not simulator_run', {