author | Jeff Walden <[email protected]> |
Wed, 16 May 2018 23:55:40 -0700 | |
changeset 418922 | 7491ab23247fc11cca02bd0a4f54ed92f15b40f4 |
parent 418921 | 7d83c7de94049fd53fec35e5e7eebf8134c52cc0 |
child 418923 | 63d37d73f5ae5d76fd50a469a9c67fc9f38b3b82 |
push id | 103419 |
push user | [email protected] |
push date | Fri, 18 May 2018 19:33:51 +0000 |
treeherder | mozilla-inbound@7658d2d1e0d7 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jandem |
bugs | 1462540 |
milestone | 62.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
js/src/vm/Iteration.cpp | file | annotate | diff | comparison | revisions | |
js/src/vm/Iteration.h | file | annotate | diff | comparison | revisions |
--- a/js/src/vm/Iteration.cpp +++ b/js/src/vm/Iteration.cpp @@ -9,16 +9,18 @@ #include "vm/Iteration.h" #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/Unused.h" +#include <new> + #include "jstypes.h" #include "jsutil.h" #include "builtin/Array.h" #include "ds/Sort.h" #include "gc/FreeOp.h" #include "gc/Marking.h" #include "js/Proxy.h" @@ -41,17 +43,16 @@ using namespace js; using namespace js::gc; using mozilla::DebugOnly; using mozilla::Maybe; using mozilla::PodCopy; using mozilla::PodEqual; -using mozilla::PodZero; typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject; static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND; void NativeIterator::trace(JSTracer* trc) { @@ -529,17 +530,27 @@ Snapshot(JSContext* cx, HandleObject pob JS_FRIEND_API(bool) js::GetPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector* props) { return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY), props); } -static inline PropertyIteratorObject* +static inline void +RegisterEnumerator(JSContext* cx, NativeIterator* ni) +{ + /* Register non-escaping native enumerators (for-in) with the current context. */ + ni->link(cx->compartment()->enumerators); + + MOZ_ASSERT(!(ni->flags & JSITER_ACTIVE)); + ni->flags |= JSITER_ACTIVE; +} + +static PropertyIteratorObject* NewPropertyIteratorObject(JSContext* cx) { RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_, TaggedProto(nullptr))); if (!group) return nullptr; const Class* clasp = &PropertyIteratorObject::class_; @@ -558,181 +569,187 @@ NewPropertyIteratorObject(JSContext* cx) // CodeGenerator::visitIteratorStartO assumes the iterator object is not // inside the nursery when deciding whether a barrier is necessary. MOZ_ASSERT(!js::gc::IsInsideNursery(res)); MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS); return res; } -NativeIterator* -NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, uint32_t plength) +static PropertyIteratorObject* +CreatePropertyIterator(JSContext* cx, Handle<JSObject*> objBeingIterated, + const AutoIdVector& props, uint32_t numGuards, uint32_t guardKey) { + Rooted<PropertyIteratorObject*> propIter(cx, NewPropertyIteratorObject(cx)); + if (!propIter) + return nullptr; + static_assert(sizeof(ReceiverGuard) == 2 * sizeof(GCPtrFlatString), "NativeIterators are allocated in space for 1) themselves, " "2) the properties a NativeIterator iterates (as " - "GCPtrFlatStrings), and 3) |numGuards| ReceiverGuard " + "GCPtrFlatStrings), and 3) |numGuards| HeapReceiverGuard " "objects; the additional-length calculation below assumes " "this size-relationship when determining the extra space to " "allocate"); - size_t extraLength = plength + numGuards * 2; - NativeIterator* ni = - cx->zone()->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraLength); - if (!ni) { + size_t extraCount = props.length() + numGuards * 2; + void* mem = + cx->zone()->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraCount); + if (!mem) { ReportOutOfMemory(cx); return nullptr; } - // Zero out the NativeIterator first. - PodZero(ni); + // This also registers |ni| with |propIter|. + bool hadError = false; + NativeIterator* ni = + new (mem) NativeIterator(cx, propIter, objBeingIterated, props, numGuards, guardKey, + &hadError); + if (hadError) + return nullptr; + + RegisterEnumerator(cx, ni); + return propIter; +} - // Zero out the remaining space for GCPtrFlatStrings for properties and - // ReceiverGuards for guards. - GCPtrFlatString* extra = ni->begin(); - PodZero(extra, extraLength); +/** + * Initialize a sentinel NativeIterator whose purpose is only to act as the + * start/end of the circular linked list of NativeIterators in + * JSCompartment::enumerators. + */ +NativeIterator::NativeIterator() +{ + // Do our best to enforce that nothing in |this| except the two fields set + // below is ever observed. + JS_POISON(static_cast<void*>(this), 0xCC, sizeof(*this), MemCheckKind::MakeUndefined); - ni->props_cursor = extra; - ni->props_end = extra + plength; - return ni; + // These are the only two fields in sentinel NativeIterators that are + // examined, in JSCompartment::sweepNativeIterators. Everything else is + // only examined *if* it's a NativeIterator being traced by a + // PropertyIteratorObject that owns it, and nothing owns this iterator. + prev_ = next_ = this; } NativeIterator* NativeIterator::allocateSentinel(JSContext* maybecx) { - NativeIterator* ni = js_pod_malloc<NativeIterator>(); + NativeIterator* ni = js_new<NativeIterator>(); if (!ni) { if (maybecx) ReportOutOfMemory(maybecx); - return nullptr; } - PodZero(ni); - - ni->next_ = ni; - ni->prev_ = ni; return ni; } -inline void -NativeIterator::init(JSObject* obj, JSObject* iterObj, uint32_t numGuards, uint32_t key) -{ - this->obj.init(obj); - this->iterObj_ = iterObj; - this->flags = 0; - this->guard_length = numGuards; - this->guard_key = key; -} - -bool -NativeIterator::initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj, - const AutoIdVector& props) +/** + * Initialize a fresh NativeIterator. + * + * This definition is a bit tricky: some parts of initializing are fallible, so + * as we initialize, we must carefully keep this in GC-safe state (see + * NativeIterator::trace). + */ +NativeIterator::NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter, + Handle<JSObject*> objBeingIterated, const AutoIdVector& props, + uint32_t numGuards, uint32_t guardKey, bool* hadError) + : obj(objBeingIterated), + iterObj_(propIter), + // NativeIterator initially acts as if it contains no properties. + props_cursor(begin()), + props_end(props_cursor), + // ...and no HeapReceiverGuards. + guard_length(0), + guard_key(guardKey), + flags(0) { - // The obj parameter is just so that we can ensure that this object will get - // traced if we GC. - MOZ_ASSERT(this == obj->getNativeIterator()); + MOZ_ASSERT(!*hadError); - size_t plength = props.length(); - MOZ_ASSERT(plength == size_t(end() - begin())); + // NOTE: This must be done first thing: PropertyIteratorObject::finalize + // can only free |this| (and not leak it) if this has happened. + propIter->setNativeIterator(this); - GCPtrFlatString* propNames = begin(); - for (size_t i = 0; i < plength; i++) { + for (size_t i = 0, len = props.length(); i < len; i++) { JSFlatString* str = IdToString(cx, props[i]); - if (!str) - return false; - propNames[i].init(str); + if (!str) { + *hadError = true; + return; + } + + // Placement-new the next property string at the end of the currently + // computed property strings. + GCPtrFlatString* loc = props_end; + + // Increase the overall property string count before initializing the + // property string, so this construction isn't on a location not known + // to the GC yet. + props_end++; + + new (loc) GCPtrFlatString(str); } - return true; -} - -static inline void -RegisterEnumerator(JSContext* cx, NativeIterator* ni) -{ - /* Register non-escaping native enumerators (for-in) with the current context. */ - ni->link(cx->compartment()->enumerators); - - MOZ_ASSERT(!(ni->flags & JSITER_ACTIVE)); - ni->flags |= JSITER_ACTIVE; -} - -static inline PropertyIteratorObject* -VectorToKeyIterator(JSContext* cx, HandleObject obj, AutoIdVector& keys, uint32_t numGuards) -{ - if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj)) - return nullptr; - MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); - - Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx)); - if (!iterobj) - return nullptr; - - NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys.length()); - if (!ni) - return nullptr; - - iterobj->setNativeIterator(ni); - ni->init(obj, iterobj, numGuards, 0); - if (!ni->initProperties(cx, iterobj, keys)) - return nullptr; - - if (numGuards) { - // Fill in the guard array from scratch. Also recompute the guard key - // as we might have reshaped the object (see for instance the - // setIteratedSingleton call above) or GC might have moved shapes and - // groups in memory. - JSObject* pobj = obj; - size_t ind = 0; + if (numGuards > 0) { + // Construct guards into the guard array. Also recompute the guard key, + // which incorporates Shape* and ObjectGroup* addresses that could have + // changed during a GC triggered in (among other places) |IdToString| + //. above. + JSObject* pobj = objBeingIterated; uint32_t key = 0; - HeapReceiverGuard* guards = ni->guardArray(); + HeapReceiverGuard* guards = guardArray(); do { ReceiverGuard guard(pobj); - guards[ind++].init(guard); + + // Placement-new the next HeapReceiverGuard at the end of the + // currently initialized HeapReceiverGuards. + uint32_t index = guard_length; + + // Increase the overall guard-count before initializing the + // HeapReceiverGuard, so this construction isn't on a location not + // known to the GC. + guard_length++; + + new (&guards[index]) HeapReceiverGuard(guard); + key = mozilla::AddToHash(key, guard.hash()); // The one caller of this method that passes |numGuards > 0|, does // so only if the entire chain consists of cacheable objects (that // necessarily have static prototypes). pobj = pobj->staticPrototype(); } while (pobj); - ni->guard_key = key; - MOZ_ASSERT(ind == numGuards); + + guard_key = key; + MOZ_ASSERT(guard_length == numGuards); } - RegisterEnumerator(cx, ni); - return iterobj; + MOZ_ASSERT(!*hadError); +} + +static inline PropertyIteratorObject* +VectorToKeyIterator(JSContext* cx, HandleObject obj, AutoIdVector& props, uint32_t numGuards) +{ + if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj)) + return nullptr; + MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED); + + return CreatePropertyIterator(cx, obj, props, numGuards, 0); } JSObject* js::EnumeratedIdVectorToIterator(JSContext* cx, HandleObject obj, AutoIdVector& props) { return VectorToKeyIterator(cx, obj, props, 0); } // Mainly used for .. in over null/undefined JSObject* js::NewEmptyPropertyIterator(JSContext* cx) { - Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx)); - if (!iterobj) - return nullptr; - - AutoIdVector keys(cx); // Empty - NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length()); - if (!ni) - return nullptr; - - iterobj->setNativeIterator(ni); - ni->init(nullptr, iterobj, 0, 0); - if (!ni->initProperties(cx, iterobj, keys)) - return nullptr; - - RegisterEnumerator(cx, ni); - return iterobj; + AutoIdVector props(cx); // Empty + return CreatePropertyIterator(cx, nullptr, props, 0, 0); } /* static */ bool IteratorHashPolicy::match(PropertyIteratorObject* obj, const Lookup& lookup) { NativeIterator* ni = obj->getNativeIterator(); if (ni->guard_key != lookup.key || ni->guard_length != lookup.numGuards) return false;
--- a/js/src/vm/Iteration.h +++ b/js/src/vm/Iteration.h @@ -26,39 +26,61 @@ #define JSITER_UNREUSABLE 0x2000 namespace js { class PropertyIteratorObject; struct NativeIterator { - GCPtrObject obj; // Object being iterated. - JSObject* iterObj_; // Internal iterator object. - GCPtrFlatString* props_cursor; - GCPtrFlatString* props_end; // also the start of HeapReceiverGuards - uint32_t guard_length; - uint32_t guard_key; - uint32_t flags; + // Object being iterated. + GCPtrObject obj = {}; + + // Internal iterator object. + JSObject* iterObj_ = nullptr; + + // The next property, pointing into an array of strings directly after this + // NativeIterator as part of the overall allocation containing |*this|. + GCPtrFlatString* props_cursor; // initialized by constructor + + // The limit/end of properties to iterate. (This is also, after casting, + // the start of an array of HeapReceiverGuards included in the overall + // allocation that stores |*this| and the iterated strings.) + GCPtrFlatString* props_end; // initialized by constructor + + uint32_t guard_length = 0; + uint32_t guard_key = 0; + uint32_t flags = 0; private: /* While in compartment->enumerators, these form a doubly linked list. */ - NativeIterator* next_; - NativeIterator* prev_; + NativeIterator* next_ = nullptr; + NativeIterator* prev_ = nullptr; // No further fields appear after here *in NativeIterator*, but this class - // is always allocated with space tacked on immediately after |this| (see - // below) to store 1) a dynamically known number of iterated property names - // and 2) a dynamically known number of HeapReceiverGuards. The limit of - // all such property names, and the start of HeapReceiverGuards, is - // |props_end|. The next property name to iterate is |props_cursor| and - // equals |props_end| when this iterator is exhausted but not ready yet for - // possible reuse. + // 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, const AutoIdVector& props, + uint32_t numGuards, uint32_t guardKey, bool* hadError); + + /** Initialize a |JSCompartment::enumerators| sentinel. */ + NativeIterator(); + GCPtrFlatString* begin() const { static_assert(alignof(NativeIterator) >= alignof(GCPtrFlatString), "GCPtrFlatStrings for properties must be able to appear " "directly after NativeIterator, with no padding space " "required for correct alignment"); // Note that JIT code inlines this computation to reset |props_cursor| // when an iterator ends: see |CodeGenerator::visitIteratorEnd|. @@ -118,26 +140,18 @@ struct NativeIterator void unlink() { next_->prev_ = prev_; prev_->next_ = next_; next_ = nullptr; prev_ = nullptr; } static NativeIterator* allocateSentinel(JSContext* maybecx); - static NativeIterator* allocateIterator(JSContext* cx, uint32_t slength, uint32_t plength); - void init(JSObject* obj, JSObject* iterObj, uint32_t slength, uint32_t key); - bool initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj, - const js::AutoIdVector& props); void trace(JSTracer* trc); - - static void destroy(NativeIterator* iter) { - js_free(iter); - } }; class PropertyIteratorObject : public NativeObject { static const ClassOps classOps_; public: static const Class class_;