diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /js/src/vm/Iteration.h | |
parent | Initial commit. (diff) | |
download | firefox-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.h | 508 |
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 */ |