LCOV - code coverage report
Current view: top level - js/src/vm - Iteration.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 198 502 39.4 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : /* JavaScript iterators. */
       8             : 
       9             : #include "vm/Iteration.h"
      10             : 
      11             : #include "mozilla/DebugOnly.h"
      12             : #include "mozilla/Likely.h"
      13             : #include "mozilla/Maybe.h"
      14             : #include "mozilla/MemoryReporting.h"
      15             : #include "mozilla/PodOperations.h"
      16             : #include "mozilla/Unused.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <new>
      20             : 
      21             : #include "jstypes.h"
      22             : #include "jsutil.h"
      23             : 
      24             : #include "builtin/Array.h"
      25             : #include "ds/Sort.h"
      26             : #include "gc/FreeOp.h"
      27             : #include "gc/Marking.h"
      28             : #include "js/Proxy.h"
      29             : #include "vm/BytecodeUtil.h"
      30             : #include "vm/GeneratorObject.h"
      31             : #include "vm/GlobalObject.h"
      32             : #include "vm/Interpreter.h"
      33             : #include "vm/JSAtom.h"
      34             : #include "vm/JSContext.h"
      35             : #include "vm/JSObject.h"
      36             : #include "vm/JSScript.h"
      37             : #include "vm/Shape.h"
      38             : #include "vm/TypedArrayObject.h"
      39             : 
      40             : #include "vm/Compartment-inl.h"
      41             : #include "vm/JSScript-inl.h"
      42             : #include "vm/NativeObject-inl.h"
      43             : #include "vm/ReceiverGuard-inl.h"
      44             : #include "vm/Stack-inl.h"
      45             : #include "vm/StringType-inl.h"
      46             : 
      47             : using namespace js;
      48             : using namespace js::gc;
      49             : 
      50             : using mozilla::DebugOnly;
      51             : using mozilla::Maybe;
      52             : using mozilla::PodCopy;
      53             : using mozilla::PodEqual;
      54             : 
      55             : typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
      56             : 
      57             : static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
      58             : 
      59             : void
      60           3 : NativeIterator::trace(JSTracer* trc)
      61             : {
      62           6 :     TraceNullableEdge(trc, &objectBeingIterated_, "objectBeingIterated_");
      63             : 
      64             :     // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
      65             :     // GC removes any elements from the list, it won't remove this one.
      66           0 :     if (iterObj_)
      67           3 :         TraceManuallyBarrieredEdge(trc, &iterObj_, "iterObj");
      68             : 
      69             :     std::for_each(guardsBegin(), guardsEnd(),
      70             :                   [trc](HeapReceiverGuard& guard) {
      71           0 :                       guard.trace(trc);
      72           9 :                   });
      73             : 
      74           6 :     GCPtrFlatString* begin = MOZ_LIKELY(isInitialized()) ? propertiesBegin() : propertyCursor_;
      75             :     std::for_each(begin, propertiesEnd(),
      76          24 :                   [trc](GCPtrFlatString& prop) {
      77             :                       // Properties begin life non-null and never *become*
      78             :                       // null.  (Deletion-suppression will shift trailing
      79             :                       // properties over a deleted property in the properties
      80             :                       // array, but it doesn't null them out.)
      81           0 :                       TraceEdge(trc, &prop, "prop");
      82           0 :                   });
      83           3 : }
      84             : 
      85             : typedef HashSet<jsid, DefaultHasher<jsid>> IdSet;
      86             : 
      87             : template <bool CheckForDuplicates>
      88             : static inline bool
      89       18055 : Enumerate(JSContext* cx, HandleObject pobj, jsid id,
      90             :           bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
      91             : {
      92             :     if (CheckForDuplicates) {
      93           0 :         if (!ht) {
      94          95 :             ht.emplace(cx);
      95             :             // Most of the time there are only a handful of entries.
      96           0 :             if (!ht->init(5))
      97           5 :                 return false;
      98             :         }
      99             : 
     100             :         // If we've already seen this, we definitely won't add it.
     101           0 :         IdSet::AddPtr p = ht->lookupForAdd(id);
     102        1870 :         if (MOZ_UNLIKELY(!!p))
     103             :             return true;
     104             : 
     105             :         // It's not necessary to add properties to the hash table at the end of
     106             :         // the prototype chain, but custom enumeration behaviors might return
     107             :         // duplicated properties, so always add in such cases.
     108           0 :         if (pobj->is<ProxyObject>() ||
     109           0 :             pobj->staticPrototype() ||
     110        2468 :             pobj->getClass()->getNewEnumerate())
     111             :         {
     112        1262 :             if (!ht->add(p, id))
     113             :                 return false;
     114             :         }
     115             :     }
     116             : 
     117       19286 :     if (!enumerable && !(flags & JSITER_HIDDEN))
     118             :         return true;
     119             : 
     120             :     // Symbol-keyed properties and nonenumerable properties are skipped unless
     121             :     // the caller specifically asks for them. A caller can also filter out
     122             :     // non-symbols by asking for JSITER_SYMBOLSONLY.
     123             :     if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
     124             :         return true;
     125             : 
     126       18567 :     return props->append(id);
     127             : }
     128             : 
     129             : template <bool CheckForDuplicates>
     130             : static bool
     131         180 : EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe<IdSet>& ht,
     132             :                          AutoIdVector* props)
     133             : {
     134         540 :     MOZ_ASSERT(obj->getClass()->getNewEnumerate());
     135             : 
     136           0 :     AutoIdVector properties(cx);
     137           0 :     bool enumerableOnly = !(flags & JSITER_HIDDEN);
     138         540 :     if (!obj->getClass()->getNewEnumerate()(cx, obj, properties, enumerableOnly))
     139             :         return false;
     140             : 
     141           0 :     RootedId id(cx);
     142           0 :     for (size_t n = 0; n < properties.length(); n++) {
     143        1164 :         id = properties[n];
     144             : 
     145             :         // The enumerate hook does not indicate whether the properties
     146             :         // it returns are enumerable or not. Since we already passed
     147             :         // `enumerableOnly` to the hook to filter out non-enumerable
     148             :         // properties, it doesn't really matter what we pass here.
     149           0 :         bool enumerable = true;
     150         776 :         if (!Enumerate<CheckForDuplicates>(cx, obj, id, enumerable, flags, ht, props))
     151             :             return false;
     152             :     }
     153             : 
     154             :     return true;
     155             : }
     156             : 
     157             : static bool
     158           0 : SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp)
     159             : {
     160             :     uint32_t indexA, indexB;
     161           0 :     MOZ_ALWAYS_TRUE(IdIsIndex(a, &indexA));
     162           0 :     MOZ_ALWAYS_TRUE(IdIsIndex(b, &indexB));
     163           0 :     *lessOrEqualp = (indexA <= indexB);
     164           0 :     return true;
     165             : }
     166             : 
     167             : template <bool CheckForDuplicates>
     168             : static bool
     169        6529 : EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
     170             :                           AutoIdVector* props, Handle<UnboxedPlainObject*> unboxed = nullptr)
     171             : {
     172             :     bool enumerateSymbols;
     173        6529 :     if (flags & JSITER_SYMBOLSONLY) {
     174             :         enumerateSymbols = true;
     175             :     } else {
     176             :         /* Collect any dense elements from this object. */
     177           0 :         size_t firstElemIndex = props->length();
     178           0 :         size_t initlen = pobj->getDenseInitializedLength();
     179           0 :         const Value* vp = pobj->getDenseElements();
     180           0 :         bool hasHoles = false;
     181           0 :         for (size_t i = 0; i < initlen; ++i, ++vp) {
     182        2688 :             if (vp->isMagic(JS_ELEMENTS_HOLE)) {
     183             :                 hasHoles = true;
     184             :             } else {
     185             :                 /* Dense arrays never get so large that i would not fit into an integer id. */
     186        7776 :                 if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
     187             :                                                    /* enumerable = */ true, flags, ht, props))
     188             :                 {
     189           0 :                     return false;
     190             :                 }
     191             :             }
     192             :         }
     193             : 
     194             :         /* Collect any typed array or shared typed array elements from this object. */
     195           0 :         if (pobj->is<TypedArrayObject>()) {
     196           0 :             size_t len = pobj->as<TypedArrayObject>().length();
     197           0 :             for (size_t i = 0; i < len; i++) {
     198           0 :                 if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
     199             :                                                    /* enumerable = */ true, flags, ht, props))
     200             :                 {
     201             :                     return false;
     202             :                 }
     203             :             }
     204             :         }
     205             : 
     206             :         // Collect any sparse elements from this object.
     207           0 :         bool isIndexed = pobj->isIndexed();
     208        6529 :         if (isIndexed) {
     209             :             // If the dense elements didn't have holes, we don't need to include
     210             :             // them in the sort.
     211           0 :             if (!hasHoles)
     212           0 :                 firstElemIndex = props->length();
     213             : 
     214           0 :             for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
     215           0 :                 Shape& shape = r.front();
     216           0 :                 jsid id = shape.propid();
     217             :                 uint32_t dummy;
     218           0 :                 if (IdIsIndex(id, &dummy)) {
     219           0 :                     if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht,
     220             :                                                        props))
     221             :                     {
     222           0 :                         return false;
     223             :                     }
     224             :                 }
     225             :             }
     226             : 
     227           0 :             MOZ_ASSERT(firstElemIndex <= props->length());
     228             : 
     229           0 :             jsid* ids = props->begin() + firstElemIndex;
     230           0 :             size_t n = props->length() - firstElemIndex;
     231             : 
     232           0 :             AutoIdVector tmp(cx);
     233           0 :             if (!tmp.resize(n))
     234             :                 return false;
     235           0 :             PodCopy(tmp.begin(), ids, n);
     236             : 
     237           0 :             if (!MergeSort(ids, n, tmp.begin(), SortComparatorIntegerIds))
     238             :                 return false;
     239             :         }
     240             : 
     241        6529 :         if (unboxed) {
     242             :             // If |unboxed| is set then |pobj| is the expando for an unboxed
     243             :             // plain object we are enumerating. Add the unboxed properties
     244             :             // themselves here since they are all property names that were
     245             :             // given to the object before any of the expando's properties.
     246           0 :             MOZ_ASSERT(pobj->is<UnboxedExpandoObject>());
     247          33 :             if (!EnumerateExtraProperties<CheckForDuplicates>(cx, unboxed, flags, ht, props))
     248             :                 return false;
     249             :         }
     250             : 
     251        6529 :         size_t initialLength = props->length();
     252             : 
     253             :         /* Collect all unique property names from this object's shape. */
     254           0 :         bool symbolsFound = false;
     255           0 :         Shape::Range<NoGC> r(pobj->lastProperty());
     256           0 :         for (; !r.empty(); r.popFront()) {
     257           0 :             Shape& shape = r.front();
     258       34972 :             jsid id = shape.propid();
     259             : 
     260       17486 :             if (JSID_IS_SYMBOL(id)) {
     261             :                 symbolsFound = true;
     262           1 :                 continue;
     263             :             }
     264             : 
     265             :             uint32_t dummy;
     266       17485 :             if (isIndexed && IdIsIndex(id, &dummy))
     267             :                 continue;
     268             : 
     269           0 :             if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht, props))
     270           0 :                 return false;
     271             :         }
     272       19587 :         ::Reverse(props->begin() + initialLength, props->end());
     273             : 
     274        6529 :         enumerateSymbols = symbolsFound && (flags & JSITER_SYMBOLS);
     275             :     }
     276             : 
     277        6529 :     if (enumerateSymbols) {
     278             :         // Do a second pass to collect symbols. ES6 draft rev 25 (2014 May 22)
     279             :         // 9.1.12 requires that all symbols appear after all strings in the
     280             :         // result.
     281           0 :         size_t initialLength = props->length();
     282           0 :         for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
     283           0 :             Shape& shape = r.front();
     284           0 :             jsid id = shape.propid();
     285           0 :             if (JSID_IS_SYMBOL(id)) {
     286           0 :                 if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht,
     287             :                                                    props))
     288             :                 {
     289           0 :                     return false;
     290             :                 }
     291             :             }
     292             :         }
     293           0 :         ::Reverse(props->begin() + initialLength, props->end());
     294             :     }
     295             : 
     296             :     return true;
     297             : }
     298             : 
     299             : static bool
     300        6529 : EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
     301             :                           AutoIdVector* props, bool checkForDuplicates,
     302             :                           Handle<UnboxedPlainObject*> unboxed = nullptr)
     303             : {
     304           0 :     if (checkForDuplicates)
     305           0 :         return EnumerateNativeProperties<true>(cx, pobj, flags, ht, props, unboxed);
     306        6334 :     return EnumerateNativeProperties<false>(cx, pobj, flags, ht, props, unboxed);
     307             : }
     308             : 
     309             : template <bool CheckForDuplicates>
     310             : static bool
     311         192 : EnumerateProxyProperties(JSContext* cx, HandleObject pobj, unsigned flags, Maybe<IdSet>& ht,
     312             :                          AutoIdVector* props)
     313             : {
     314         384 :     MOZ_ASSERT(pobj->is<ProxyObject>());
     315             : 
     316         384 :     AutoIdVector proxyProps(cx);
     317             : 
     318         192 :     if (flags & JSITER_HIDDEN || flags & JSITER_SYMBOLS) {
     319             :         // This gets all property keys, both strings and symbols. The call to
     320             :         // Enumerate in the loop below will filter out unwanted keys, per the
     321             :         // flags.
     322          24 :         if (!Proxy::ownPropertyKeys(cx, pobj, proxyProps))
     323             :             return false;
     324             : 
     325           0 :         Rooted<PropertyDescriptor> desc(cx);
     326           0 :         for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
     327         182 :             bool enumerable = false;
     328             : 
     329             :             // We need to filter, if the caller just wants enumerable symbols.
     330           0 :             if (!(flags & JSITER_HIDDEN)) {
     331           0 :                 if (!Proxy::getOwnPropertyDescriptor(cx, pobj, proxyProps[n], &desc))
     332             :                     return false;
     333           0 :                 enumerable = desc.enumerable();
     334             :             }
     335             : 
     336         546 :             if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], enumerable, flags, ht,
     337             :                                                props))
     338             :             {
     339             :                 return false;
     340             :             }
     341             :         }
     342             : 
     343             :         return true;
     344             :     }
     345             : 
     346             :     // Returns enumerable property names (no symbols).
     347         168 :     if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, proxyProps))
     348             :         return false;
     349             : 
     350           0 :     for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
     351        1608 :         if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], true, flags, ht, props))
     352             :             return false;
     353             :     }
     354             : 
     355             :     return true;
     356             : }
     357             : 
     358             : #ifdef JS_MORE_DETERMINISTIC
     359             : 
     360             : struct SortComparatorIds
     361             : {
     362             :     JSContext*  const cx;
     363             : 
     364             :     SortComparatorIds(JSContext* cx)
     365             :       : cx(cx) {}
     366             : 
     367             :     bool operator()(jsid a, jsid b, bool* lessOrEqualp)
     368             :     {
     369             :         // Pick an arbitrary order on jsids that is as stable as possible
     370             :         // across executions.
     371             :         if (a == b) {
     372             :             *lessOrEqualp = true;
     373             :             return true;
     374             :         }
     375             : 
     376             :         size_t ta = JSID_BITS(a) & JSID_TYPE_MASK;
     377             :         size_t tb = JSID_BITS(b) & JSID_TYPE_MASK;
     378             :         if (ta != tb) {
     379             :             *lessOrEqualp = (ta <= tb);
     380             :             return true;
     381             :         }
     382             : 
     383             :         if (JSID_IS_INT(a)) {
     384             :             *lessOrEqualp = (JSID_TO_INT(a) <= JSID_TO_INT(b));
     385             :             return true;
     386             :         }
     387             : 
     388             :         RootedString astr(cx), bstr(cx);
     389             :         if (JSID_IS_SYMBOL(a)) {
     390             :             MOZ_ASSERT(JSID_IS_SYMBOL(b));
     391             :             JS::SymbolCode ca = JSID_TO_SYMBOL(a)->code();
     392             :             JS::SymbolCode cb = JSID_TO_SYMBOL(b)->code();
     393             :             if (ca != cb) {
     394             :                 *lessOrEqualp = uint32_t(ca) <= uint32_t(cb);
     395             :                 return true;
     396             :             }
     397             :             MOZ_ASSERT(ca == JS::SymbolCode::InSymbolRegistry || ca == JS::SymbolCode::UniqueSymbol);
     398             :             astr = JSID_TO_SYMBOL(a)->description();
     399             :             bstr = JSID_TO_SYMBOL(b)->description();
     400             :             if (!astr || !bstr) {
     401             :                 *lessOrEqualp = !astr;
     402             :                 return true;
     403             :             }
     404             : 
     405             :             // Fall through to string comparison on the descriptions. The sort
     406             :             // order is nondeterministic if two different unique symbols have
     407             :             // the same description.
     408             :         } else {
     409             :             astr = IdToString(cx, a);
     410             :             if (!astr)
     411             :                 return false;
     412             :             bstr = IdToString(cx, b);
     413             :             if (!bstr)
     414             :                 return false;
     415             :         }
     416             : 
     417             :         int32_t result;
     418             :         if (!CompareStrings(cx, astr, bstr, &result))
     419             :             return false;
     420             : 
     421             :         *lessOrEqualp = (result <= 0);
     422             :         return true;
     423             :     }
     424             : };
     425             : 
     426             : #endif /* JS_MORE_DETERMINISTIC */
     427             : 
     428             : static bool
     429        6768 : Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
     430             : {
     431             :     // We initialize |ht| lazily (in Enumerate()) because it ends up unused
     432             :     // anywhere from 67--99.9% of the time.
     433           0 :     Maybe<IdSet> ht;
     434       13536 :     RootedObject pobj(cx, pobj_);
     435             : 
     436             :     // Don't check for duplicates if we're only interested in own properties.
     437             :     // This does the right thing for most objects: native objects don't have
     438             :     // duplicate property ids and we allow the [[OwnPropertyKeys]] proxy trap to
     439             :     // return duplicates.
     440             :     //
     441             :     // The only special case is when the object has a newEnumerate hook: it
     442             :     // can return duplicate properties and we have to filter them. This is
     443             :     // handled below.
     444        6768 :     bool checkForDuplicates = !(flags & JSITER_OWNONLY);
     445             : 
     446           0 :     do {
     447           0 :         if (pobj->getClass()->getNewEnumerate()) {
     448         540 :             if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) {
     449             :                 // Special case unboxed objects with an expando object.
     450           0 :                 RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando());
     451          66 :                 if (!EnumerateNativeProperties(cx, expando, flags, ht, props, checkForDuplicates,
     452             :                                                pobj.as<UnboxedPlainObject>()))
     453             :                 {
     454           0 :                     return false;
     455             :                 }
     456             :             } else {
     457             :                 // The newEnumerate hook may return duplicates. Whitelist the
     458             :                 // unboxed object hooks because we know they are well-behaved.
     459           0 :                 if (!pobj->is<UnboxedPlainObject>())
     460           0 :                     checkForDuplicates = true;
     461             : 
     462           0 :                 if (checkForDuplicates) {
     463           0 :                     if (!EnumerateExtraProperties<true>(cx, pobj, flags, ht, props))
     464             :                         return false;
     465             :                 } else {
     466         147 :                     if (!EnumerateExtraProperties<false>(cx, pobj, flags, ht, props))
     467             :                         return false;
     468             :                 }
     469             : 
     470           0 :                 if (pobj->isNative()) {
     471           0 :                     if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props,
     472             :                                                    checkForDuplicates))
     473             :                     {
     474             :                         return false;
     475             :                     }
     476             :                 }
     477             :             }
     478       13376 :         } else if (pobj->isNative()) {
     479             :             // Give the object a chance to resolve all lazy properties
     480           0 :             if (JSEnumerateOp enumerate = pobj->getClass()->getEnumerate()) {
     481          14 :                 if (!enumerate(cx, pobj.as<NativeObject>()))
     482             :                     return false;
     483             :             }
     484        6496 :             if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props,
     485             :                                            checkForDuplicates))
     486             :             {
     487             :                 return false;
     488             :             }
     489           0 :         } else if (pobj->is<ProxyObject>()) {
     490           0 :             if (checkForDuplicates) {
     491           0 :                 if (!EnumerateProxyProperties<true>(cx, pobj, flags, ht, props))
     492             :                     return false;
     493             :             } else {
     494         192 :                 if (!EnumerateProxyProperties<false>(cx, pobj, flags, ht, props))
     495             :                     return false;
     496             :             }
     497             :         } else {
     498           0 :             MOZ_CRASH("non-native objects must have an enumerate op");
     499             :         }
     500             : 
     501        6868 :         if (flags & JSITER_OWNONLY)
     502             :             break;
     503             : 
     504         390 :         if (!GetPrototype(cx, pobj, &pobj))
     505             :             return false;
     506             : 
     507             :     } while (pobj != nullptr);
     508             : 
     509             : #ifdef JS_MORE_DETERMINISTIC
     510             : 
     511             :     /*
     512             :      * In some cases the enumeration order for an object depends on the
     513             :      * execution mode (interpreter vs. JIT), especially for native objects
     514             :      * with a class enumerate hook (where resolving a property changes the
     515             :      * resulting enumeration order). These aren't really bugs, but the
     516             :      * differences can change the generated output and confuse correctness
     517             :      * fuzzers, so we sort the ids if such a fuzzer is running.
     518             :      *
     519             :      * We don't do this in the general case because (a) doing so is slow,
     520             :      * and (b) it also breaks the web, which expects enumeration order to
     521             :      * follow the order in which properties are added, in certain cases.
     522             :      * Since ECMA does not specify an enumeration order for objects, both
     523             :      * behaviors are technically correct to do.
     524             :      */
     525             : 
     526             :     jsid* ids = props->begin();
     527             :     size_t n = props->length();
     528             : 
     529             :     AutoIdVector tmp(cx);
     530             :     if (!tmp.resize(n))
     531             :         return false;
     532             :     PodCopy(tmp.begin(), ids, n);
     533             : 
     534             :     if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
     535             :         return false;
     536             : 
     537             : #endif /* JS_MORE_DETERMINISTIC */
     538             : 
     539             :     return true;
     540             : }
     541             : 
     542             : JS_FRIEND_API(bool)
     543        6663 : js::GetPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector* props)
     544             : {
     545        6663 :     return Snapshot(cx, obj,
     546             :                     flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY),
     547        6673 :                     props);
     548             : }
     549             : 
     550             : static inline void
     551         171 : RegisterEnumerator(ObjectRealm& realm, NativeIterator* ni)
     552             : {
     553             :     /* Register non-escaping native enumerators (for-in) with the current context. */
     554         171 :     ni->link(realm.enumerators);
     555             : 
     556           0 :     MOZ_ASSERT(!ni->isActive());
     557           0 :     ni->markActive();
     558         171 : }
     559             : 
     560             : static PropertyIteratorObject*
     561         124 : NewPropertyIteratorObject(JSContext* cx)
     562             : {
     563           0 :     RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_,
     564           0 :                                                              TaggedProto(nullptr)));
     565         124 :     if (!group)
     566             :         return nullptr;
     567             : 
     568           0 :     const Class* clasp = &PropertyIteratorObject::class_;
     569           0 :     RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr),
     570           0 :                                                       ITERATOR_FINALIZE_KIND));
     571         124 :     if (!shape)
     572             :         return nullptr;
     573             : 
     574             :     JSObject* obj;
     575         372 :     JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::create(cx, ITERATOR_FINALIZE_KIND,
     576             :                                                             GetInitialHeap(GenericObject, clasp),
     577             :                                                             shape, group));
     578             : 
     579         124 :     PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
     580             : 
     581             :     // CodeGenerator::visitIteratorStartO assumes the iterator object is not
     582             :     // inside the nursery when deciding whether a barrier is necessary.
     583         124 :     MOZ_ASSERT(!js::gc::IsInsideNursery(res));
     584             : 
     585         248 :     MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
     586             :     return res;
     587             : }
     588             : 
     589             : static PropertyIteratorObject*
     590         124 : CreatePropertyIterator(JSContext* cx, Handle<JSObject*> objBeingIterated,
     591             :                        const AutoIdVector& props, uint32_t numGuards, uint32_t guardKey)
     592             : {
     593           0 :     Rooted<PropertyIteratorObject*> propIter(cx, NewPropertyIteratorObject(cx));
     594         124 :     if (!propIter)
     595             :         return nullptr;
     596             : 
     597             :     static_assert(sizeof(ReceiverGuard) == 2 * sizeof(GCPtrFlatString),
     598             :                   "NativeIterators are allocated in space for 1) themselves, "
     599             :                   "2) the properties a NativeIterator iterates (as "
     600             :                   "GCPtrFlatStrings), and 3) |numGuards| HeapReceiverGuard "
     601             :                   "objects; the additional-length calculation below assumes "
     602             :                   "this size-relationship when determining the extra space to "
     603             :                   "allocate");
     604             : 
     605         124 :     size_t extraCount = props.length() + numGuards * 2;
     606             :     void* mem =
     607           0 :         cx->zone()->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraCount);
     608           0 :     if (!mem) {
     609           0 :         ReportOutOfMemory(cx);
     610           0 :         return nullptr;
     611             :     }
     612             : 
     613             :     // This also registers |ni| with |propIter|.
     614         124 :     bool hadError = false;
     615             :     NativeIterator* ni =
     616             :         new (mem) NativeIterator(cx, propIter, objBeingIterated, props, numGuards, guardKey,
     617           0 :                                  &hadError);
     618         124 :     if (hadError)
     619             :         return nullptr;
     620             : 
     621             :     ObjectRealm& realm =
     622           0 :         objBeingIterated ? ObjectRealm::get(objBeingIterated) : ObjectRealm::get(propIter);
     623         124 :     RegisterEnumerator(realm, ni);
     624             : 
     625         124 :     return propIter;
     626             : }
     627             : 
     628             : /**
     629             :  * Initialize a sentinel NativeIterator whose purpose is only to act as the
     630             :  * start/end of the circular linked list of NativeIterators in
     631             :  * ObjectRealm::enumerators.
     632             :  */
     633          94 : NativeIterator::NativeIterator()
     634             : {
     635             :     // Do our best to enforce that nothing in |this| except the two fields set
     636             :     // below is ever observed.
     637          47 :     JS_POISON(static_cast<void*>(this), 0xCC, sizeof(*this), MemCheckKind::MakeUndefined);
     638             : 
     639             :     // These are the only two fields in sentinel NativeIterators that are
     640             :     // examined, in ObjectRealm::sweepNativeIterators.  Everything else is
     641             :     // only examined *if* it's a NativeIterator being traced by a
     642             :     // PropertyIteratorObject that owns it, and nothing owns this iterator.
     643           1 :     prev_ = next_ = this;
     644           0 : }
     645             : 
     646             : NativeIterator*
     647          47 : NativeIterator::allocateSentinel(JSContext* maybecx)
     648             : {
     649           0 :     NativeIterator* ni = js_new<NativeIterator>();
     650           0 :     if (!ni) {
     651           0 :         if (maybecx)
     652           0 :             ReportOutOfMemory(maybecx);
     653             :     }
     654             : 
     655          47 :     return ni;
     656             : }
     657             : 
     658             : /**
     659             :  * Initialize a fresh NativeIterator.
     660             :  *
     661             :  * This definition is a bit tricky: some parts of initializing are fallible, so
     662             :  * as we initialize, we must carefully keep this in GC-safe state (see
     663             :  * NativeIterator::trace).
     664             :  */
     665         124 : NativeIterator::NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
     666             :                                Handle<JSObject*> objBeingIterated, const AutoIdVector& props,
     667         124 :                                uint32_t numGuards, uint32_t guardKey, bool* hadError)
     668             :   : objectBeingIterated_(objBeingIterated),
     669             :     iterObj_(propIter),
     670             :     // NativeIterator initially acts (before full initialization) as if it
     671             :     // contains no guards...
     672         124 :     guardsEnd_(guardsBegin()),
     673             :     // ...and no properties.
     674         124 :     propertyCursor_(reinterpret_cast<GCPtrFlatString*>(guardsBegin() + numGuards)),
     675             :     propertiesEnd_(propertyCursor_),
     676             :     guardKey_(guardKey),
     677         620 :     flags_(0)
     678             : {
     679         124 :     MOZ_ASSERT(!*hadError);
     680             : 
     681             :     // NOTE: This must be done first thing: PropertyIteratorObject::finalize
     682             :     //       can only free |this| (and not leak it) if this has happened.
     683         248 :     propIter->setNativeIterator(this);
     684             : 
     685           0 :     for (size_t i = 0, len = props.length(); i < len; i++) {
     686           0 :         JSFlatString* str = IdToString(cx, props[i]);
     687           0 :         if (!str) {
     688           0 :             *hadError = true;
     689           0 :             return;
     690             :         }
     691             : 
     692             :         // Placement-new the next property string at the end of the currently
     693             :         // computed property strings.
     694         746 :         GCPtrFlatString* loc = propertiesEnd_;
     695             : 
     696             :         // Increase the overall property string count before initializing the
     697             :         // property string, so this construction isn't on a location not known
     698             :         // to the GC yet.
     699         746 :         propertiesEnd_++;
     700             : 
     701        1492 :         new (loc) GCPtrFlatString(str);
     702             :     }
     703             : 
     704         124 :     if (numGuards > 0) {
     705             :         // Construct guards into the guard array.  Also recompute the guard key,
     706             :         // which incorporates Shape* and ObjectGroup* addresses that could have
     707             :         // changed during a GC triggered in (among other places) |IdToString|
     708             :         //. above.
     709          94 :         JSObject* pobj = objBeingIterated;
     710             : #ifdef DEBUG
     711          94 :         uint32_t i = 0;
     712             : #endif
     713          94 :         uint32_t key = 0;
     714             :         do {
     715         192 :             ReceiverGuard guard(pobj);
     716             : 
     717             :             // Placement-new the next HeapReceiverGuard at the end of the
     718             :             // currently initialized HeapReceiverGuards.
     719         192 :             HeapReceiverGuard* loc = guardsEnd_;
     720             : 
     721             :             // Increase the overall guard-count before initializing the
     722             :             // HeapReceiverGuard, so this construction isn't on a location not
     723             :             // known to the GC.
     724         192 :             guardsEnd_++;
     725             : #ifdef DEBUG
     726         192 :             i++;
     727             : #endif
     728             : 
     729         384 :             new (loc) HeapReceiverGuard(guard);
     730             : 
     731         384 :             key = mozilla::AddToHash(key, guard.hash());
     732             : 
     733             :             // The one caller of this method that passes |numGuards > 0|, does
     734             :             // so only if the entire chain consists of cacheable objects (that
     735             :             // necessarily have static prototypes).
     736           0 :             pobj = pobj->staticPrototype();
     737         192 :         } while (pobj);
     738             : 
     739           0 :         guardKey_ = key;
     740          94 :         MOZ_ASSERT(i == numGuards);
     741             :     }
     742             : 
     743           0 :     MOZ_ASSERT(static_cast<void*>(guardsEnd_) == propertyCursor_);
     744         124 :     markInitialized();
     745             : 
     746         124 :     MOZ_ASSERT(!*hadError);
     747             : }
     748             : 
     749             : static inline PropertyIteratorObject*
     750         119 : VectorToKeyIterator(JSContext* cx, HandleObject obj, AutoIdVector& props, uint32_t numGuards)
     751             : {
     752         270 :     if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
     753             :         return nullptr;
     754         119 :     MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
     755             : 
     756         119 :     return CreatePropertyIterator(cx, obj, props, numGuards, 0);
     757             : }
     758             : 
     759             : 
     760             : JSObject*
     761          24 : js::EnumeratedIdVectorToIterator(JSContext* cx, HandleObject obj, AutoIdVector& props)
     762             : {
     763          24 :     return VectorToKeyIterator(cx, obj, props, 0);
     764             : }
     765             : 
     766             : // Mainly used for .. in over null/undefined
     767             : JSObject*
     768           5 : js::NewEmptyPropertyIterator(JSContext* cx)
     769             : {
     770           0 :     AutoIdVector props(cx); // Empty
     771          10 :     return CreatePropertyIterator(cx, nullptr, props, 0, 0);
     772             : }
     773             : 
     774             : /* static */ bool
     775          78 : IteratorHashPolicy::match(PropertyIteratorObject* obj, const Lookup& lookup)
     776             : {
     777           0 :     NativeIterator* ni = obj->getNativeIterator();
     778         156 :     if (ni->guardKey() != lookup.key || ni->guardCount() != lookup.numGuards)
     779             :         return false;
     780             : 
     781           0 :     return PodEqual(reinterpret_cast<ReceiverGuard*>(ni->guardsBegin()), lookup.guards,
     782         156 :                     ni->guardCount());
     783             : }
     784             : 
     785             : static inline bool
     786         465 : CanCompareIterableObjectToCache(JSObject* obj)
     787             : {
     788           0 :     if (obj->isNative())
     789           0 :         return obj->as<NativeObject>().hasEmptyElements();
     790           0 :     if (obj->is<UnboxedPlainObject>()) {
     791           0 :         if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
     792           0 :             return expando->hasEmptyElements();
     793             :         return true;
     794             :     }
     795             :     return false;
     796             : }
     797             : 
     798             : using ReceiverGuardVector = Vector<ReceiverGuard, 8>;
     799             : 
     800             : static MOZ_ALWAYS_INLINE PropertyIteratorObject*
     801         247 : LookupInIteratorCache(JSContext* cx, JSObject* obj, uint32_t* numGuards)
     802             : {
     803         247 :     MOZ_ASSERT(*numGuards == 0);
     804             : 
     805           0 :     ReceiverGuardVector guards(cx);
     806           0 :     uint32_t key = 0;
     807         247 :     JSObject* pobj = obj;
     808             :     do {
     809           0 :         if (!CanCompareIterableObjectToCache(pobj))
     810          44 :             return nullptr;
     811             : 
     812           0 :         ReceiverGuard guard(pobj);
     813         842 :         key = mozilla::AddToHash(key, guard.hash());
     814             : 
     815           1 :         if (MOZ_UNLIKELY(!guards.append(guard))) {
     816           0 :             cx->recoverFromOutOfMemory();
     817           0 :             return nullptr;
     818             :         }
     819             : 
     820           0 :         pobj = pobj->staticPrototype();
     821         421 :     } while (pobj);
     822             : 
     823           0 :     MOZ_ASSERT(!guards.empty());
     824         203 :     *numGuards = guards.length();
     825             : 
     826           0 :     IteratorHashPolicy::Lookup lookup(guards.begin(), guards.length(), key);
     827           0 :     auto p = ObjectRealm::get(obj).iteratorCache.lookup(lookup);
     828         203 :     if (!p)
     829             :         return nullptr;
     830             : 
     831           0 :     PropertyIteratorObject* iterobj = *p;
     832         228 :     MOZ_ASSERT(iterobj->compartment() == cx->compartment());
     833             : 
     834           0 :     NativeIterator* ni = iterobj->getNativeIterator();
     835          76 :     if (!ni->isReusable())
     836             :         return nullptr;
     837             : 
     838          75 :     return iterobj;
     839             : }
     840             : 
     841             : static bool
     842         189 : CanStoreInIteratorCache(JSObject* obj)
     843             : {
     844             :     do {
     845           0 :         if (obj->isNative()) {
     846         770 :             MOZ_ASSERT(obj->as<NativeObject>().hasEmptyElements());
     847             : 
     848             :             // Typed arrays have indexed properties not captured by the Shape guard.
     849             :             // Enumerate hooks may add extra properties.
     850           0 :             const Class* clasp = obj->getClass();
     851         385 :             if (MOZ_UNLIKELY(IsTypedArrayClass(clasp)))
     852             :                 return false;
     853         770 :             if (MOZ_UNLIKELY(clasp->getNewEnumerate() || clasp->getEnumerate()))
     854             :                 return false;
     855             :         } else {
     856           0 :             MOZ_ASSERT(obj->is<UnboxedPlainObject>());
     857             :         }
     858             : 
     859           0 :         obj = obj->staticPrototype();
     860         384 :     } while (obj);
     861             : 
     862             :     return true;
     863             : }
     864             : 
     865             : static MOZ_MUST_USE bool
     866          94 : StoreInIteratorCache(JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj)
     867             : {
     868          94 :     MOZ_ASSERT(CanStoreInIteratorCache(obj));
     869             : 
     870           0 :     NativeIterator* ni = iterobj->getNativeIterator();
     871          94 :     MOZ_ASSERT(ni->guardCount() > 0);
     872             : 
     873           0 :     IteratorHashPolicy::Lookup lookup(reinterpret_cast<ReceiverGuard*>(ni->guardsBegin()),
     874           0 :                                       ni->guardCount(),
     875         282 :                                       ni->guardKey());
     876             : 
     877          94 :     ObjectRealm::IteratorCache& cache = ObjectRealm::get(obj).iteratorCache;
     878             :     bool ok;
     879           0 :     auto p = cache.lookupForAdd(lookup);
     880           0 :     if (MOZ_LIKELY(!p)) {
     881          92 :         ok = cache.add(p, iterobj);
     882             :     } else {
     883             :         // If we weren't able to use an existing cached iterator, just
     884             :         // replace it.
     885           0 :         cache.remove(p);
     886           2 :         ok = cache.relookupOrAdd(p, lookup, iterobj);
     887             :     }
     888           1 :     if (!ok) {
     889           0 :         ReportOutOfMemory(cx);
     890           0 :         return false;
     891             :     }
     892             : 
     893             :     return true;
     894             : }
     895             : 
     896             : JSObject*
     897         166 : js::GetIterator(JSContext* cx, HandleObject obj)
     898             : {
     899           0 :     uint32_t numGuards = 0;
     900           0 :     if (PropertyIteratorObject* iterobj = LookupInIteratorCache(cx, obj, &numGuards)) {
     901           0 :         NativeIterator* ni = iterobj->getNativeIterator();
     902           0 :         ni->changeObjectBeingIterated(*obj);
     903           0 :         RegisterEnumerator(ObjectRealm::get(obj), ni);
     904          47 :         return iterobj;
     905             :     }
     906             : 
     907           0 :     if (numGuards > 0 && !CanStoreInIteratorCache(obj))
     908           1 :         numGuards = 0;
     909             : 
     910         238 :     MOZ_ASSERT(!obj->is<PropertyIteratorObject>());
     911             : 
     912           0 :     if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
     913          24 :         return Proxy::enumerate(cx, obj);
     914             : 
     915           0 :     AutoIdVector keys(cx);
     916          95 :     if (!Snapshot(cx, obj, 0, &keys))
     917             :         return nullptr;
     918             : 
     919           0 :     JSObject* res = VectorToKeyIterator(cx, obj, keys, numGuards);
     920          95 :     if (!res)
     921             :         return nullptr;
     922             : 
     923           0 :     PropertyIteratorObject* iterobj = &res->as<PropertyIteratorObject>();
     924          95 :     assertSameCompartment(cx, iterobj);
     925             : 
     926             :     // Cache the iterator object.
     927           0 :     if (numGuards > 0) {
     928         188 :         if (!StoreInIteratorCache(cx, obj, iterobj))
     929             :             return nullptr;
     930             :     }
     931             : 
     932          95 :     return iterobj;
     933             : }
     934             : 
     935             : PropertyIteratorObject*
     936          81 : js::LookupInIteratorCache(JSContext* cx, HandleObject obj)
     937             : {
     938           0 :     uint32_t numGuards = 0;
     939          81 :     return LookupInIteratorCache(cx, obj, &numGuards);
     940             : }
     941             : 
     942             : // ES 2017 draft 7.4.7.
     943             : JSObject*
     944           0 : js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
     945             : {
     946             :     // Step 1 (implicit).
     947             : 
     948             :     // Step 2.
     949           0 :     RootedObject templateObject(cx, cx->realm()->getOrCreateIterResultTemplateObject(cx));
     950           0 :     if (!templateObject)
     951             :         return nullptr;
     952             : 
     953             :     NativeObject* resultObj;
     954           0 :     JS_TRY_VAR_OR_RETURN_NULL(cx, resultObj, NativeObject::createWithTemplate(cx, gc::DefaultHeap,
     955             :                                                                               templateObject));
     956             : 
     957             :     // Step 3.
     958           0 :     resultObj->setSlot(Realm::IterResultObjectValueSlot, value);
     959             : 
     960             :     // Step 4.
     961           0 :     resultObj->setSlot(Realm::IterResultObjectDoneSlot,
     962           0 :                        done ? TrueHandleValue : FalseHandleValue);
     963             : 
     964             :     // Step 5.
     965           0 :     return resultObj;
     966             : }
     967             : 
     968             : NativeObject*
     969           0 : Realm::getOrCreateIterResultTemplateObject(JSContext* cx)
     970             : {
     971           0 :     MOZ_ASSERT(cx->realm() == this);
     972             : 
     973           0 :     if (iterResultTemplate_)
     974           0 :         return iterResultTemplate_;
     975             : 
     976             :     // Create template plain object
     977           0 :     RootedNativeObject templateObject(cx, NewBuiltinClassInstance<PlainObject>(cx, TenuredObject));
     978           0 :     if (!templateObject)
     979           0 :         return iterResultTemplate_; // = nullptr
     980             : 
     981             :     // Create a new group for the template.
     982           0 :     Rooted<TaggedProto> proto(cx, templateObject->taggedProto());
     983           0 :     RootedObjectGroup group(cx, ObjectGroupRealm::makeGroup(cx, templateObject->getClass(),
     984           0 :                                                             proto));
     985           0 :     if (!group)
     986           0 :         return iterResultTemplate_; // = nullptr
     987           0 :     templateObject->setGroup(group);
     988             : 
     989             :     // Set dummy `value` property
     990           0 :     if (!NativeDefineDataProperty(cx, templateObject, cx->names().value, UndefinedHandleValue,
     991             :                                   JSPROP_ENUMERATE))
     992             :     {
     993           0 :         return iterResultTemplate_; // = nullptr
     994             :     }
     995             : 
     996             :     // Set dummy `done` property
     997           0 :     if (!NativeDefineDataProperty(cx, templateObject, cx->names().done, TrueHandleValue,
     998             :                                   JSPROP_ENUMERATE))
     999             :     {
    1000           0 :         return iterResultTemplate_; // = nullptr
    1001             :     }
    1002             : 
    1003           0 :     AutoSweepObjectGroup sweep(group);
    1004           0 :     if (!group->unknownProperties(sweep)) {
    1005             :         // Update `value` property typeset, since it can be any value.
    1006           0 :         HeapTypeSet* types = group->maybeGetProperty(sweep, NameToId(cx->names().value));
    1007           0 :         MOZ_ASSERT(types);
    1008             :         {
    1009           0 :             AutoEnterAnalysis enter(cx);
    1010           0 :             types->makeUnknown(sweep, cx);
    1011             :         }
    1012             :     }
    1013             : 
    1014             :     // Make sure that the properties are in the right slots.
    1015           0 :     DebugOnly<Shape*> shape = templateObject->lastProperty();
    1016           0 :     MOZ_ASSERT(shape->previous()->slot() == Realm::IterResultObjectValueSlot &&
    1017             :                shape->previous()->propidRef() == NameToId(cx->names().value));
    1018           0 :     MOZ_ASSERT(shape->slot() == Realm::IterResultObjectDoneSlot &&
    1019             :                shape->propidRef() == NameToId(cx->names().done));
    1020             : 
    1021           0 :     iterResultTemplate_.set(templateObject);
    1022             : 
    1023           0 :     return iterResultTemplate_;
    1024             : }
    1025             : 
    1026             : /*** Iterator objects ****************************************************************************/
    1027             : 
    1028             : bool
    1029           0 : js::IsPropertyIterator(HandleValue v)
    1030             : {
    1031           0 :     return v.isObject() && v.toObject().is<PropertyIteratorObject>();
    1032             : }
    1033             : 
    1034             : size_t
    1035           0 : PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
    1036             : {
    1037           0 :     return mallocSizeOf(getPrivate());
    1038             : }
    1039             : 
    1040             : void
    1041           3 : PropertyIteratorObject::trace(JSTracer* trc, JSObject* obj)
    1042             : {
    1043           0 :     if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
    1044           0 :         ni->trace(trc);
    1045           3 : }
    1046             : 
    1047             : void
    1048           0 : PropertyIteratorObject::finalize(FreeOp* fop, JSObject* obj)
    1049             : {
    1050           0 :     if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
    1051           0 :         fop->free_(ni);
    1052           0 : }
    1053             : 
    1054             : const ClassOps PropertyIteratorObject::classOps_ = {
    1055             :     nullptr, /* addProperty */
    1056             :     nullptr, /* delProperty */
    1057             :     nullptr, /* enumerate */
    1058             :     nullptr, /* newEnumerate */
    1059             :     nullptr, /* resolve */
    1060             :     nullptr, /* mayResolve */
    1061             :     finalize,
    1062             :     nullptr, /* call        */
    1063             :     nullptr, /* hasInstance */
    1064             :     nullptr, /* construct   */
    1065             :     trace
    1066             : };
    1067             : 
    1068             : const Class PropertyIteratorObject::class_ = {
    1069             :     "Iterator",
    1070             :     JSCLASS_HAS_PRIVATE |
    1071             :     JSCLASS_BACKGROUND_FINALIZE,
    1072             :     &PropertyIteratorObject::classOps_
    1073             : };
    1074             : 
    1075             : static const Class ArrayIteratorPrototypeClass = {
    1076             :     "Array Iterator",
    1077             :     0
    1078             : };
    1079             : 
    1080             : enum {
    1081             :     ArrayIteratorSlotIteratedObject,
    1082             :     ArrayIteratorSlotNextIndex,
    1083             :     ArrayIteratorSlotItemKind,
    1084             :     ArrayIteratorSlotCount
    1085             : };
    1086             : 
    1087             : const Class ArrayIteratorObject::class_ = {
    1088             :     "Array Iterator",
    1089             :     JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount)
    1090             : };
    1091             : 
    1092             : 
    1093             : ArrayIteratorObject*
    1094        1596 : js::NewArrayIteratorObject(JSContext* cx, NewObjectKind newKind)
    1095             : {
    1096           0 :     RootedObject proto(cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
    1097        1596 :     if (!proto)
    1098             :         return nullptr;
    1099             : 
    1100        3192 :     return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto, newKind);
    1101             : }
    1102             : 
    1103             : static const JSFunctionSpec array_iterator_methods[] = {
    1104             :     JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
    1105             :     JS_FS_END
    1106             : };
    1107             : 
    1108             : static const Class StringIteratorPrototypeClass = {
    1109             :     "String Iterator",
    1110             :     0
    1111             : };
    1112             : 
    1113             : enum {
    1114             :     StringIteratorSlotIteratedObject,
    1115             :     StringIteratorSlotNextIndex,
    1116             :     StringIteratorSlotCount
    1117             : };
    1118             : 
    1119             : const Class StringIteratorObject::class_ = {
    1120             :     "String Iterator",
    1121             :     JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount)
    1122             : };
    1123             : 
    1124             : static const JSFunctionSpec string_iterator_methods[] = {
    1125             :     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
    1126             :     JS_FS_END
    1127             : };
    1128             : 
    1129             : StringIteratorObject*
    1130           0 : js::NewStringIteratorObject(JSContext* cx, NewObjectKind newKind)
    1131             : {
    1132           0 :     RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
    1133           0 :     if (!proto)
    1134             :         return nullptr;
    1135             : 
    1136           0 :     return NewObjectWithGivenProto<StringIteratorObject>(cx, proto, newKind);
    1137             : }
    1138             : 
    1139             : JSObject*
    1140         147 : js::ValueToIterator(JSContext* cx, HandleValue vp)
    1141             : {
    1142           0 :     RootedObject obj(cx);
    1143         147 :     if (vp.isObject()) {
    1144             :         /* Common case. */
    1145           0 :         obj = &vp.toObject();
    1146           5 :     } else if (vp.isNullOrUndefined()) {
    1147             :         /*
    1148             :          * Enumerating over null and undefined gives an empty enumerator, so
    1149             :          * that |for (var p in <null or undefined>) <loop>;| never executes
    1150             :          * <loop>, per ES5 12.6.4.
    1151             :          */
    1152           5 :         return NewEmptyPropertyIterator(cx);
    1153             :     } else {
    1154           0 :         obj = ToObject(cx, vp);
    1155           0 :         if (!obj)
    1156             :             return nullptr;
    1157             :     }
    1158             : 
    1159         142 :     return GetIterator(cx, obj);
    1160             : }
    1161             : 
    1162             : void
    1163         273 : js::CloseIterator(JSObject* obj)
    1164             : {
    1165         273 :     if (obj->is<PropertyIteratorObject>()) {
    1166             :         /* Remove enumerators from the active list, which is a stack. */
    1167         546 :         NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
    1168             : 
    1169         273 :         ni->unlink();
    1170             : 
    1171           0 :         MOZ_ASSERT(ni->isActive());
    1172         273 :         ni->markInactive();
    1173             : 
    1174             :         // Reset the enumerator; it may still be in the cached iterators for
    1175             :         // this thread and can be reused.
    1176         273 :         ni->resetPropertyCursorForReuse();
    1177             :     }
    1178         273 : }
    1179             : 
    1180             : bool
    1181           0 : js::IteratorCloseForException(JSContext* cx, HandleObject obj)
    1182             : {
    1183           0 :     MOZ_ASSERT(cx->isExceptionPending());
    1184             : 
    1185           0 :     bool isClosingGenerator = cx->isClosingGenerator();
    1186           0 :     JS::AutoSaveExceptionState savedExc(cx);
    1187             : 
    1188             :     // Implements IteratorClose (ES 7.4.6) for exception unwinding. See
    1189             :     // also the bytecode generated by BytecodeEmitter::emitIteratorClose.
    1190             : 
    1191             :     // Step 3.
    1192             :     //
    1193             :     // Get the "return" method.
    1194           0 :     RootedValue returnMethod(cx);
    1195           0 :     if (!GetProperty(cx, obj, obj, cx->names().return_, &returnMethod))
    1196             :         return false;
    1197             : 
    1198             :     // Step 4.
    1199             :     //
    1200             :     // Do nothing if "return" is null or undefined. Throw a TypeError if the
    1201             :     // method is not IsCallable.
    1202           0 :     if (returnMethod.isNullOrUndefined())
    1203             :         return true;
    1204           0 :     if (!IsCallable(returnMethod))
    1205           0 :         return ReportIsNotFunction(cx, returnMethod);
    1206             : 
    1207             :     // Step 5, 6, 8.
    1208             :     //
    1209             :     // Call "return" if it is not null or undefined.
    1210           0 :     RootedValue rval(cx);
    1211           0 :     bool ok = Call(cx, returnMethod, obj, &rval);
    1212           0 :     if (isClosingGenerator) {
    1213             :         // Closing an iterator is implemented as an exception, but in spec
    1214             :         // terms it is a Completion value with [[Type]] return. In this case
    1215             :         // we *do* care if the call threw and if it returned an object.
    1216           0 :         if (!ok)
    1217             :             return false;
    1218           0 :         if (!rval.isObject())
    1219           0 :             return ThrowCheckIsObject(cx, CheckIsObjectKind::IteratorReturn);
    1220             :     } else {
    1221             :         // We don't care if the call threw or that it returned an Object, as
    1222             :         // Step 6 says if IteratorClose is being called during a throw, the
    1223             :         // original throw has primacy.
    1224           0 :         savedExc.restore();
    1225             :     }
    1226             : 
    1227             :     return true;
    1228             : }
    1229             : 
    1230             : void
    1231           0 : js::UnwindIteratorForUncatchableException(JSObject* obj)
    1232             : {
    1233           0 :     if (obj->is<PropertyIteratorObject>()) {
    1234           0 :         NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
    1235           0 :         ni->unlink();
    1236             :     }
    1237           0 : }
    1238             : 
    1239             : static bool
    1240           1 : SuppressDeletedProperty(JSContext* cx, NativeIterator* ni, HandleObject obj,
    1241             :                         Handle<JSFlatString*> str)
    1242             : {
    1243           2 :     if (ni->objectBeingIterated() != obj)
    1244             :         return true;
    1245             : 
    1246             :     // Optimization for the following common case:
    1247             :     //
    1248             :     //    for (var p in o) {
    1249             :     //        delete o[p];
    1250             :     //    }
    1251             :     //
    1252             :     // Note that usually both strings will be atoms so we only check for pointer
    1253             :     // equality here.
    1254           1 :     if (ni->previousPropertyWas(str))
    1255             :         return true;
    1256             : 
    1257             :     while (true) {
    1258           0 :         bool restart = false;
    1259             : 
    1260             :         // Check whether id is still to come.
    1261           0 :         GCPtrFlatString* const cursor = ni->nextProperty();
    1262           0 :         GCPtrFlatString* const end = ni->propertiesEnd();
    1263           0 :         for (GCPtrFlatString* idp = cursor; idp < end; ++idp) {
    1264             :             // Common case: both strings are atoms.
    1265           0 :             if ((*idp)->isAtom() && str->isAtom()) {
    1266           0 :                 if (*idp != str)
    1267           0 :                     continue;
    1268             :             } else {
    1269           0 :                 if (!EqualStrings(*idp, str))
    1270             :                     continue;
    1271             :             }
    1272             : 
    1273             :             // Check whether another property along the prototype chain became
    1274             :             // visible as a result of this deletion.
    1275           0 :             RootedObject proto(cx);
    1276           0 :             if (!GetPrototype(cx, obj, &proto))
    1277           0 :                 return false;
    1278           0 :             if (proto) {
    1279           0 :                 RootedId id(cx);
    1280           0 :                 RootedValue idv(cx, StringValue(*idp));
    1281           0 :                 if (!ValueToId<CanGC>(cx, idv, &id))
    1282           0 :                     return false;
    1283             : 
    1284           0 :                 Rooted<PropertyDescriptor> desc(cx);
    1285           0 :                 if (!GetPropertyDescriptor(cx, proto, id, &desc))
    1286           0 :                     return false;
    1287             : 
    1288           0 :                 if (desc.object() && desc.enumerable())
    1289           0 :                     continue;
    1290             :             }
    1291             : 
    1292             :             // If GetPropertyDescriptor above removed a property from ni, start
    1293             :             // over.
    1294           0 :             if (end != ni->propertiesEnd() || cursor != ni->nextProperty()) {
    1295           0 :                 restart = true;
    1296           0 :                 break;
    1297             :             }
    1298             : 
    1299             :             // No property along the prototype chain stepped in to take the
    1300             :             // property's place, so go ahead and delete id from the list.
    1301             :             // If it is the next property to be enumerated, just skip it.
    1302           0 :             if (idp == cursor) {
    1303           0 :                 ni->incCursor();
    1304             :             } else {
    1305           0 :                 for (GCPtrFlatString* p = idp; p + 1 != end; p++)
    1306           0 :                     *p = *(p + 1);
    1307             : 
    1308           0 :                 ni->trimLastProperty();
    1309             :             }
    1310             : 
    1311           0 :             ni->markHasUnvisitedPropertyDeletion();
    1312           0 :             return true;
    1313             :         }
    1314             : 
    1315           0 :         if (!restart)
    1316             :             return true;
    1317             :     }
    1318             : }
    1319             : 
    1320             : /*
    1321             :  * Suppress enumeration of deleted properties. This function must be called
    1322             :  * when a property is deleted and there might be active enumerators.
    1323             :  *
    1324             :  * We maintain a list of active non-escaping for-in enumerators. To suppress
    1325             :  * a property, we check whether each active enumerator contains the (obj, id)
    1326             :  * pair and has not yet enumerated |id|. If so, and |id| is the next property,
    1327             :  * we simply advance the cursor. Otherwise, we delete |id| from the list.
    1328             :  *
    1329             :  * We do not suppress enumeration of a property deleted along an object's
    1330             :  * prototype chain. Only direct deletions on the object are handled.
    1331             :  */
    1332             : static bool
    1333           1 : SuppressDeletedPropertyHelper(JSContext* cx, HandleObject obj, Handle<JSFlatString*> str)
    1334             : {
    1335           0 :     NativeIterator* enumeratorList = ObjectRealm::get(obj).enumerators;
    1336           1 :     NativeIterator* ni = enumeratorList->next();
    1337             : 
    1338           0 :     while (ni != enumeratorList) {
    1339           1 :         if (!SuppressDeletedProperty(cx, ni, obj, str))
    1340             :             return false;
    1341           1 :         ni = ni->next();
    1342             :     }
    1343             : 
    1344             :     return true;
    1345             : }
    1346             : 
    1347             : bool
    1348         133 : js::SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id)
    1349             : {
    1350         399 :     if (MOZ_LIKELY(!ObjectRealm::get(obj).objectMaybeInIteration(obj)))
    1351             :         return true;
    1352             : 
    1353           1 :     if (JSID_IS_SYMBOL(id))
    1354             :         return true;
    1355             : 
    1356           0 :     Rooted<JSFlatString*> str(cx, IdToString(cx, id));
    1357           1 :     if (!str)
    1358             :         return false;
    1359           1 :     return SuppressDeletedPropertyHelper(cx, obj, str);
    1360             : }
    1361             : 
    1362             : bool
    1363         104 : js::SuppressDeletedElement(JSContext* cx, HandleObject obj, uint32_t index)
    1364             : {
    1365         312 :     if (MOZ_LIKELY(!ObjectRealm::get(obj).objectMaybeInIteration(obj)))
    1366             :         return true;
    1367             : 
    1368           0 :     RootedId id(cx);
    1369           0 :     if (!IndexToId(cx, index, &id))
    1370             :         return false;
    1371             : 
    1372           0 :     Rooted<JSFlatString*> str(cx, IdToString(cx, id));
    1373           0 :     if (!str)
    1374             :         return false;
    1375           0 :     return SuppressDeletedPropertyHelper(cx, obj, str);
    1376             : }
    1377             : 
    1378             : bool
    1379         195 : js::IteratorMore(JSContext* cx, HandleObject iterobj, MutableHandleValue rval)
    1380             : {
    1381             :     // Fast path for native iterators.
    1382           0 :     if (MOZ_LIKELY(iterobj->is<PropertyIteratorObject>())) {
    1383           0 :         NativeIterator* ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
    1384           0 :         rval.set(ni->nextIteratedValueAndAdvance());
    1385         195 :         return true;
    1386             :     }
    1387             : 
    1388           0 :     if (JS_IsDeadWrapper(iterobj)) {
    1389           0 :         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEAD_OBJECT);
    1390           0 :         return false;
    1391             :     }
    1392             : 
    1393           0 :     MOZ_ASSERT(IsWrapper(iterobj));
    1394             : 
    1395           0 :     RootedObject obj(cx, CheckedUnwrap(iterobj));
    1396           0 :     if (!obj)
    1397             :         return false;
    1398             : 
    1399           0 :     MOZ_RELEASE_ASSERT(obj->is<PropertyIteratorObject>());
    1400             :     {
    1401           0 :         AutoRealm ar(cx, obj);
    1402           0 :         NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
    1403           0 :         rval.set(ni->nextIteratedValueAndAdvance());
    1404             :     }
    1405           0 :     return cx->compartment()->wrap(cx, rval);
    1406             : }
    1407             : 
    1408             : static const JSFunctionSpec iterator_proto_methods[] = {
    1409             :     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
    1410             :     JS_FS_END
    1411             : };
    1412             : 
    1413             : /* static */ bool
    1414          18 : GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
    1415             : {
    1416          18 :     if (global->getReservedSlot(ITERATOR_PROTO).isObject())
    1417             :         return true;
    1418             : 
    1419           0 :     RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
    1420          54 :     if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods))
    1421             :         return false;
    1422             : 
    1423           0 :     global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
    1424          18 :     return true;
    1425             : }
    1426             : 
    1427             : /* static */ bool
    1428          16 : GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
    1429             : {
    1430          16 :     if (global->getReservedSlot(ARRAY_ITERATOR_PROTO).isObject())
    1431             :         return true;
    1432             : 
    1433           0 :     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
    1434          16 :     if (!iteratorProto)
    1435             :         return false;
    1436             : 
    1437           0 :     const Class* cls = &ArrayIteratorPrototypeClass;
    1438           0 :     RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, cls, iteratorProto));
    1439           0 :     if (!proto ||
    1440           0 :         !DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods) ||
    1441          64 :         !DefineToStringTag(cx, proto, cx->names().ArrayIterator))
    1442             :     {
    1443             :         return false;
    1444             :     }
    1445             : 
    1446           0 :     global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
    1447          16 :     return true;
    1448             : }
    1449             : 
    1450             : /* static */ bool
    1451           0 : GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
    1452             : {
    1453           0 :     if (global->getReservedSlot(STRING_ITERATOR_PROTO).isObject())
    1454             :         return true;
    1455             : 
    1456           0 :     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
    1457           0 :     if (!iteratorProto)
    1458             :         return false;
    1459             : 
    1460           0 :     const Class* cls = &StringIteratorPrototypeClass;
    1461           0 :     RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, cls, iteratorProto));
    1462           0 :     if (!proto ||
    1463           0 :         !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods) ||
    1464           0 :         !DefineToStringTag(cx, proto, cx->names().StringIterator))
    1465             :     {
    1466             :         return false;
    1467             :     }
    1468             : 
    1469           0 :     global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
    1470             :     return true;
    1471             : }

Generated by: LCOV version 1.13-14-ga5dd952