summaryrefslogtreecommitdiffstats
path: root/js/src/vm/Iteration.h
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/vm/Iteration.h
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/vm/Iteration.h')
-rw-r--r--js/src/vm/Iteration.h508
1 files changed, 508 insertions, 0 deletions
diff --git a/js/src/vm/Iteration.h b/js/src/vm/Iteration.h
new file mode 100644
index 0000000000..a0e135325d
--- /dev/null
+++ b/js/src/vm/Iteration.h
@@ -0,0 +1,508 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_Iteration_h
+#define vm_Iteration_h
+
+/*
+ * JavaScript iterators.
+ */
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/MemoryReporting.h"
+
+#include "builtin/SelfHostingDefines.h"
+#include "gc/Barrier.h"
+#include "vm/ReceiverGuard.h"
+#include "vm/Stack.h"
+
+namespace js {
+
+class PlainObject;
+class PropertyIteratorObject;
+
+struct NativeIterator {
+ private:
+ // Object being iterated. Non-null except in NativeIterator sentinels and
+ // empty property iterators created when |null| or |undefined| is iterated.
+ GCPtrObject objectBeingIterated_ = {};
+
+ // Internal iterator object.
+ const GCPtrObject iterObj_ = {};
+
+ // The end of HeapReceiverGuards that appear directly after |this|, as part
+ // of an overall allocation that stores |*this|, receiver guards, and
+ // iterated strings. Once this has been fully initialized, it also equals
+ // the start of iterated strings.
+ HeapReceiverGuard* guardsEnd_; // initialized by constructor
+
+ // The next property, pointing into an array of strings directly after any
+ // HeapReceiverGuards that appear directly after |*this|, as part of an
+ // overall allocation that stores |*this|, receiver guards, and iterated
+ // strings.
+ GCPtrLinearString* propertyCursor_; // initialized by constructor
+
+ // The limit/end of properties to iterate (and, assuming no error occurred
+ // while constructing this NativeIterator, the end of the full allocation
+ // storing |*this|, receiver guards, and strings). Beware! This value may
+ // change as properties are deleted from the observed object.
+ GCPtrLinearString* propertiesEnd_; // initialized by constructor
+
+ uint32_t guardKey_; // initialized by constructor
+
+ public:
+ // For cacheable native iterators, whether the iterator is currently
+ // active. Not serialized by XDR.
+ struct Flags {
+ // This flag is set when all guards and properties associated with this
+ // NativeIterator have been initialized, such that |guardsEnd_|, in
+ // addition to being the end of guards, is also the beginning of
+ // properties.
+ //
+ // This flag is only *not* set when a NativeIterator is in the process
+ // of being constructed. At such time |guardsEnd_| accounts only for
+ // guards that have been initialized -- potentially none of them.
+ // Instead, |propertyCursor_| is initialized to the ultimate/actual
+ // start of properties and must be used instead of |propertiesBegin()|,
+ // which asserts that this flag is present to guard against misuse.
+ static constexpr uint32_t Initialized = 0x1;
+
+ // This flag indicates that this NativeIterator is currently being used
+ // to enumerate an object's properties and has not yet been closed.
+ static constexpr uint32_t Active = 0x2;
+
+ // This flag indicates that the object being enumerated by this
+ // |NativeIterator| had a property deleted from it before it was
+ // visited, forcing the properties array in this to be mutated to
+ // remove it.
+ static constexpr uint32_t HasUnvisitedPropertyDeletion = 0x4;
+
+ // If any of these bits are set on a |NativeIterator|, it isn't
+ // currently reusable. (An active |NativeIterator| can't be stolen
+ // *right now*; a |NativeIterator| that's had its properties mutated
+ // can never be reused, because it would give incorrect results.)
+ static constexpr uint32_t NotReusable =
+ Active | HasUnvisitedPropertyDeletion;
+ };
+
+ private:
+ static constexpr uint32_t FlagsBits = 3;
+ static constexpr uint32_t FlagsMask = (1 << FlagsBits) - 1;
+
+ public:
+ static constexpr uint32_t PropCountLimit = 1 << (32 - FlagsBits);
+
+ private:
+ // While in compartment->enumerators, these form a doubly linked list.
+ NativeIterator* next_ = nullptr;
+ NativeIterator* prev_ = nullptr;
+
+ // Stores Flags bits in the lower bits and the initial property count above
+ // them.
+ uint32_t flagsAndCount_ = 0;
+
+#ifdef DEBUG
+ // If true, this iterator may contain indexed properties that came from
+ // objects on the prototype chain. This is used by certain debug assertions.
+ bool maybeHasIndexedPropertiesFromProto_ = false;
+#endif
+
+ // END OF PROPERTIES
+
+ // No further fields appear after here *in NativeIterator*, but this class
+ // is always allocated with space tacked on immediately after |this| to
+ // store iterated property names up to |props_end| and |guard_length|
+ // HeapReceiverGuards after that.
+
+ public:
+ /**
+ * Initialize a NativeIterator properly allocated for |props.length()|
+ * properties and |numGuards| guards.
+ *
+ * Despite being a constructor, THIS FUNCTION CAN REPORT ERRORS. Users
+ * MUST set |*hadError = false| on entry and consider |*hadError| on return
+ * to mean this function failed.
+ */
+ NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
+ Handle<JSObject*> objBeingIterated, HandleIdVector props,
+ uint32_t numGuards, uint32_t guardKey, bool* hadError);
+
+ /** Initialize an |ObjectRealm::enumerators| sentinel. */
+ NativeIterator();
+
+ JSObject* objectBeingIterated() const { return objectBeingIterated_; }
+
+ void changeObjectBeingIterated(JSObject& obj) { objectBeingIterated_ = &obj; }
+
+ HeapReceiverGuard* guardsBegin() const {
+ static_assert(alignof(HeapReceiverGuard) <= alignof(NativeIterator),
+ "NativeIterator must be aligned to begin storing "
+ "HeapReceiverGuards immediately after it with no "
+ "required padding");
+ const NativeIterator* immediatelyAfter = this + 1;
+ auto* afterNonConst = const_cast<NativeIterator*>(immediatelyAfter);
+ return reinterpret_cast<HeapReceiverGuard*>(afterNonConst);
+ }
+
+ HeapReceiverGuard* guardsEnd() const { return guardsEnd_; }
+
+ uint32_t guardCount() const {
+ return mozilla::PointerRangeSize(guardsBegin(), guardsEnd());
+ }
+
+ GCPtrLinearString* propertiesBegin() const {
+ static_assert(alignof(HeapReceiverGuard) >= alignof(GCPtrLinearString),
+ "GCPtrLinearStrings for properties must be able to appear "
+ "directly after any HeapReceiverGuards after this "
+ "NativeIterator, with no padding space required for "
+ "correct alignment");
+ static_assert(alignof(NativeIterator) >= alignof(GCPtrLinearString),
+ "GCPtrLinearStrings for properties must be able to appear "
+ "directly after this NativeIterator when no "
+ "HeapReceiverGuards are present, with no padding space "
+ "required for correct alignment");
+
+ // We *could* just check the assertion below if we wanted, but the
+ // incompletely-initialized NativeIterator case matters for so little
+ // code that we prefer not imposing the condition-check on every single
+ // user.
+ MOZ_ASSERT(isInitialized(),
+ "NativeIterator must be initialized, or else |guardsEnd_| "
+ "isn't necessarily the start of properties and instead "
+ "|propertyCursor_| instead is");
+
+ return reinterpret_cast<GCPtrLinearString*>(guardsEnd_);
+ }
+
+ GCPtrLinearString* propertiesEnd() const { return propertiesEnd_; }
+
+ GCPtrLinearString* nextProperty() const { return propertyCursor_; }
+
+ MOZ_ALWAYS_INLINE JS::Value nextIteratedValueAndAdvance() {
+ if (propertyCursor_ >= propertiesEnd_) {
+ MOZ_ASSERT(propertyCursor_ == propertiesEnd_);
+ return JS::MagicValue(JS_NO_ITER_VALUE);
+ }
+
+ JSLinearString* str = *propertyCursor_;
+ incCursor();
+ return JS::StringValue(str);
+ }
+
+ void resetPropertyCursorForReuse() {
+ MOZ_ASSERT(isInitialized());
+
+ // This function is called unconditionally on IteratorClose, so
+ // unvisited properties might have been deleted, so we can't assert
+ // this NativeIterator is reusable. (Should we not bother resetting
+ // the cursor in that case?)
+
+ // Note: JIT code inlines |propertyCursor_| resetting when an iterator
+ // ends: see |CodeGenerator::visitIteratorEnd|.
+ propertyCursor_ = propertiesBegin();
+ }
+
+ bool previousPropertyWas(JS::Handle<JSLinearString*> str) {
+ MOZ_ASSERT(isInitialized());
+ return propertyCursor_ > propertiesBegin() && propertyCursor_[-1] == str;
+ }
+
+ size_t numKeys() const {
+ return mozilla::PointerRangeSize(propertiesBegin(), propertiesEnd());
+ }
+
+ void trimLastProperty() {
+ MOZ_ASSERT(isInitialized());
+
+ propertiesEnd_--;
+
+ // This invokes the pre barrier on this property, since it's no longer
+ // going to be marked, and it ensures that any existing remembered set
+ // entry will be dropped.
+ *propertiesEnd_ = nullptr;
+ }
+
+ JSObject* iterObj() const { return iterObj_; }
+ GCPtrLinearString* currentProperty() const {
+ MOZ_ASSERT(propertyCursor_ < propertiesEnd());
+ return propertyCursor_;
+ }
+
+ NativeIterator* next() { return next_; }
+
+ void incCursor() {
+ MOZ_ASSERT(isInitialized());
+ propertyCursor_++;
+ }
+
+ uint32_t guardKey() const { return guardKey_; }
+
+ bool isInitialized() const { return flags() & Flags::Initialized; }
+
+ size_t allocationSize() const;
+
+#ifdef DEBUG
+ void setMaybeHasIndexedPropertiesFromProto() {
+ maybeHasIndexedPropertiesFromProto_ = true;
+ }
+ bool maybeHasIndexedPropertiesFromProto() const {
+ return maybeHasIndexedPropertiesFromProto_;
+ }
+#endif
+
+ private:
+ uint32_t flags() const { return flagsAndCount_ & FlagsMask; }
+
+ uint32_t initialPropertyCount() const { return flagsAndCount_ >> FlagsBits; }
+
+ static uint32_t initialFlagsAndCount(uint32_t count) {
+ // No flags are initially set.
+ MOZ_ASSERT(count < PropCountLimit);
+ return count << FlagsBits;
+ }
+
+ void setFlags(uint32_t flags) {
+ MOZ_ASSERT((flags & ~FlagsMask) == 0);
+ flagsAndCount_ = (initialPropertyCount() << FlagsBits) | flags;
+ }
+
+ void markInitialized() {
+ MOZ_ASSERT(flags() == 0);
+ setFlags(Flags::Initialized);
+ }
+
+ public:
+ bool isActive() const {
+ MOZ_ASSERT(isInitialized());
+
+ return flags() & Flags::Active;
+ }
+
+ void markActive() {
+ MOZ_ASSERT(isInitialized());
+
+ flagsAndCount_ |= Flags::Active;
+ }
+
+ void markInactive() {
+ MOZ_ASSERT(isInitialized());
+
+ flagsAndCount_ &= ~Flags::Active;
+ }
+
+ bool isReusable() const {
+ MOZ_ASSERT(isInitialized());
+
+ // Cached NativeIterators are reusable if they're not currently active
+ // and their properties array hasn't been mutated, i.e. if only
+ // |Flags::Initialized| is set. Using |Flags::NotReusable| to test
+ // would also work, but this formulation is safer against memory
+ // corruption.
+ return flags() == Flags::Initialized;
+ }
+
+ void markHasUnvisitedPropertyDeletion() {
+ MOZ_ASSERT(isInitialized());
+
+ flagsAndCount_ |= Flags::HasUnvisitedPropertyDeletion;
+ }
+
+ void link(NativeIterator* other) {
+ // The NativeIterator sentinel doesn't have to be linked, because it's
+ // the start of the list. Anything else added should have been
+ // initialized.
+ MOZ_ASSERT(isInitialized());
+
+ /* A NativeIterator cannot appear in the enumerator list twice. */
+ MOZ_ASSERT(!next_ && !prev_);
+
+ this->next_ = other;
+ this->prev_ = other->prev_;
+ other->prev_->next_ = this;
+ other->prev_ = this;
+ }
+ void unlink() {
+ MOZ_ASSERT(isInitialized());
+
+ next_->prev_ = prev_;
+ prev_->next_ = next_;
+ next_ = nullptr;
+ prev_ = nullptr;
+ }
+
+ static NativeIterator* allocateSentinel(JSContext* cx);
+
+ void trace(JSTracer* trc);
+
+ static constexpr size_t offsetOfObjectBeingIterated() {
+ return offsetof(NativeIterator, objectBeingIterated_);
+ }
+
+ static constexpr size_t offsetOfGuardsEnd() {
+ return offsetof(NativeIterator, guardsEnd_);
+ }
+
+ static constexpr size_t offsetOfPropertyCursor() {
+ return offsetof(NativeIterator, propertyCursor_);
+ }
+
+ static constexpr size_t offsetOfPropertiesEnd() {
+ return offsetof(NativeIterator, propertiesEnd_);
+ }
+
+ static constexpr size_t offsetOfFlagsAndCount() {
+ return offsetof(NativeIterator, flagsAndCount_);
+ }
+
+ static constexpr size_t offsetOfNext() {
+ return offsetof(NativeIterator, next_);
+ }
+
+ static constexpr size_t offsetOfPrev() {
+ return offsetof(NativeIterator, prev_);
+ }
+};
+
+class PropertyIteratorObject : public NativeObject {
+ static const JSClassOps classOps_;
+
+ public:
+ static const JSClass class_;
+
+ // We don't use the fixed slot but the JITs use this constant to load the
+ // private value (the NativeIterator*).
+ static const uint32_t NUM_FIXED_SLOTS = 1;
+
+ NativeIterator* getNativeIterator() const {
+ return static_cast<js::NativeIterator*>(getPrivate());
+ }
+ void setNativeIterator(js::NativeIterator* ni) { setPrivate(ni); }
+
+ size_t sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const;
+
+ private:
+ static void trace(JSTracer* trc, JSObject* obj);
+ static void finalize(JSFreeOp* fop, JSObject* obj);
+};
+
+class ArrayIteratorObject : public NativeObject {
+ public:
+ static const JSClass class_;
+};
+
+ArrayIteratorObject* NewArrayIteratorTemplate(JSContext* cx);
+ArrayIteratorObject* NewArrayIterator(JSContext* cx);
+
+class StringIteratorObject : public NativeObject {
+ public:
+ static const JSClass class_;
+};
+
+StringIteratorObject* NewStringIteratorTemplate(JSContext* cx);
+StringIteratorObject* NewStringIterator(JSContext* cx);
+
+class RegExpStringIteratorObject : public NativeObject {
+ public:
+ static const JSClass class_;
+};
+
+RegExpStringIteratorObject* NewRegExpStringIteratorTemplate(JSContext* cx);
+RegExpStringIteratorObject* NewRegExpStringIterator(JSContext* cx);
+
+MOZ_MUST_USE bool EnumerateProperties(JSContext* cx, HandleObject obj,
+ MutableHandleIdVector props);
+
+PropertyIteratorObject* LookupInIteratorCache(JSContext* cx, HandleObject obj);
+
+JSObject* ValueToIterator(JSContext* cx, HandleValue vp);
+
+void CloseIterator(JSObject* obj);
+
+bool IteratorCloseForException(JSContext* cx, HandleObject obj);
+
+void UnwindIteratorForUncatchableException(JSObject* obj);
+
+extern bool SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id);
+
+extern bool SuppressDeletedElement(JSContext* cx, HandleObject obj,
+ uint32_t index);
+
+#ifdef DEBUG
+extern void AssertDenseElementsNotIterated(NativeObject* obj);
+#else
+inline void AssertDenseElementsNotIterated(NativeObject* obj) {}
+#endif
+
+/*
+ * IteratorMore() returns the next iteration value. If no value is available,
+ * MagicValue(JS_NO_ITER_VALUE) is returned.
+ */
+inline Value IteratorMore(JSObject* iterobj) {
+ NativeIterator* ni =
+ iterobj->as<PropertyIteratorObject>().getNativeIterator();
+ return ni->nextIteratedValueAndAdvance();
+}
+
+/*
+ * Create an object of the form { value: VALUE, done: DONE }.
+ * ES 2017 draft 7.4.7.
+ */
+extern PlainObject* CreateIterResultObject(JSContext* cx, HandleValue value,
+ bool done);
+
+/*
+ * Global Iterator constructor.
+ * Iterator Helpers proposal 2.1.3.
+ */
+class IteratorObject : public NativeObject {
+ public:
+ static const JSClass class_;
+ static const JSClass protoClass_;
+};
+
+/*
+ * Wrapper for iterators created via Iterator.from.
+ * Iterator Helpers proposal 2.1.3.3.1.1.
+ */
+class WrapForValidIteratorObject : public NativeObject {
+ public:
+ static const JSClass class_;
+
+ enum { IteratedSlot, SlotCount };
+
+ static_assert(
+ IteratedSlot == ITERATED_SLOT,
+ "IteratedSlot must match self-hosting define for iterated object slot.");
+};
+
+WrapForValidIteratorObject* NewWrapForValidIterator(JSContext* cx);
+
+/*
+ * Generator-esque object returned by Iterator Helper methods.
+ */
+class IteratorHelperObject : public NativeObject {
+ public:
+ static const JSClass class_;
+
+ enum {
+ // The implementation (an instance of one of the generators in
+ // builtin/Iterator.js).
+ // Never null.
+ GeneratorSlot,
+
+ SlotCount,
+ };
+
+ static_assert(GeneratorSlot == ITERATOR_HELPER_GENERATOR_SLOT,
+ "GeneratorSlot must match self-hosting define for generator "
+ "object slot.");
+};
+
+IteratorHelperObject* NewIteratorHelper(JSContext* cx);
+
+} /* namespace js */
+
+#endif /* vm_Iteration_h */