Commit 168229ffddf for nodejs
commit 168229ffddf291edca6488f5e6dcfb369dac919e
Author: Node.js GitHub Bot <github-bot@iojs.org>
Date: Mon Nov 2 19:52:58 2026 -0500
deps: patch V8 to 15.6.75.33
Refs: https://github.com/v8/v8/compare/15.6.75.25...15.6.75.33
PR-URL: https://github.com/nodejs/node/pull/67174
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
diff --git a/deps/v8/include/cppgc/heap-consistency.h b/deps/v8/include/cppgc/heap-consistency.h
index 23b5d909945..31858f0a631 100644
--- a/deps/v8/include/cppgc/heap-consistency.h
+++ b/deps/v8/include/cppgc/heap-consistency.h
@@ -146,8 +146,8 @@ class HeapConsistency final {
}
/**
- * Steele-style write barrier that re-processes an object if it has already
- * been processed.
+ * Steele-style write barrier that processes an object regardless of whether
+ * or not it has already been processed.
*
* \param params The parameters retrieved from `GetWriteBarrierType()`.
* \param object The pointer to the object which must point to an object that
diff --git a/deps/v8/include/v8-version.h b/deps/v8/include/v8-version.h
index f91b6e7018f..11e0def9233 100644
--- a/deps/v8/include/v8-version.h
+++ b/deps/v8/include/v8-version.h
@@ -11,7 +11,7 @@
#define V8_MAJOR_VERSION 15
#define V8_MINOR_VERSION 6
#define V8_BUILD_NUMBER 75
-#define V8_PATCH_LEVEL 25
+#define V8_PATCH_LEVEL 33
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/deps/v8/src/builtins/promise-any.tq b/deps/v8/src/builtins/promise-any.tq
index 427d31c2840..6f3d91f1995 100644
--- a/deps/v8/src/builtins/promise-any.tq
+++ b/deps/v8/src/builtins/promise-any.tq
@@ -26,9 +26,7 @@ extern operator '[]' macro LoadContextElementNoCell(
// together with the errors array. Since all closures for a single Promise.any
// call use the same context, we need to store the indices for the individual
// closures somewhere else (we put them into the identity hash field of the
-// closures), and we also need to have a separate marker for when the closure
-// was called already (we slap the native context onto the closure in that
-// case to mark it's done). See Promise.all which uses the same approach.
+// closures).
transitioning macro CreatePromiseAnyRejectElementContext(
implicit context: Context)(capability: PromiseCapability,
nativeContext: NativeContext): PromiseAnyRejectElementContext {
@@ -75,36 +73,30 @@ transitioning javascript builtin PromiseAnyRejectElementClosure(
js-implicit context: Context, receiver: JSAny, target: JSFunction)(
value: JSAny): JSAny {
// 1. Let F be the active function object.
-
- // 2. Let alreadyCalled be F.[[AlreadyCalled]].
-
- // 3. If alreadyCalled.[[Value]] is true, return undefined.
-
- // We use the function's context as the marker to remember whether this
- // reject element closure was already called. It points to the reject
- // element context (which is a FunctionContext) until it was called the
- // first time, in which case we make it point to the native context here
- // to mark this reject element closure as done.
- if (IsNativeContext(context)) deferred {
- return Undefined;
- }
-
dcheck(
context.length ==
SmiTag(
PromiseAnyRejectElementContextSlots::kPromiseAnyRejectElementLength));
const context = %RawDownCast<PromiseAnyRejectElementContext>(context);
- // 4. Set alreadyCalled.[[Value]] to true.
- const nativeContext = LoadNativeContext(context);
- target.context = nativeContext;
-
// 5. Let index be F.[[Index]].
dcheck(kPropertyArrayNoHashSentinel == 0);
const identityHash = LoadJSReceiverIdentityHash(target) otherwise unreachable;
dcheck(ChangeUint32ToWord(identityHash) < kSmiMaxValue);
const index = Signed(ChangeUint32ToWord(identityHash)) - 1;
+ // 8. Let remainingElementsCount be F.[[RemainingElements]].
+ let remainingElementsCount = *ContextSlot(
+ context,
+ PromiseAnyRejectElementContextSlots::
+ kPromiseAnyRejectElementRemainingSlot);
+
+ // If all promises were already rejected, the remaining count will already be
+ // 0.
+ if (remainingElementsCount == 0) deferred {
+ return Undefined;
+ }
+
// 6. Let errors be F.[[Errors]].
let errorsRef:&FixedArray = ContextSlot(
context,
@@ -113,12 +105,6 @@ transitioning javascript builtin PromiseAnyRejectElementClosure(
// 7. Let promiseCapability be F.[[Capability]].
- // 8. Let remainingElementsCount be F.[[RemainingElements]].
- let remainingElementsCount = *ContextSlot(
- context,
- PromiseAnyRejectElementContextSlots::
- kPromiseAnyRejectElementRemainingSlot);
-
// 9. Set errors[index] to x.
// The max computation below is an optimization to avoid excessive allocations
@@ -138,11 +124,22 @@ transitioning javascript builtin PromiseAnyRejectElementClosure(
errors, 0, errors.length_intptr, newCapacity, PromiseHole);
*errorsRef = errors;
}
+
+ // 2. Let alreadyCalled be F.[[AlreadyCalled]].
+ // 3. If alreadyCalled.[[Value]] is true, return undefined.
+ // 4. Set alreadyCalled.[[Value]] to true.
+ if (errors.objects[index] != PromiseHole) deferred {
+ return Undefined;
+ }
+
+ // Make sure that we never see the PromiseHole here as a result.
+ check(value != PromiseHole);
errors.objects[index] = value;
// 10. Set remainingElementsCount.[[Value]] to
// remainingElementsCount.[[Value]] - 1.
remainingElementsCount = remainingElementsCount - 1;
+ check(remainingElementsCount >= 0);
*ContextSlot(
context,
PromiseAnyRejectElementContextSlots::
diff --git a/deps/v8/src/compiler/js-generic-lowering.cc b/deps/v8/src/compiler/js-generic-lowering.cc
index 2affa53f2fb..d32bb061c04 100644
--- a/deps/v8/src/compiler/js-generic-lowering.cc
+++ b/deps/v8/src/compiler/js-generic-lowering.cc
@@ -759,9 +759,6 @@ void JSGenericLowering::LowerJSGetTemplateObject(Node* node) {
SharedFunctionInfoRef shared = p.shared();
TemplateObjectDescriptionRef description = p.description();
- DCHECK_EQ(node->op()->ControlInputCount(), 1);
- node->RemoveInput(NodeProperties::FirstControlIndex(node));
-
static_assert(JSGetTemplateObjectNode::FeedbackVectorIndex() == 0);
node->InsertInput(zone(), 0, jsgraph()->ConstantNoHole(shared, broker()));
node->InsertInput(zone(), 1,
diff --git a/deps/v8/src/compiler/js-operator.cc b/deps/v8/src/compiler/js-operator.cc
index 5aec87c5ad3..75810fa1a73 100644
--- a/deps/v8/src/compiler/js-operator.cc
+++ b/deps/v8/src/compiler/js-operator.cc
@@ -1614,7 +1614,7 @@ const Operator* JSOperatorBuilder::GetTemplateObject(
GetTemplateObjectParameters parameters(description, shared, feedback);
return zone()->New<Operator1<GetTemplateObjectParameters>>( // --
IrOpcode::kJSGetTemplateObject, // opcode
- Operator::kEliminatable, // properties
+ Operator::kNoDeopt | Operator::kNoThrow, // properties
"JSGetTemplateObject", // name
1, 1, 1, 1, 1, 0, // counts
parameters); // parameter
diff --git a/deps/v8/src/compiler/turboshaft/pipelines.cc b/deps/v8/src/compiler/turboshaft/pipelines.cc
index 3c773aeac12..4aff92cb2a8 100644
--- a/deps/v8/src/compiler/turboshaft/pipelines.cc
+++ b/deps/v8/src/compiler/turboshaft/pipelines.cc
@@ -85,6 +85,21 @@ bool Pipeline::AllocateRegisters(const RegisterConfiguration* config,
RUN_MAYBE_ABORT(DecideSpillingModePhase);
RUN_MAYBE_ABORT(AssignSpillSlotsPhase);
+
+ // The prologue grows the frame with a single stack pointer decrement, before
+ // the function entry stack check runs. A frame no larger than the smallest OS
+ // page cannot step over a guard page, so bail out rather than emit an
+ // unchecked stack pointer jump of arbitrary size.
+ // When replacing this bailout with a different strategy, consider doing the
+ // same in Maglev as well.
+ constexpr int kMaxStackSlots = 4 * KB / kSystemPointerSize;
+ if (data_->info()->IsOptimizing() &&
+ data_->frame()->GetTotalFrameSlotCount() > kMaxStackSlots) {
+ data_->info()->AbortOptimization(BailoutReason::kFunctionTooBig);
+ data()->ClearRegisterComponent();
+ return false;
+ }
+
RUN_MAYBE_ABORT(CommitAssignmentPhase);
// TODO(chromium:725559): remove this check once
diff --git a/deps/v8/src/heap/cppgc-internal/marker.h b/deps/v8/src/heap/cppgc-internal/marker.h
index 84fec071262..ad96e22f188 100644
--- a/deps/v8/src/heap/cppgc-internal/marker.h
+++ b/deps/v8/src/heap/cppgc-internal/marker.h
@@ -10,6 +10,7 @@
#include "include/cppgc/heap.h"
#include "include/cppgc/platform.h"
#include "include/cppgc/visitor.h"
+#include "src/base/logging.h"
#include "src/base/macros.h"
#include "src/base/platform/time.h"
#include "src/heap/base/incremental-marking-schedule.h"
@@ -238,20 +239,15 @@ class V8_EXPORT_PRIVATE Marker final : public MarkerBase {
template <MarkerBase::WriteBarrierType type>
void MarkerBase::WriteBarrierForObject(HeapObjectHeader& header) {
- // The barrier optimizes for the bailout cases:
- // - kDijkstra: Marked objects.
- // - kSteele: Unmarked objects.
- switch (type) {
- case MarkerBase::WriteBarrierType::kDijkstra:
- if (!header.TryMarkAtomic()) {
- return;
- }
- break;
- case MarkerBase::WriteBarrierType::kSteele:
- if (!header.IsMarked<AccessMode::kAtomic>()) {
- return;
- }
- break;
+ // The Dijkstra barrier bails out on marked objects.
+ // The Steele barrier marks the object regardless of its current state.
+ // It's important to make sure the object is marked here since unmarking
+ // below for in construction objects expects a marked object.
+ const bool was_already_marked = !header.TryMarkAtomic();
+ if (type == MarkerBase::WriteBarrierType::kDijkstra) {
+ if (was_already_marked) {
+ return;
+ }
}
// The barrier fired. Filter out in-construction objects here. This possibly
@@ -267,13 +263,18 @@ void MarkerBase::WriteBarrierForObject(HeapObjectHeader& header) {
return;
}
- switch (type) {
- case MarkerBase::WriteBarrierType::kDijkstra:
- mutator_marking_state_.write_barrier_worklist().Push(&header);
- break;
- case MarkerBase::WriteBarrierType::kSteele:
- mutator_marking_state_.retrace_marked_objects_worklist().Push(&header);
- break;
+ DCHECK_IMPLIES(was_already_marked,
+ type == MarkerBase::WriteBarrierType::kSteele);
+ if ((type == MarkerBase::WriteBarrierType::kDijkstra) ||
+ !was_already_marked) {
+ // Push previously unmarked objects to the write barrier worklist, even for
+ // the Steele barrier, since popping from that worklist would also update
+ // the marked bytes counter.
+ mutator_marking_state_.write_barrier_worklist().Push(&header);
+ } else {
+ DCHECK((type == MarkerBase::WriteBarrierType::kSteele) &&
+ was_already_marked);
+ mutator_marking_state_.retrace_marked_objects_worklist().Push(&header);
}
}
diff --git a/deps/v8/src/maglev/maglev-compiler.cc b/deps/v8/src/maglev/maglev-compiler.cc
index 06dd7c5e301..74236225fc0 100644
--- a/deps/v8/src/maglev/maglev-compiler.cc
+++ b/deps/v8/src/maglev/maglev-compiler.cc
@@ -285,6 +285,8 @@ bool MaglevCompiler::Compile(LocalIsolate* local_isolate,
// FunctionEntryStackCheck runs. A frame no larger than the smallest OS page
// cannot step over a guard page, so bail out rather than emit an unchecked
// stack pointer jump of arbitrary size.
+ // When replacing this bailout with a different strategy, consider doing the
+ // same in Turboshaft as well.
if (graph->tagged_stack_slots() + graph->untagged_stack_slots() >
kMaxStackSlots) {
return false;
diff --git a/deps/v8/src/maglev/maglev-graph-builder.cc b/deps/v8/src/maglev/maglev-graph-builder.cc
index ca199bab0fa..d9ed8b2c9fb 100644
--- a/deps/v8/src/maglev/maglev-graph-builder.cc
+++ b/deps/v8/src/maglev/maglev-graph-builder.cc
@@ -6368,6 +6368,7 @@ void MaglevGraphBuilder::AdvanceThroughContinuationForPolymorphicPropertyLoad(
while (iterator_.current_offset() < continuation.last_continuation) {
iterator_.Advance();
UpdateSourceAndBytecodePosition(iterator_.current_offset());
+ PrewalkBytecode();
}
}
diff --git a/deps/v8/src/objects/js-function.cc b/deps/v8/src/objects/js-function.cc
index 585d97d436c..310ed57d8ad 100644
--- a/deps/v8/src/objects/js-function.cc
+++ b/deps/v8/src/objects/js-function.cc
@@ -991,8 +991,9 @@ void JSFunction::EnsureHasInitialMap(Isolate* isolate,
// system.
// TODO(leszeks): Figure out if this CHECK is needed.
CHECK(IsJSReceiver(*prototype));
- JSFunction::SetInitialMap(isolate, function, map, prototype);
+ // Initialize slack tracking before publishing the map to concurrent readers.
map->StartInobjectSlackTracking();
+ JSFunction::SetInitialMap(isolate, function, map, prototype);
}
namespace {
@@ -1183,11 +1184,12 @@ bool FastInitializeDerivedMap(Isolate* isolate,
Map::CopyInitialMap(isolate, constructor_initial_map, instance_size,
in_object_properties, unused_property_fields);
map->set_new_target_is_base(false);
+ // Initialize slack tracking before publishing the map to concurrent readers.
+ map->set_construction_counter(Map::kNoSlackTracking);
+ map->StartInobjectSlackTracking();
DirectHandle<JSReceiver> prototype(new_target->instance_prototype(), isolate);
JSFunction::SetInitialMap(isolate, new_target, map, prototype, constructor);
DCHECK(IsJSReceiver(new_target->instance_prototype()));
- map->set_construction_counter(Map::kNoSlackTracking);
- map->StartInobjectSlackTracking();
return true;
}
diff --git a/deps/v8/src/objects/map-inl.h b/deps/v8/src/objects/map-inl.h
index 1cbddb0d987..63cfe9edd10 100644
--- a/deps/v8/src/objects/map-inl.h
+++ b/deps/v8/src/objects/map-inl.h
@@ -1258,9 +1258,12 @@ void Map::InobjectSlackTrackingStep(Isolate* isolate) {
DCHECK(IsUndefined(GetBackPointer()));
if (!this->IsInobjectSlackTrackingInProgress()) return;
int counter = construction_counter();
- set_construction_counter(counter - 1);
if (counter == kSlackTrackingCounterEnd) {
+ // The last decrement must happen under the map_updater_access mutex,
+ // together with the instance size changes.
MapUpdater::CompleteInobjectSlackTracking(isolate, this);
+ } else {
+ set_construction_counter(counter - 1);
}
}
diff --git a/deps/v8/src/objects/map-updater.cc b/deps/v8/src/objects/map-updater.cc
index f1437356d69..84999a3a34d 100644
--- a/deps/v8/src/objects/map-updater.cc
+++ b/deps/v8/src/objects/map-updater.cc
@@ -400,7 +400,9 @@ std::optional<Tagged<Map>> MapUpdater::TryUpdateNoLock(Isolate* isolate,
ElementsKind to_kind = old_map->elements_kind();
IntegrityLevelTransitionInfo info(old_map);
- if (root_map->is_extensible() != old_map->is_extensible()) {
+ if (!root_map->is_extensible() && old_map != root_map) {
+ return {};
+ } else if (root_map->is_extensible() != old_map->is_extensible()) {
DCHECK(!old_map->is_extensible());
DCHECK(root_map->is_extensible());
info = DetectIntegrityLevelTransitions(old_map, isolate, &no_gc, cmode);
@@ -412,7 +414,7 @@ std::optional<Tagged<Map>> MapUpdater::TryUpdateNoLock(Isolate* isolate,
DCHECK(to_kind == DICTIONARY_ELEMENTS ||
to_kind == SLOW_STRING_WRAPPER_ELEMENTS ||
IsTypedArrayOrRabGsabTypedArrayElementsKind(to_kind) ||
- IsAnyHoleyNonextensibleElementsKind(to_kind));
+ IsAnyNonextensibleElementsKind(to_kind));
to_kind = info.integrity_level_source_map->elements_kind();
}
if (from_kind != to_kind) {
@@ -622,6 +624,8 @@ MapUpdater::State MapUpdater::FindRootMap() {
direct_handle(root_map_->prototype(), isolate_),
root_map_->instance_type())) {
return Normalize("Normalize_NotEquivalent");
+ } else if (!root_map_->is_extensible() && *old_map_ != *root_map_) {
+ return Normalize("Normalize_PrivateSymbolsOnNonExtensible");
} else if (old_map_->is_extensible() != root_map_->is_extensible()) {
DCHECK(!old_map_->is_extensible());
DCHECK(root_map_->is_extensible());
diff --git a/deps/v8/src/objects/map.cc b/deps/v8/src/objects/map.cc
index a6f6a26a945..cdc41455019 100644
--- a/deps/v8/src/objects/map.cc
+++ b/deps/v8/src/objects/map.cc
@@ -603,10 +603,7 @@ MaybeHandle<Map> Map::CopyWithField(Isolate* isolate, DirectHandle<Map> map,
MaybeObjectDirectHandle wrapped_type = WrapFieldType(type);
Descriptor d = Descriptor::DataField(name, offset, attributes, constness,
representation, wrapped_type);
- Handle<Map> new_map = Map::CopyAddDescriptor(isolate, map, &d, flag);
-
- new_map->AccountAddedPropertyField();
- return new_map;
+ return Map::CopyAddDescriptor(isolate, map, &d, flag);
}
MaybeHandle<Map> Map::CopyWithConstant(Isolate* isolate, DirectHandle<Map> map,
@@ -1615,6 +1612,10 @@ Handle<Map> Map::ShareDescriptor(Isolate* isolate, DirectHandle<Map> map,
DisallowGarbageCollection no_gc;
descriptors->Append(descriptor);
result->InitializeDescriptors(*descriptors);
+ // Account for the field before publishing the transition.
+ if (descriptor->GetDetails().location() == PropertyLocation::kField) {
+ result->AccountAddedPropertyField();
+ }
}
DCHECK(result->NumberOfOwnDescriptors() == map->NumberOfOwnDescriptors() + 1);
@@ -2333,7 +2334,12 @@ Handle<Map> Map::CopyAddDescriptor(Isolate* isolate, DirectHandle<Map> map,
DescriptorArray::CopyUpTo(isolate, descriptors, nof, 1);
new_descriptors->Append(descriptor);
- return CopyReplaceDescriptors(isolate, map, new_descriptors, flag,
+ auto InitMap = [&](DirectHandle<Map> new_map) {
+ if (descriptor->GetDetails().location() == PropertyLocation::kField) {
+ new_map->AccountAddedPropertyField();
+ }
+ };
+ return CopyReplaceDescriptors(isolate, map, new_descriptors, flag, InitMap,
descriptor->GetKey(), "CopyAddDescriptor",
SIMPLE_PROPERTY_TRANSITION);
}
diff --git a/deps/v8/src/runtime/runtime-literals.cc b/deps/v8/src/runtime/runtime-literals.cc
index 312f5ddcf90..4a3cfca512d 100644
--- a/deps/v8/src/runtime/runtime-literals.cc
+++ b/deps/v8/src/runtime/runtime-literals.cc
@@ -856,6 +856,14 @@ static bool IsDefaultFunctionPrototype(DirectHandle<JSObject> js_proto,
// Object function prototype's map.
Tagged<Map> proto_map = js_proto->map();
+ // Check that the prototype is a plain JSObject with no elements.
+ if (proto_map->instance_type() != JS_OBJECT_TYPE) {
+ return false;
+ }
+ if (js_proto->elements() != ReadOnlyRoots(isolate).empty_fixed_array()) {
+ return false;
+ }
+
// Check that given function.prototype object has a default initial state:
// it's extensible.
if (!proto_map->is_extensible()) {
@@ -868,13 +876,24 @@ static bool IsDefaultFunctionPrototype(DirectHandle<JSObject> js_proto,
}
// it has exactly one "constructor" property installed.
- if (js_proto->property_dictionary()->NumberOfElements() != 1) {
- return false;
- }
- if (js_proto->property_dictionary()
- ->FindEntry(isolate, isolate->factory()->constructor_string())
- .is_not_found()) {
- return false;
+ if constexpr (V8_ENABLE_SWISS_NAME_DICTIONARY_BOOL) {
+ if (js_proto->property_dictionary_swiss()->NumberOfElements() != 1) {
+ return false;
+ }
+ if (js_proto->property_dictionary_swiss()
+ ->FindEntry(isolate, isolate->factory()->constructor_string())
+ .is_not_found()) {
+ return false;
+ }
+ } else {
+ if (js_proto->property_dictionary()->NumberOfElements() != 1) {
+ return false;
+ }
+ if (js_proto->property_dictionary()
+ ->FindEntry(isolate, isolate->factory()->constructor_string())
+ .is_not_found()) {
+ return false;
+ }
}
// its prototype is the original and unmodified Object.prototype object.
@@ -907,7 +926,7 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
}
DirectHandle<JSFunction> acc_fun = Cast<JSFunction>(obj);
- if (!acc_fun->has_prototype_slot()) {
+ if (!acc_fun->has_prototype_slot() || !acc_fun->has_prototype_property()) {
RETURN_RESULT_OR_FAILURE(
isolate, SetPrototypePropertiesSlow(isolate, context, obj,
object_boilerplate_description,
@@ -937,18 +956,21 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
feedback_cell_array, current_slot));
}
- if (IsSpecialReceiverMap(js_proto->map())) {
- RETURN_RESULT_OR_FAILURE(
- isolate, SetPrototypePropertiesSlow(isolate, context, obj,
- object_boilerplate_description,
- feedback_cell_array, current_slot));
- }
-
- if (!JSObject::IsExtensible(isolate, js_proto)) {
- RETURN_RESULT_OR_FAILURE(
- isolate, SetPrototypePropertiesSlow(isolate, context, obj,
- object_boilerplate_description,
- feedback_cell_array, current_slot));
+ // Use the slow path for exotic receivers, interceptors, or access checks.
+ {
+ Tagged<Map> js_proto_map = js_proto->map();
+ if (IsCustomElementsReceiverMap(js_proto_map) ||
+ IsJSTypedArrayMap(js_proto_map) ||
+ IsAlwaysSharedSpaceJSObjectMap(js_proto_map) ||
+ js_proto_map->is_access_check_needed() ||
+ js_proto_map->has_named_interceptor() ||
+ js_proto_map->has_indexed_interceptor() ||
+ !JSObject::IsExtensible(isolate, js_proto)) {
+ RETURN_RESULT_OR_FAILURE(
+ isolate, SetPrototypePropertiesSlow(
+ isolate, context, obj, object_boilerplate_description,
+ feedback_cell_array, current_slot));
+ }
}
bool is_default_func_prototype =
@@ -977,13 +999,14 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
DirectHandle<AccessorInfo> accessor_info =
isolate->factory()->lazy_closure_accessor();
- JSObject::SetAccessor(js_proto, name, accessor_info,
- PropertyAttributes::NONE)
- .Check();
+ RETURN_FAILURE_ON_EXCEPTION(
+ isolate, JSObject::SetAccessor(js_proto, name, accessor_info,
+ PropertyAttributes::NONE));
} else {
- Object::TransitionAndWriteDataProperty(
- &it, value, NONE, Just(kDontThrow), StoreOrigin::kNamed)
- .Check();
+ MAYBE_RETURN(
+ Object::TransitionAndWriteDataProperty(
+ &it, value, NONE, Just(kDontThrow), StoreOrigin::kNamed),
+ ReadOnlyRoots(isolate).exception());
}
result = value;
}
@@ -1029,7 +1052,8 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
current_slot++;
}
it.UpdateProtector();
- Object::SetDataProperty(&it, value).Check();
+ MAYBE_RETURN(Object::SetDataProperty(&it, value),
+ ReadOnlyRoots(isolate).exception());
} else {
value = InstantiateIfSharedFunctionInfo(
context, isolate, js_proto, value, feedback_cell_array,
@@ -1042,13 +1066,15 @@ RUNTIME_FUNCTION(Runtime_SetPrototypeProperties) {
// doesn't invalidate protectors, so do it here for e.g. `then` or
// `next` added to a watched prototype.
it.UpdateProtector();
- JSObject::SetAccessor(js_proto, Cast<Name>(key), accessor_info,
- PropertyAttributes::NONE)
- .Check();
+ RETURN_FAILURE_ON_EXCEPTION(
+ isolate,
+ JSObject::SetAccessor(js_proto, Cast<Name>(key), accessor_info,
+ PropertyAttributes::NONE));
} else {
- Object::TransitionAndWriteDataProperty(
- &it, value, NONE, Just(kDontThrow), StoreOrigin::kNamed)
- .Check();
+ MAYBE_RETURN(
+ Object::TransitionAndWriteDataProperty(
+ &it, value, NONE, Just(kDontThrow), StoreOrigin::kNamed),
+ ReadOnlyRoots(isolate).exception());
}
}
result = value;
diff --git a/deps/v8/test/mjsunit/compiler/regress-574339635.js b/deps/v8/test/mjsunit/compiler/regress-574339635.js
new file mode 100644
index 00000000000..f131352bccf
--- /dev/null
+++ b/deps/v8/test/mjsunit/compiler/regress-574339635.js
@@ -0,0 +1,37 @@
+// Copyright 2026 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: --turbofan --allow-natives-syntax
+
+const P = Array.prototype;
+const Obj = Object;
+let s;
+
+function foo(o, t) {
+ o[0];
+ let r = 0;
+ if (t) r = Obj`hello`;
+ const v = o.x;
+ s = r;
+ return v;
+}
+
+new WeakMap().set(P, 1);
+for (const _ in []) {}
+P[0] = 1;
+P.x = 1;
+Object.defineProperty(P, "x", {value: 1.5, enumerable: false});
+
+%PrepareFunctionForOptimization(foo);
+foo(P, false); foo(P, false); foo(P, false);
+%OptimizeFunctionOnNextCall(foo);
+assertEquals(1.5, foo(P, false));
+
+for (let i = 0; i < 400; i += 3) {
+ P["p" + i] = "x";
+ P["p" + (i + 1)] = "corrupted";
+ P["p" + (i + 2)] = 0;
+}
+
+assertEquals(1.5, foo(P, true));
diff --git a/deps/v8/test/mjsunit/compiler/regress-578688670.js b/deps/v8/test/mjsunit/compiler/regress-578688670.js
new file mode 100644
index 00000000000..b16f0752f28
--- /dev/null
+++ b/deps/v8/test/mjsunit/compiler/regress-578688670.js
@@ -0,0 +1,56 @@
+// Copyright 2026 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
+
+let rejected_error;
+function CustomPromise(executor) {
+ executor(() => {}, (err) => { rejected_error = err; });
+}
+
+let captured_reject_closure;
+CustomPromise.resolve = function(thenable) {
+ return thenable;
+};
+
+const pending_thenables = [
+ { then(resolve, reject) { captured_reject_closure = reject; } },
+ { then() {} },
+ { then() {} },
+];
+
+Promise.any.call(CustomPromise, pending_thenables);
+
+// Store the reject element closure on a constant holder so that inlining
+// invoke_reject_closure into call_reject_closure constant-folds the closure
+// target and its initial FunctionContext.
+const closure_holder = { reject: captured_reject_closure };
+
+function invoke_reject_closure(closure, should_call) {
+ if (should_call) closure('err');
+}
+
+function call_reject_closure(should_call) {
+ invoke_reject_closure(closure_holder.reject, should_call);
+}
+
+// Make the call site inside invoke_reject_closure polymorphic before
+// calling captured_reject_closure so Maglev/TurboFan compile the call without
+// invoking captured_reject_closure during warmup.
+%PrepareFunctionForOptimization(invoke_reject_closure);
+invoke_reject_closure(() => {}, true);
+invoke_reject_closure((x) => x, true);
+
+%PrepareFunctionForOptimization(call_reject_closure);
+call_reject_closure(false);
+%OptimizeFunctionOnNextCall(call_reject_closure);
+call_reject_closure(false);
+
+// Repeated calls to the same reject element closure must be ignored after the
+// first call and must not decrement remainingElementsCount to 0.
+call_reject_closure(true);
+call_reject_closure(true);
+call_reject_closure(true);
+
+assertEquals(undefined, rejected_error);
diff --git a/deps/v8/test/mjsunit/maglev/regress-574021176.js b/deps/v8/test/mjsunit/maglev/regress-574021176.js
new file mode 100644
index 00000000000..780b5b1c8cf
--- /dev/null
+++ b/deps/v8/test/mjsunit/maglev/regress-574021176.js
@@ -0,0 +1,44 @@
+// Copyright 2026 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
+
+let g = () => class {};
+
+function* gen(o) {
+ // Context-allocated, maybe-assigned variable so the function context uses a
+ // ContextCell for `x`.
+ let x = 42;
+ () => x = 0;
+
+ // Polymorphic property load with a call continuation. The class literal
+ // pushes a `CLASS_SCOPE` context (needed for `super.x`) before evaluating
+ // `extends g()` and pops it after defining the class. When `g()` aborts in
+ // every polymorphic arm, skipping the rest of the continuation must still pop
+ // that `CLASS_SCOPE`.
+ o.f(class extends g() {
+ constructor() {
+ super.x;
+ }
+ });
+
+ // In dead code after all polymorphic arms abort, `SuspendGenerator` records
+ // `current_scope_info_` on the `ResumeGenerator` merge state.
+ yield;
+
+ // Loads `x` using the `ScopeInfo` from the resume merge state (which must be
+ // the generator's `FUNCTION_SCOPE` with cells, not the leaked `CLASS_SCOPE`).
+ return x;
+}
+
+const suspended = gen({f() {}});
+suspended.next();
+
+g = class {};
+%PrepareFunctionForOptimization(gen);
+assertThrows(() => gen({f() {}}).next(), TypeError);
+assertThrows(() => gen({a: 1, f() {}}).next(), TypeError);
+
+%OptimizeFunctionOnNextCall(gen);
+assertEquals(42, suspended.next().value);
diff --git a/deps/v8/test/mjsunit/opt-proto-seq/regress-577944790.js b/deps/v8/test/mjsunit/opt-proto-seq/regress-577944790.js
new file mode 100644
index 00000000000..e01a1280d53
--- /dev/null
+++ b/deps/v8/test/mjsunit/opt-proto-seq/regress-577944790.js
@@ -0,0 +1,65 @@
+// Copyright 2026 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: --mock-arraybuffer-allocator
+
+// Test exception propagation when a TypedArray prototype rejects a BigInt store.
+(function testLargeTypedArrayStoreException() {
+ let ta;
+ try {
+ ta = new Int8Array(4294967296);
+ } catch (e) {
+ assertInstanceof(e, RangeError);
+ return;
+ }
+
+ assertThrows(() => {
+ function F() {}
+ F.prototype = ta;
+ F.prototype['4294967295'] = 1n;
+ F.prototype.named = 1;
+ }, TypeError);
+
+ assertEquals(undefined, ta.named);
+})();
+
+// Test exception propagation when valueOf throws during a TypedArray store.
+(function testLargeTypedArrayValueOfException() {
+ let ta;
+ try {
+ ta = new Uint8Array(4294967296);
+ } catch (e) {
+ assertInstanceof(e, RangeError);
+ return;
+ }
+
+ const badValue = {
+ valueOf() {
+ throw new URIError();
+ },
+ };
+
+ assertThrows(() => {
+ function F() {}
+ F.prototype = ta;
+ F.prototype['4294967295'] = badValue;
+ F.prototype.named = 2;
+ }, URIError);
+
+ assertEquals(undefined, ta.named);
+})();
+
+// Test exception propagation on a small BigInt64Array prototype.
+(function testBigIntTypedArrayStoreException() {
+ const ta = new BigInt64Array(4);
+
+ assertThrows(() => {
+ function F() {}
+ F.prototype = ta;
+ F.prototype['4294967295'] = 1;
+ F.prototype.named = 3;
+ }, TypeError);
+
+ assertEquals(undefined, ta.named);
+})();
diff --git a/deps/v8/test/mjsunit/opt-proto-seq/regress-578083257.js b/deps/v8/test/mjsunit/opt-proto-seq/regress-578083257.js
new file mode 100644
index 00000000000..30e50a33d98
--- /dev/null
+++ b/deps/v8/test/mjsunit/opt-proto-seq/regress-578083257.js
@@ -0,0 +1,63 @@
+// Copyright 2026 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.
+
+// Create a dictionary-mode TypedArray that mimics a default function prototype.
+function makeDictionaryTypedArray() {
+ const ta = new Uint8Array(4);
+ ta.temp = 1;
+ delete ta.temp;
+ ta.constructor = Object;
+ Object.setPrototypeOf(ta, Object.prototype);
+ return ta;
+}
+
+// Test integer indices above kMaxElementIndex on a TypedArray prototype.
+(function testTypedArrayLargeIntegerIndices() {
+ const ta = makeDictionaryTypedArray();
+ function F() {}
+ F.prototype = ta;
+ F.prototype['4294967295'] = 1;
+ F.prototype['4294967296'] = 2;
+ F.prototype.named = 3;
+
+ assertEquals(undefined, Object.getOwnPropertyDescriptor(ta, '4294967295'));
+ assertEquals(undefined, Object.getOwnPropertyDescriptor(ta, '4294967296'));
+ assertEquals(undefined, ta['4294967295']);
+ assertEquals(undefined, ta['4294967296']);
+ assertEquals(3, ta.named);
+})();
+
+// Test canonical numeric index strings on a TypedArray prototype.
+(function testTypedArraySpecialNumericIndices() {
+ const ta = makeDictionaryTypedArray();
+ function F() {}
+ F.prototype = ta;
+ F.prototype['1.5'] = 1;
+ F.prototype['-0'] = 2;
+ F.prototype.named = 3;
+
+ assertEquals(undefined, Object.getOwnPropertyDescriptor(ta, '1.5'));
+ assertEquals(undefined, Object.getOwnPropertyDescriptor(ta, '-0'));
+ assertEquals(undefined, ta['1.5']);
+ assertEquals(undefined, ta['-0']);
+ assertEquals(3, ta.named);
+})();
+
+// Test a dictionary-mode String wrapper as a function prototype.
+(function testStringWrapperPrototype() {
+ const wrapper = new String('abc');
+ wrapper.temp = 1;
+ delete wrapper.temp;
+ wrapper.constructor = Object;
+ Object.setPrototypeOf(wrapper, Object.prototype);
+
+ function F() {}
+ F.prototype = wrapper;
+ F.prototype.first = 10;
+ F.prototype.second = 20;
+
+ assertEquals(10, wrapper.first);
+ assertEquals(20, wrapper.second);
+ assertEquals('a', wrapper[0]);
+})();
diff --git a/deps/v8/test/mjsunit/regress/regress-577318603.js b/deps/v8/test/mjsunit/regress/regress-577318603.js
new file mode 100644
index 00000000000..e89da019658
--- /dev/null
+++ b/deps/v8/test/mjsunit/regress/regress-577318603.js
@@ -0,0 +1,74 @@
+// Copyright 2026 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: --no-js-nonextensible-applies-to-private
+
+class Base {
+ constructor(o) {
+ return o;
+ }
+}
+
+class InstallPrivateField extends Base {
+ #p = 1;
+ static setP(o, v) {
+ o.#p = v;
+ }
+ static getP(o) {
+ return o.#p;
+ }
+}
+
+// Private field added onto a non-extensible root map before Object.seal.
+{
+ const obj = new Date();
+ obj[0] = 1;
+ obj.x = 1;
+
+ Object.preventExtensions(obj);
+ Object.defineProperty(obj, 0, { enumerable: false });
+
+ new InstallPrivateField(obj);
+ Object.seal(obj);
+ InstallPrivateField.setP(obj, 1.5);
+
+ assertEquals(1.5, InstallPrivateField.getP(obj));
+ assertFalse(Object.getOwnPropertyDescriptor(obj, 'x').configurable);
+}
+
+// Object.seal on a non-extensible root map before adding a private field.
+{
+ const obj = new Date();
+ obj[0] = 1;
+ obj.x = 1;
+
+ Object.preventExtensions(obj);
+ Object.defineProperty(obj, 0, { enumerable: false });
+
+ Object.seal(obj);
+ new InstallPrivateField(obj);
+ InstallPrivateField.setP(obj, 1.5);
+
+ assertEquals(1.5, InstallPrivateField.getP(obj));
+ assertFalse(Object.getOwnPropertyDescriptor(obj, 'x').configurable);
+}
+
+// Constness generalization of a root-map property after Object.seal on a
+// non-extensible dictionary-elements root map must not drop the seal
+// transition.
+{
+ const obj = new Date();
+ obj[0] = 1;
+ obj.x = 1;
+
+ Object.preventExtensions(obj);
+ Object.defineProperty(obj, 0, { enumerable: false });
+
+ Object.seal(obj);
+ obj.x = 2;
+
+ assertEquals(2, obj.x);
+ assertTrue(Object.isSealed(obj));
+ assertFalse(Object.getOwnPropertyDescriptor(obj, 'x').configurable);
+}
diff --git a/deps/v8/test/mjsunit/turboshaft/regress-574660146.js b/deps/v8/test/mjsunit/turboshaft/regress-574660146.js
new file mode 100644
index 00000000000..bc82a2684ba
--- /dev/null
+++ b/deps/v8/test/mjsunit/turboshaft/regress-574660146.js
@@ -0,0 +1,41 @@
+// Copyright 2026 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 --turbofan --no-maglev
+
+function cb() {
+ return arguments.length;
+}
+%NeverOptimizeFunction(cb);
+
+// Passing `kArgCount` distinct expressions to a non-inlined call keeps all of
+// them live simultaneously at the call site, requiring > 1024 stack slots
+// (> 4KB on both 32-bit and 64-bit targets) while keeping the bytecode small
+// enough to be compiled.
+const kArgCount = 1100;
+const args = Array.from({length: kArgCount}, (_, k) => `p ^ ${k + 1}`);
+
+function buildWideFrame(osr) {
+ return Function('x', 'cb', 'p', `
+ let result = 0;
+ for (let i = 0; i < 10; ++i) {
+ ${osr ? 'if (i === 5) %OptimizeOsr();' : ''}
+ if (x < 0) return cb(${args});
+ result = (x + i) | 0;
+ }
+ return result;
+ `);
+}
+
+const wide = buildWideFrame(false);
+%PrepareFunctionForOptimization(wide);
+assertEquals(kArgCount, wide(-1, cb, 1));
+assertEquals(10, wide(1, cb, 1));
+%OptimizeFunctionOnNextCall(wide);
+assertEquals(10, wide(1, cb, 1));
+
+const wideOsr = buildWideFrame(true);
+%PrepareFunctionForOptimization(wideOsr);
+assertEquals(kArgCount, wideOsr(-1, cb, 1));
+assertEquals(10, wideOsr(1, cb, 1));
diff --git a/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc b/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc
index 9df26a108ca..bfe99ffa50e 100644
--- a/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc
+++ b/deps/v8/test/unittests/heap/cppgc/write-barrier-unittest.cc
@@ -476,11 +476,11 @@ TEST_F(WriteBarrierTest, SteeleWriteBarrierTriggersWhenMarkingIsOn) {
}
}
-TEST_F(WriteBarrierTest, SteeleWriteBarrierBailoutIfNotMarked) {
+TEST_F(WriteBarrierTest, SteeleWriteBarrierTriggersEvenIfNotMarked) {
auto* object1 = MakeGarbageCollected<GCed>(GetAllocationHandle());
auto* object2 = MakeGarbageCollected<GCed>(GetAllocationHandle(), object1);
{
- ExpectNoWriteBarrierFires scope(marker(), {object1});
+ ExpectWriteBarrierFires scope(marker(), {object1});
WriteBarrierParams params;
EXPECT_EQ(WriteBarrierType::kMarking,
HeapConsistency::GetWriteBarrierType(