commit 4931779c528211b8785c435a1185a8d2fb9c256d
parent ae8d1d79ec312f4e03bef28921041e1c0b72b0b7
Author: alexical <dothayer@mozilla.com>
Date: Wed, 12 Nov 2025 01:41:22 +0000
Bug 1995077 - Track own property count in NativeIterator r=iain
Differential Revision: https://phabricator.services.mozilla.com/D269126
Diffstat:
2 files changed, 29 insertions(+), 10 deletions(-)
diff --git a/js/src/vm/Iteration.cpp b/js/src/vm/Iteration.cpp
@@ -94,6 +94,8 @@ class PropertyEnumerator {
uint32_t flags_;
Rooted<PropertyKeySet> visited_;
+ uint32_t ownPropertyCount_;
+
bool enumeratingProtoChain_ = false;
enum class IndicesState {
@@ -138,6 +140,7 @@ class PropertyEnumerator {
bool allocatingIndices() const {
return indicesState_ == IndicesState::Allocating;
}
+ uint32_t ownPropertyCount() const { return ownPropertyCount_; }
private:
template <bool CheckForDuplicates>
@@ -667,6 +670,10 @@ bool PropertyEnumerator::snapshot(JSContext* cx) {
MOZ_CRASH("non-native objects must have an enumerate op");
}
+ if (!enumeratingProtoChain_) {
+ ownPropertyCount_ = props_.length();
+ }
+
if (flags_ & JSITER_OWNONLY) {
break;
}
@@ -783,7 +790,7 @@ static inline size_t AllocationSize(size_t propertyCount,
static PropertyIteratorObject* CreatePropertyIterator(
JSContext* cx, Handle<JSObject*> objBeingIterated, HandleIdVector props,
bool supportsIndices, PropertyIndexVector* indices,
- uint32_t cacheableProtoChainLength) {
+ uint32_t cacheableProtoChainLength, uint32_t ownPropertyCount) {
MOZ_ASSERT_IF(indices, supportsIndices);
if (props.length() >= NativeIterator::PropCountLimit) {
ReportAllocationOverflow(cx);
@@ -819,8 +826,9 @@ static PropertyIteratorObject* CreatePropertyIterator(
// This also registers |ni| with |propIter|.
bool hadError = false;
- new (mem) NativeIterator(cx, propIter, objBeingIterated, props,
- supportsIndices, indices, numShapes, &hadError);
+ new (mem)
+ NativeIterator(cx, propIter, objBeingIterated, props, supportsIndices,
+ indices, numShapes, ownPropertyCount, &hadError);
if (hadError) {
return nullptr;
}
@@ -844,13 +852,14 @@ NativeIterator::NativeIterator(JSContext* cx,
Handle<JSObject*> objBeingIterated,
HandleIdVector props, bool supportsIndices,
PropertyIndexVector* indices, uint32_t numShapes,
- bool* hadError)
+ uint32_t ownPropertyCount, bool* hadError)
: objectBeingIterated_(objBeingIterated),
iterObj_(propIter),
objShape_(numShapes > 0 ? objBeingIterated->shape() : nullptr),
// This holds the allocated property count until we're done with
// initialization
propertyCursor_(props.length()),
+ ownPropertyCount_(ownPropertyCount),
shapesHash_(0) {
// If there are shapes, the object and all objects on its prototype chain must
// be native objects. See CanCompareIterableObjectToCache.
@@ -1207,6 +1216,7 @@ static PropertyIteratorObject* GetIteratorImpl(JSContext* cx,
RootedIdVector keys(cx);
PropertyIndexVector indices(cx);
bool supportsIndices = false;
+ uint32_t ownPropertyCount = 0;
if (MOZ_UNLIKELY(obj->is<ProxyObject>())) {
if (!Proxy::enumerate(cx, obj, &keys)) {
@@ -1219,6 +1229,7 @@ static PropertyIteratorObject* GetIteratorImpl(JSContext* cx,
return nullptr;
}
supportsIndices = enumerator.supportsIndices();
+ ownPropertyCount = enumerator.ownPropertyCount();
MOZ_ASSERT_IF(WantIndices && supportsIndices,
keys.length() == indices.length());
}
@@ -1240,8 +1251,9 @@ static PropertyIteratorObject* GetIteratorImpl(JSContext* cx,
PropertyIndexVector* indicesPtr =
WantIndices && supportsIndices ? &indices : nullptr;
- PropertyIteratorObject* iterobj = CreatePropertyIterator(
- cx, obj, keys, supportsIndices, indicesPtr, cacheableProtoChainLength);
+ PropertyIteratorObject* iterobj =
+ CreatePropertyIterator(cx, obj, keys, supportsIndices, indicesPtr,
+ cacheableProtoChainLength, ownPropertyCount);
if (!iterobj) {
return nullptr;
}
@@ -1644,7 +1656,7 @@ PropertyIteratorObject* GlobalObject::getOrCreateEmptyIterator(JSContext* cx) {
if (!cx->global()->data().emptyIterator) {
RootedIdVector props(cx); // Empty
PropertyIteratorObject* iter =
- CreatePropertyIterator(cx, nullptr, props, false, nullptr, 0);
+ CreatePropertyIterator(cx, nullptr, props, false, nullptr, 0, 0);
if (!iter) {
return nullptr;
}
diff --git a/js/src/vm/Iteration.h b/js/src/vm/Iteration.h
@@ -232,8 +232,9 @@ struct NativeIterator : public NativeIteratorListNode {
const GCPtr<JSObject*> iterObj_ = {};
const GCPtr<Shape*> objShape_ = {};
uint32_t propertyCount_ = 0;
- uint32_t propertyCursor_; // initialized by constructor
- HashNumber shapesHash_; // initialized by constructor
+ uint32_t propertyCursor_; // initialized by constructor
+ uint32_t ownPropertyCount_; // initialized by constructor
+ HashNumber shapesHash_; // initialized by constructor
uint16_t protoShapeCount_ = 0;
uint8_t flags_ = 0;
@@ -327,7 +328,7 @@ struct NativeIterator : public NativeIteratorListNode {
NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
Handle<JSObject*> objBeingIterated, HandleIdVector props,
bool supportsIndices, PropertyIndexVector* indices,
- uint32_t numShapes, bool* hadError);
+ uint32_t numShapes, uint32_t ownPropertyCount, bool* hadError);
JSObject* objectBeingIterated() const { return objectBeingIterated_; }
@@ -443,6 +444,8 @@ struct NativeIterator : public NativeIteratorListNode {
size_t numKeys() const { return propertyCount_; }
+ size_t ownPropertyCount() const { return ownPropertyCount_; }
+
size_t allocatedPropertyCount() const {
// propertyCursor_ holds the number of allocated properties until
// the iterator is initialized. This is so we can know the proper layout
@@ -619,6 +622,10 @@ struct NativeIterator : public NativeIteratorListNode {
return offsetof(NativeIterator, propertyCount_);
}
+ static constexpr size_t offsetOfOwnPropertyCount() {
+ return offsetof(NativeIterator, ownPropertyCount_);
+ }
+
static constexpr size_t offsetOfFlags() {
return offsetof(NativeIterator, flags_);
}