LCOV - code coverage report
Current view: top level - js/src - jsiter.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 447 780 57.3 %
Date: 2017-07-14 16:53:18 Functions: 43 63 68.3 %
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 "jsiter.h"
      10             : 
      11             : #include "mozilla/ArrayUtils.h"
      12             : #include "mozilla/Maybe.h"
      13             : #include "mozilla/MemoryReporting.h"
      14             : #include "mozilla/PodOperations.h"
      15             : #include "mozilla/Unused.h"
      16             : 
      17             : #include "jsarray.h"
      18             : #include "jsatom.h"
      19             : #include "jscntxt.h"
      20             : #include "jsgc.h"
      21             : #include "jsobj.h"
      22             : #include "jsopcode.h"
      23             : #include "jsscript.h"
      24             : #include "jstypes.h"
      25             : #include "jsutil.h"
      26             : 
      27             : #include "ds/Sort.h"
      28             : #include "gc/Marking.h"
      29             : #include "js/Proxy.h"
      30             : #include "vm/GeneratorObject.h"
      31             : #include "vm/GlobalObject.h"
      32             : #include "vm/Interpreter.h"
      33             : #include "vm/Shape.h"
      34             : #include "vm/StopIterationObject.h"
      35             : #include "vm/TypedArrayObject.h"
      36             : 
      37             : #include "jsscriptinlines.h"
      38             : 
      39             : #include "vm/NativeObject-inl.h"
      40             : #include "vm/ReceiverGuard-inl.h"
      41             : #include "vm/Stack-inl.h"
      42             : #include "vm/String-inl.h"
      43             : 
      44             : using namespace js;
      45             : using namespace js::gc;
      46             : using JS::ForOfIterator;
      47             : 
      48             : using mozilla::ArrayLength;
      49             : using mozilla::Maybe;
      50             : using mozilla::PodCopy;
      51             : using mozilla::PodZero;
      52             : 
      53             : typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
      54             : 
      55             : static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::AllocKind::OBJECT2_BACKGROUND;
      56             : 
      57             : void
      58           0 : NativeIterator::trace(JSTracer* trc)
      59             : {
      60           0 :     for (GCPtrFlatString* str = begin(); str < end(); str++)
      61           0 :         TraceNullableEdge(trc, str, "prop");
      62           0 :     TraceNullableEdge(trc, &obj, "obj");
      63             : 
      64           0 :     for (size_t i = 0; i < guard_length; i++)
      65           0 :         guard_array[i].trace(trc);
      66             : 
      67             :     // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
      68             :     // GC removes any elements from the list, it won't remove this one.
      69           0 :     if (iterObj_)
      70           0 :         TraceManuallyBarrieredEdge(trc, &iterObj_, "iterObj");
      71           0 : }
      72             : 
      73             : typedef HashSet<jsid, DefaultHasher<jsid>> IdSet;
      74             : 
      75             : static inline bool
      76           0 : NewKeyValuePair(JSContext* cx, jsid id, const Value& val, MutableHandleValue rval)
      77             : {
      78           0 :     return NewValuePair(cx, IdToValue(id), val, rval);
      79             : }
      80             : 
      81             : template <bool CheckForDuplicates>
      82             : static inline bool
      83        6437 : Enumerate(JSContext* cx, HandleObject pobj, jsid id,
      84             :           bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
      85             : {
      86             :     if (CheckForDuplicates) {
      87        3981 :         if (!ht) {
      88          66 :             ht.emplace(cx);
      89             :             // Most of the time there are only a handful of entries.
      90          66 :             if (!ht->init(5))
      91          21 :                 return false;
      92             :         }
      93             : 
      94             :         // If we've already seen this, we definitely won't add it.
      95        3981 :         IdSet::AddPtr p = ht->lookupForAdd(id);
      96        3981 :         if (MOZ_UNLIKELY(!!p))
      97          21 :             return true;
      98             : 
      99             :         // It's not necessary to add properties to the hash table at the end of
     100             :         // the prototype chain, but custom enumeration behaviors might return
     101             :         // duplicated properties, so always add in such cases.
     102       11880 :         if (pobj->is<ProxyObject>() ||
     103        5023 :             pobj->staticPrototype() ||
     104        1063 :             pobj->getClass()->getNewEnumerate())
     105             :         {
     106        2897 :             if (!ht->add(p, id))
     107           0 :                 return false;
     108             :         }
     109             :     }
     110             : 
     111        7374 :     if (!enumerable && !(flags & JSITER_HIDDEN))
     112         977 :         return true;
     113             : 
     114             :     // Symbol-keyed properties and nonenumerable properties are skipped unless
     115             :     // the caller specifically asks for them. A caller can also filter out
     116             :     // non-symbols by asking for JSITER_SYMBOLSONLY.
     117        5439 :     if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
     118          31 :         return true;
     119             : 
     120        5408 :     return props->append(id);
     121             : }
     122             : 
     123             : template <bool CheckForDuplicates>
     124             : static bool
     125           3 : EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe<IdSet>& ht,
     126             :                          AutoIdVector* props)
     127             : {
     128           3 :     MOZ_ASSERT(obj->getClass()->getNewEnumerate());
     129             : 
     130           6 :     AutoIdVector properties(cx);
     131           3 :     bool enumerableOnly = !(flags & JSITER_HIDDEN);
     132           3 :     if (!obj->getClass()->getNewEnumerate()(cx, obj, properties, enumerableOnly))
     133           0 :         return false;
     134             : 
     135           6 :     RootedId id(cx);
     136        2313 :     for (size_t n = 0; n < properties.length(); n++) {
     137        2310 :         id = properties[n];
     138             : 
     139             :         // The enumerate hook does not indicate whether the properties
     140             :         // it returns are enumerable or not. Since we already passed
     141             :         // `enumerableOnly` to the hook to filter out non-enumerable
     142             :         // properties, it doesn't really matter what we pass here.
     143        2310 :         bool enumerable = true;
     144        2310 :         if (!Enumerate<CheckForDuplicates>(cx, obj, id, enumerable, flags, ht, props))
     145           0 :             return false;
     146             :     }
     147             : 
     148           3 :     return true;
     149             : }
     150             : 
     151             : static bool
     152           0 : SortComparatorIntegerIds(jsid a, jsid b, bool* lessOrEqualp)
     153             : {
     154             :     uint32_t indexA, indexB;
     155           0 :     MOZ_ALWAYS_TRUE(IdIsIndex(a, &indexA));
     156           0 :     MOZ_ALWAYS_TRUE(IdIsIndex(b, &indexB));
     157           0 :     *lessOrEqualp = (indexA <= indexB);
     158           0 :     return true;
     159             : }
     160             : 
     161             : template <bool CheckForDuplicates>
     162             : static bool
     163         635 : EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
     164             :                           AutoIdVector* props, Handle<UnboxedPlainObject*> unboxed = nullptr)
     165             : {
     166             :     bool enumerateSymbols;
     167         635 :     if (flags & JSITER_SYMBOLSONLY) {
     168          54 :         enumerateSymbols = true;
     169             :     } else {
     170             :         /* Collect any dense elements from this object. */
     171         581 :         size_t firstElemIndex = props->length();
     172         581 :         size_t initlen = pobj->getDenseInitializedLength();
     173         581 :         const Value* vp = pobj->getDenseElements();
     174         581 :         bool hasHoles = false;
     175         617 :         for (size_t i = 0; i < initlen; ++i, ++vp) {
     176          36 :             if (vp->isMagic(JS_ELEMENTS_HOLE)) {
     177           0 :                 hasHoles = true;
     178             :             } else {
     179             :                 /* Dense arrays never get so large that i would not fit into an integer id. */
     180          36 :                 if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
     181             :                                                    /* enumerable = */ true, flags, ht, props))
     182             :                 {
     183           0 :                     return false;
     184             :                 }
     185             :             }
     186             :         }
     187             : 
     188             :         /* Collect any typed array or shared typed array elements from this object. */
     189         581 :         if (pobj->is<TypedArrayObject>()) {
     190           0 :             size_t len = pobj->as<TypedArrayObject>().length();
     191           0 :             for (size_t i = 0; i < len; i++) {
     192           0 :                 if (!Enumerate<CheckForDuplicates>(cx, pobj, INT_TO_JSID(i),
     193             :                                                    /* enumerable = */ true, flags, ht, props))
     194             :                 {
     195           0 :                     return false;
     196             :                 }
     197             :             }
     198             :         }
     199             : 
     200             :         // Collect any sparse elements from this object.
     201         581 :         bool isIndexed = pobj->isIndexed();
     202         581 :         if (isIndexed) {
     203             :             // If the dense elements didn't have holes, we don't need to include
     204             :             // them in the sort.
     205           0 :             if (!hasHoles)
     206           0 :                 firstElemIndex = props->length();
     207             : 
     208           0 :             for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
     209           0 :                 Shape& shape = r.front();
     210           0 :                 jsid id = shape.propid();
     211             :                 uint32_t dummy;
     212           0 :                 if (IdIsIndex(id, &dummy)) {
     213           0 :                     if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht,
     214             :                                                        props))
     215             :                     {
     216           0 :                         return false;
     217             :                     }
     218             :                 }
     219             :             }
     220             : 
     221           0 :             MOZ_ASSERT(firstElemIndex <= props->length());
     222             : 
     223           0 :             jsid* ids = props->begin() + firstElemIndex;
     224           0 :             size_t n = props->length() - firstElemIndex;
     225             : 
     226           0 :             AutoIdVector tmp(cx);
     227           0 :             if (!tmp.resize(n))
     228           0 :                 return false;
     229           0 :             PodCopy(tmp.begin(), ids, n);
     230             : 
     231           0 :             if (!MergeSort(ids, n, tmp.begin(), SortComparatorIntegerIds))
     232           0 :                 return false;
     233             :         }
     234             : 
     235         581 :         if (unboxed) {
     236             :             // If |unboxed| is set then |pobj| is the expando for an unboxed
     237             :             // plain object we are enumerating. Add the unboxed properties
     238             :             // themselves here since they are all property names that were
     239             :             // given to the object before any of the expando's properties.
     240           0 :             MOZ_ASSERT(pobj->is<UnboxedExpandoObject>());
     241           0 :             if (!EnumerateExtraProperties<CheckForDuplicates>(cx, unboxed, flags, ht, props))
     242           0 :                 return false;
     243             :         }
     244             : 
     245         581 :         size_t initialLength = props->length();
     246             : 
     247             :         /* Collect all unique property names from this object's shape. */
     248         581 :         bool symbolsFound = false;
     249         581 :         Shape::Range<NoGC> r(pobj->lastProperty());
     250        8145 :         for (; !r.empty(); r.popFront()) {
     251        3782 :             Shape& shape = r.front();
     252        3782 :             jsid id = shape.propid();
     253             : 
     254        3782 :             if (JSID_IS_SYMBOL(id)) {
     255           3 :                 symbolsFound = true;
     256           6 :                 continue;
     257             :             }
     258             : 
     259             :             uint32_t dummy;
     260        3779 :             if (isIndexed && IdIsIndex(id, &dummy))
     261           0 :                 continue;
     262             : 
     263        3779 :             if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht, props))
     264           0 :                 return false;
     265             :         }
     266         581 :         ::Reverse(props->begin() + initialLength, props->end());
     267             : 
     268         581 :         enumerateSymbols = symbolsFound && (flags & JSITER_SYMBOLS);
     269             :     }
     270             : 
     271         635 :     if (enumerateSymbols) {
     272             :         // Do a second pass to collect symbols. ES6 draft rev 25 (2014 May 22)
     273             :         // 9.1.12 requires that all symbols appear after all strings in the
     274             :         // result.
     275          55 :         size_t initialLength = props->length();
     276         213 :         for (Shape::Range<NoGC> r(pobj->lastProperty()); !r.empty(); r.popFront()) {
     277         158 :             Shape& shape = r.front();
     278         158 :             jsid id = shape.propid();
     279         158 :             if (JSID_IS_SYMBOL(id)) {
     280           1 :                 if (!Enumerate<CheckForDuplicates>(cx, pobj, id, shape.enumerable(), flags, ht,
     281             :                                                    props))
     282             :                 {
     283           0 :                     return false;
     284             :                 }
     285             :             }
     286             :         }
     287          55 :         ::Reverse(props->begin() + initialLength, props->end());
     288             :     }
     289             : 
     290         635 :     return true;
     291             : }
     292             : 
     293             : static bool
     294         635 : EnumerateNativeProperties(JSContext* cx, HandleNativeObject pobj, unsigned flags, Maybe<IdSet>& ht,
     295             :                           AutoIdVector* props, bool checkForDuplicates,
     296             :                           Handle<UnboxedPlainObject*> unboxed = nullptr)
     297             : {
     298         635 :     if (checkForDuplicates)
     299         136 :         return EnumerateNativeProperties<true>(cx, pobj, flags, ht, props, unboxed);
     300         499 :     return EnumerateNativeProperties<false>(cx, pobj, flags, ht, props, unboxed);
     301             : }
     302             : 
     303             : template <bool CheckForDuplicates>
     304             : static bool
     305          59 : EnumerateProxyProperties(JSContext* cx, HandleObject pobj, unsigned flags, Maybe<IdSet>& ht,
     306             :                          AutoIdVector* props)
     307             : {
     308          59 :     MOZ_ASSERT(pobj->is<ProxyObject>());
     309             : 
     310         118 :     AutoIdVector proxyProps(cx);
     311             : 
     312          59 :     if (flags & JSITER_HIDDEN || flags & JSITER_SYMBOLS) {
     313             :         // This gets all property keys, both strings and symbols. The call to
     314             :         // Enumerate in the loop below will filter out unwanted keys, per the
     315             :         // flags.
     316          10 :         if (!Proxy::ownPropertyKeys(cx, pobj, proxyProps))
     317           0 :             return false;
     318             : 
     319          20 :         Rooted<PropertyDescriptor> desc(cx);
     320         145 :         for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
     321         135 :             bool enumerable = false;
     322             : 
     323             :             // We need to filter, if the caller just wants enumerable symbols.
     324         135 :             if (!(flags & JSITER_HIDDEN)) {
     325           0 :                 if (!Proxy::getOwnPropertyDescriptor(cx, pobj, proxyProps[n], &desc))
     326           0 :                     return false;
     327           0 :                 enumerable = desc.enumerable();
     328             :             }
     329             : 
     330         135 :             if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], enumerable, flags, ht,
     331             :                                                props))
     332             :             {
     333           0 :                 return false;
     334             :             }
     335             :         }
     336             : 
     337          10 :         return true;
     338             :     }
     339             : 
     340             :     // Returns enumerable property names (no symbols).
     341          49 :     if (!Proxy::getOwnEnumerablePropertyKeys(cx, pobj, proxyProps))
     342           0 :         return false;
     343             : 
     344         225 :     for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
     345         176 :         if (!Enumerate<CheckForDuplicates>(cx, pobj, proxyProps[n], true, flags, ht, props))
     346           0 :             return false;
     347             :     }
     348             : 
     349          49 :     return true;
     350             : }
     351             : 
     352             : #ifdef JS_MORE_DETERMINISTIC
     353             : 
     354             : struct SortComparatorIds
     355             : {
     356             :     JSContext*  const cx;
     357             : 
     358             :     SortComparatorIds(JSContext* cx)
     359             :       : cx(cx) {}
     360             : 
     361             :     bool operator()(jsid a, jsid b, bool* lessOrEqualp)
     362             :     {
     363             :         // Pick an arbitrary order on jsids that is as stable as possible
     364             :         // across executions.
     365             :         if (a == b) {
     366             :             *lessOrEqualp = true;
     367             :             return true;
     368             :         }
     369             : 
     370             :         size_t ta = JSID_BITS(a) & JSID_TYPE_MASK;
     371             :         size_t tb = JSID_BITS(b) & JSID_TYPE_MASK;
     372             :         if (ta != tb) {
     373             :             *lessOrEqualp = (ta <= tb);
     374             :             return true;
     375             :         }
     376             : 
     377             :         if (JSID_IS_INT(a)) {
     378             :             *lessOrEqualp = (JSID_TO_INT(a) <= JSID_TO_INT(b));
     379             :             return true;
     380             :         }
     381             : 
     382             :         RootedString astr(cx), bstr(cx);
     383             :         if (JSID_IS_SYMBOL(a)) {
     384             :             MOZ_ASSERT(JSID_IS_SYMBOL(b));
     385             :             JS::SymbolCode ca = JSID_TO_SYMBOL(a)->code();
     386             :             JS::SymbolCode cb = JSID_TO_SYMBOL(b)->code();
     387             :             if (ca != cb) {
     388             :                 *lessOrEqualp = uint32_t(ca) <= uint32_t(cb);
     389             :                 return true;
     390             :             }
     391             :             MOZ_ASSERT(ca == JS::SymbolCode::InSymbolRegistry || ca == JS::SymbolCode::UniqueSymbol);
     392             :             astr = JSID_TO_SYMBOL(a)->description();
     393             :             bstr = JSID_TO_SYMBOL(b)->description();
     394             :             if (!astr || !bstr) {
     395             :                 *lessOrEqualp = !astr;
     396             :                 return true;
     397             :             }
     398             : 
     399             :             // Fall through to string comparison on the descriptions. The sort
     400             :             // order is nondeterministic if two different unique symbols have
     401             :             // the same description.
     402             :         } else {
     403             :             astr = IdToString(cx, a);
     404             :             if (!astr)
     405             :                 return false;
     406             :             bstr = IdToString(cx, b);
     407             :             if (!bstr)
     408             :                 return false;
     409             :         }
     410             : 
     411             :         int32_t result;
     412             :         if (!CompareStrings(cx, astr, bstr, &result))
     413             :             return false;
     414             : 
     415             :         *lessOrEqualp = (result <= 0);
     416             :         return true;
     417             :     }
     418             : };
     419             : 
     420             : #endif /* JS_MORE_DETERMINISTIC */
     421             : 
     422             : static bool
     423         624 : Snapshot(JSContext* cx, HandleObject pobj_, unsigned flags, AutoIdVector* props)
     424             : {
     425             :     // We initialize |ht| lazily (in Enumerate()) because it ends up unused
     426             :     // anywhere from 67--99.9% of the time.
     427        1248 :     Maybe<IdSet> ht;
     428        1248 :     RootedObject pobj(cx, pobj_);
     429             : 
     430             :     // Don't check for duplicates if we're only interested in own properties.
     431             :     // This does the right thing for most objects: native objects don't have
     432             :     // duplicate property ids and we allow the [[OwnPropertyKeys]] proxy trap to
     433             :     // return duplicates.
     434             :     //
     435             :     // The only special case is when the object has a newEnumerate hook: it
     436             :     // can return duplicate properties and we have to filter them. This is
     437             :     // handled below.
     438         624 :     bool checkForDuplicates = !(flags & JSITER_OWNONLY);
     439             : 
     440         133 :     do {
     441         694 :         if (pobj->getClass()->getNewEnumerate()) {
     442           3 :             if (pobj->is<UnboxedPlainObject>() && pobj->as<UnboxedPlainObject>().maybeExpando()) {
     443             :                 // Special case unboxed objects with an expando object.
     444           0 :                 RootedNativeObject expando(cx, pobj->as<UnboxedPlainObject>().maybeExpando());
     445           0 :                 if (!EnumerateNativeProperties(cx, expando, flags, ht, props, checkForDuplicates,
     446             :                                                pobj.as<UnboxedPlainObject>()))
     447             :                 {
     448           0 :                     return false;
     449             :                 }
     450             :             } else {
     451             :                 // The newEnumerate hook may return duplicates. Whitelist the
     452             :                 // unboxed object hooks because we know they are well-behaved.
     453           3 :                 if (!pobj->is<UnboxedPlainObject>() && !pobj->is<UnboxedArrayObject>())
     454           3 :                     checkForDuplicates = true;
     455             : 
     456           3 :                 if (checkForDuplicates) {
     457           3 :                     if (!EnumerateExtraProperties<true>(cx, pobj, flags, ht, props))
     458           0 :                         return false;
     459             :                 } else {
     460           0 :                     if (!EnumerateExtraProperties<false>(cx, pobj, flags, ht, props))
     461           0 :                         return false;
     462             :                 }
     463             : 
     464           3 :                 if (pobj->isNative()) {
     465           3 :                     if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props,
     466             :                                                    checkForDuplicates))
     467             :                     {
     468           0 :                         return false;
     469             :                     }
     470             :                 }
     471             :             }
     472         691 :         } else if (pobj->isNative()) {
     473             :             // Give the object a chance to resolve all lazy properties
     474         632 :             if (JSEnumerateOp enumerate = pobj->getClass()->getEnumerate()) {
     475           8 :                 if (!enumerate(cx, pobj.as<NativeObject>()))
     476           0 :                     return false;
     477             :             }
     478         632 :             if (!EnumerateNativeProperties(cx, pobj.as<NativeObject>(), flags, ht, props,
     479             :                                            checkForDuplicates))
     480             :             {
     481           0 :                 return false;
     482             :             }
     483          59 :         } else if (pobj->is<ProxyObject>()) {
     484          59 :             if (checkForDuplicates) {
     485           0 :                 if (!EnumerateProxyProperties<true>(cx, pobj, flags, ht, props))
     486           0 :                     return false;
     487             :             } else {
     488          59 :                 if (!EnumerateProxyProperties<false>(cx, pobj, flags, ht, props))
     489           0 :                     return false;
     490             :             }
     491             :         } else {
     492           0 :             MOZ_CRASH("non-native objects must have an enumerate op");
     493             :         }
     494             : 
     495         694 :         if (flags & JSITER_OWNONLY)
     496         561 :             break;
     497             : 
     498         133 :         if (!GetPrototype(cx, pobj, &pobj))
     499           0 :             return false;
     500             : 
     501             :     } while (pobj != nullptr);
     502             : 
     503             : #ifdef JS_MORE_DETERMINISTIC
     504             : 
     505             :     /*
     506             :      * In some cases the enumeration order for an object depends on the
     507             :      * execution mode (interpreter vs. JIT), especially for native objects
     508             :      * with a class enumerate hook (where resolving a property changes the
     509             :      * resulting enumeration order). These aren't really bugs, but the
     510             :      * differences can change the generated output and confuse correctness
     511             :      * fuzzers, so we sort the ids if such a fuzzer is running.
     512             :      *
     513             :      * We don't do this in the general case because (a) doing so is slow,
     514             :      * and (b) it also breaks the web, which expects enumeration order to
     515             :      * follow the order in which properties are added, in certain cases.
     516             :      * Since ECMA does not specify an enumeration order for objects, both
     517             :      * behaviors are technically correct to do.
     518             :      */
     519             : 
     520             :     jsid* ids = props->begin();
     521             :     size_t n = props->length();
     522             : 
     523             :     AutoIdVector tmp(cx);
     524             :     if (!tmp.resize(n))
     525             :         return false;
     526             :     PodCopy(tmp.begin(), ids, n);
     527             : 
     528             :     if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
     529             :         return false;
     530             : 
     531             : #endif /* JS_MORE_DETERMINISTIC */
     532             : 
     533         624 :     return true;
     534             : }
     535             : 
     536             : JS_FRIEND_API(bool)
     537         561 : js::GetPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector* props)
     538             : {
     539         561 :     return Snapshot(cx, obj,
     540             :                     flags & (JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS | JSITER_SYMBOLSONLY),
     541         561 :                     props);
     542             : }
     543             : 
     544             : size_t sCustomIteratorCount = 0;
     545             : 
     546             : static inline bool
     547          63 : GetCustomIterator(JSContext* cx, HandleObject obj, unsigned flags, MutableHandleObject objp)
     548             : {
     549          63 :     if (MOZ_UNLIKELY(!CheckRecursionLimit(cx)))
     550           0 :         return false;
     551             : 
     552         126 :     RootedValue rval(cx);
     553             :     /* Check whether we have a valid __iterator__ method. */
     554          63 :     HandlePropertyName name = cx->names().iteratorIntrinsic;
     555          63 :     if (!GetProperty(cx, obj, obj, name, &rval))
     556           0 :         return false;
     557             : 
     558             :     /* If there is no custom __iterator__ method, we are done here. */
     559          63 :     if (MOZ_LIKELY(!rval.isObject())) {
     560          63 :         objp.set(nullptr);
     561          63 :         return true;
     562             :     }
     563             : 
     564           0 :     if (!cx->runningWithTrustedPrincipals())
     565           0 :         ++sCustomIteratorCount;
     566             : 
     567             :     /* Otherwise call it and return that object. */
     568             :     {
     569           0 :         FixedInvokeArgs<1> args(cx);
     570             : 
     571           0 :         args[0].setBoolean((flags & JSITER_FOREACH) == 0);
     572             : 
     573           0 :         RootedValue thisv(cx, ObjectValue(*obj));
     574           0 :         if (!js::Call(cx, rval, thisv, args, &rval))
     575           0 :             return false;
     576             :     }
     577             : 
     578           0 :     if (rval.isPrimitive()) {
     579             :         // Ignore the stack when throwing. We can't tell whether we were
     580             :         // supposed to skip over a new.target or not.
     581           0 :         JSAutoByteString bytes;
     582           0 :         if (!AtomToPrintableString(cx, name, &bytes))
     583           0 :             return false;
     584           0 :         RootedValue val(cx, ObjectValue(*obj));
     585           0 :         ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
     586           0 :                           JSDVG_IGNORE_STACK, val, nullptr, bytes.ptr());
     587           0 :         return false;
     588             :     }
     589           0 :     objp.set(&rval.toObject());
     590           0 :     return true;
     591             : }
     592             : 
     593             : template <typename T>
     594             : static inline bool
     595          30 : Compare(T* a, T* b, size_t c)
     596             : {
     597          30 :     size_t n = (c + size_t(7)) / size_t(8);
     598          30 :     switch (c % 8) {
     599          30 :       case 0: do { if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     600           0 :       case 7:      if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     601           0 :       case 6:      if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     602           0 :       case 5:      if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     603           0 :       case 4:      if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     604           4 :       case 3:      if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     605          30 :       case 2:      if (*a++ != *b++) return false; MOZ_FALLTHROUGH;
     606          30 :       case 1:      if (*a++ != *b++) return false;
     607             :               } while (--n > 0);
     608             :     }
     609          30 :     return true;
     610             : }
     611             : 
     612             : static bool legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp);
     613             : 
     614             : static inline PropertyIteratorObject*
     615          63 : NewPropertyIteratorObject(JSContext* cx, unsigned flags)
     616             : {
     617          63 :     if (flags & JSITER_ENUMERATE) {
     618         116 :         RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_,
     619         232 :                                                                  TaggedProto(nullptr)));
     620          58 :         if (!group)
     621           0 :             return nullptr;
     622             : 
     623          58 :         const Class* clasp = &PropertyIteratorObject::class_;
     624         116 :         RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, TaggedProto(nullptr),
     625         116 :                                                           ITERATOR_FINALIZE_KIND));
     626          58 :         if (!shape)
     627           0 :             return nullptr;
     628             : 
     629             :         JSObject* obj;
     630          58 :         JS_TRY_VAR_OR_RETURN_NULL(cx, obj, NativeObject::create(cx, ITERATOR_FINALIZE_KIND,
     631             :                                                                 GetInitialHeap(GenericObject, clasp),
     632             :                                                                 shape, group));
     633             : 
     634          58 :         PropertyIteratorObject* res = &obj->as<PropertyIteratorObject>();
     635             : 
     636             :         // CodeGenerator::visitIteratorStartO assumes the iterator object is not
     637             :         // inside the nursery when deciding whether a barrier is necessary.
     638          58 :         MOZ_ASSERT(!js::gc::IsInsideNursery(res));
     639             : 
     640          58 :         MOZ_ASSERT(res->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
     641          58 :         return res;
     642             :     }
     643             : 
     644          10 :     Rooted<PropertyIteratorObject*> res(cx, NewBuiltinClassInstance<PropertyIteratorObject>(cx));
     645           5 :     if (!res)
     646           0 :         return nullptr;
     647             : 
     648           5 :     if (flags == 0) {
     649             :         // Redefine next as an own property. This ensure that deleting the
     650             :         // next method on the prototype doesn't break cross-global for .. in.
     651             :         // We don't have to do this for JSITER_ENUMERATE because that object always
     652             :         // takes an optimized path.
     653          10 :         RootedFunction next(cx, NewNativeFunction(cx, legacy_iterator_next, 0,
     654          15 :                                                   HandlePropertyName(cx->names().next)));
     655           5 :         if (!next)
     656           0 :             return nullptr;
     657             : 
     658          10 :         RootedValue value(cx, ObjectValue(*next));
     659           5 :         if (!DefineProperty(cx, res, cx->names().next, value))
     660           0 :             return nullptr;
     661             :     }
     662             : 
     663           5 :     return res;
     664             : }
     665             : 
     666             : NativeIterator*
     667          91 : NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, uint32_t plength)
     668             : {
     669             :     JS_STATIC_ASSERT(sizeof(ReceiverGuard) == 2 * sizeof(void*));
     670             : 
     671          91 :     size_t extraLength = plength + numGuards * 2;
     672          91 :     NativeIterator* ni = cx->zone()->pod_malloc_with_extra<NativeIterator, void*>(extraLength);
     673          91 :     if (!ni) {
     674           0 :         ReportOutOfMemory(cx);
     675           0 :         return nullptr;
     676             :     }
     677             : 
     678          91 :     void** extra = reinterpret_cast<void**>(ni + 1);
     679          91 :     PodZero(ni);
     680          91 :     PodZero(extra, extraLength);
     681          91 :     ni->props_array = ni->props_cursor = reinterpret_cast<GCPtrFlatString*>(extra);
     682          91 :     ni->props_end = ni->props_array + plength;
     683          91 :     return ni;
     684             : }
     685             : 
     686             : NativeIterator*
     687         315 : NativeIterator::allocateSentinel(JSContext* maybecx)
     688             : {
     689         315 :     NativeIterator* ni = js_pod_malloc<NativeIterator>();
     690         315 :     if (!ni) {
     691           0 :         if (maybecx)
     692           0 :             ReportOutOfMemory(maybecx);
     693           0 :         return nullptr;
     694             :     }
     695             : 
     696         315 :     PodZero(ni);
     697             : 
     698         315 :     ni->next_ = ni;
     699         315 :     ni->prev_ = ni;
     700         315 :     return ni;
     701             : }
     702             : 
     703             : inline void
     704          91 : NativeIterator::init(JSObject* obj, JSObject* iterObj, unsigned flags, uint32_t numGuards, uint32_t key)
     705             : {
     706          91 :     this->obj.init(obj);
     707          91 :     this->iterObj_ = iterObj;
     708          91 :     this->flags = flags;
     709          91 :     this->guard_array = (HeapReceiverGuard*) this->props_end;
     710          91 :     this->guard_length = numGuards;
     711          91 :     this->guard_key = key;
     712          91 : }
     713             : 
     714             : bool
     715          63 : NativeIterator::initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj,
     716             :                                const AutoIdVector& props)
     717             : {
     718             :     // The obj parameter is just so that we can ensure that this object will get
     719             :     // traced if we GC.
     720          63 :     MOZ_ASSERT(this == obj->getNativeIterator());
     721             : 
     722          63 :     size_t plength = props.length();
     723          63 :     MOZ_ASSERT(plength == size_t(end() - begin()));
     724             : 
     725         755 :     for (size_t i = 0; i < plength; i++) {
     726         692 :         JSFlatString* str = IdToString(cx, props[i]);
     727         692 :         if (!str)
     728           0 :             return false;
     729         692 :         props_array[i].init(str);
     730             :     }
     731             : 
     732          63 :     return true;
     733             : }
     734             : 
     735             : static inline void
     736          98 : RegisterEnumerator(JSContext* cx, PropertyIteratorObject* iterobj, NativeIterator* ni)
     737             : {
     738             :     /* Register non-escaping native enumerators (for-in) with the current context. */
     739          98 :     if (ni->flags & JSITER_ENUMERATE) {
     740          93 :         ni->link(cx->compartment()->enumerators);
     741             : 
     742          93 :         MOZ_ASSERT(!(ni->flags & JSITER_ACTIVE));
     743          93 :         ni->flags |= JSITER_ACTIVE;
     744             :     }
     745          98 : }
     746             : 
     747             : static inline PropertyIteratorObject*
     748          63 : VectorToKeyIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector& keys,
     749             :                     uint32_t numGuards, uint32_t key)
     750             : {
     751          63 :     MOZ_ASSERT(!(flags & JSITER_FOREACH));
     752             : 
     753          63 :     if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
     754           0 :         return nullptr;
     755          63 :     MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
     756             : 
     757         126 :     Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     758          63 :     if (!iterobj)
     759           0 :         return nullptr;
     760             : 
     761          63 :     NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys.length());
     762          63 :     if (!ni)
     763           0 :         return nullptr;
     764             : 
     765          63 :     iterobj->setNativeIterator(ni);
     766          63 :     ni->init(obj, iterobj, flags, numGuards, key);
     767          63 :     if (!ni->initProperties(cx, iterobj, keys))
     768           0 :         return nullptr;
     769             : 
     770          63 :     if (numGuards) {
     771             :         // Fill in the guard array from scratch.
     772          56 :         JSObject* pobj = obj;
     773          56 :         size_t ind = 0;
     774          61 :         do {
     775         117 :             ni->guard_array[ind++].init(ReceiverGuard(pobj));
     776             : 
     777             :             // The one caller of this method that passes |numGuards > 0|, does
     778             :             // so only if the entire chain consists of cacheable objects (that
     779             :             // necessarily have static prototypes).
     780         117 :             pobj = pobj->staticPrototype();
     781         117 :         } while (pobj);
     782          56 :         MOZ_ASSERT(ind == numGuards);
     783             :     }
     784             : 
     785          63 :     RegisterEnumerator(cx, iterobj, ni);
     786          63 :     return iterobj;
     787             : }
     788             : 
     789             : static PropertyIteratorObject*
     790           0 : VectorToValueIterator(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVector& keys)
     791             : {
     792           0 :     MOZ_ASSERT(flags & JSITER_FOREACH);
     793             : 
     794           0 :     if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
     795           0 :         return nullptr;
     796           0 :     MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
     797             : 
     798           0 :     Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     799           0 :     if (!iterobj)
     800           0 :         return nullptr;
     801             : 
     802           0 :     NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
     803           0 :     if (!ni)
     804           0 :         return nullptr;
     805             : 
     806           0 :     iterobj->setNativeIterator(ni);
     807           0 :     ni->init(obj, iterobj, flags, 0, 0);
     808           0 :     if (!ni->initProperties(cx, iterobj, keys))
     809           0 :         return nullptr;
     810             : 
     811           0 :     RegisterEnumerator(cx, iterobj, ni);
     812           0 :     return iterobj;
     813             : }
     814             : 
     815             : JSObject*
     816           0 : js::EnumeratedIdVectorToIterator(JSContext* cx, HandleObject obj, unsigned flags,
     817             :                                  AutoIdVector& props)
     818             : {
     819           0 :     if (!(flags & JSITER_FOREACH))
     820           0 :         return VectorToKeyIterator(cx, obj, flags, props, 0, 0);
     821             : 
     822           0 :     return VectorToValueIterator(cx, obj, flags, props);
     823             : }
     824             : 
     825             : // Mainly used for .. in over null/undefined
     826             : JSObject*
     827           0 : js::NewEmptyPropertyIterator(JSContext* cx, unsigned flags)
     828             : {
     829           0 :     Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
     830           0 :     if (!iterobj)
     831           0 :         return nullptr;
     832             : 
     833           0 :     AutoIdVector keys(cx); // Empty
     834           0 :     NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
     835           0 :     if (!ni)
     836           0 :         return nullptr;
     837             : 
     838           0 :     iterobj->setNativeIterator(ni);
     839           0 :     ni->init(nullptr, iterobj, flags, 0, 0);
     840           0 :     if (!ni->initProperties(cx, iterobj, keys))
     841           0 :         return nullptr;
     842             : 
     843           0 :     RegisterEnumerator(cx, iterobj, ni);
     844           0 :     return iterobj;
     845             : }
     846             : 
     847             : static inline void
     848          35 : UpdateNativeIterator(NativeIterator* ni, JSObject* obj)
     849             : {
     850             :     // Update the object for which the native iterator is associated, so
     851             :     // SuppressDeletedPropertyHelper will recognize the iterator as a match.
     852          35 :     ni->obj = obj;
     853          35 : }
     854             : 
     855             : static inline bool
     856         257 : CanCompareIterableObjectToCache(JSObject* obj)
     857             : {
     858         257 :     if (obj->isNative())
     859         252 :         return obj->as<NativeObject>().hasEmptyElements();
     860           5 :     if (obj->is<UnboxedPlainObject>()) {
     861           0 :         if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando())
     862           0 :             return expando->hasEmptyElements();
     863           0 :         return true;
     864             :     }
     865           5 :     return false;
     866             : }
     867             : 
     868             : static MOZ_ALWAYS_INLINE void
     869          86 : UpdateLastCachedNativeIterator(JSContext* cx, JSObject* obj, PropertyIteratorObject* iterobj)
     870             : {
     871             :     // lastCachedNativeIterator is only used when there's a single object on
     872             :     // the prototype chain, to simplify JIT code.
     873          86 :     if (iterobj->getNativeIterator()->guard_length != 2)
     874          11 :         return;
     875             : 
     876             :     // Both GetIterator and JIT code assume the receiver has a non-null proto,
     877             :     // so we have to make sure a Shape change is triggered when the proto
     878             :     // changes. Note that this does not apply to the object on the proto chain
     879             :     // because we always check it has a null proto.
     880          75 :     if (obj->hasUncacheableProto())
     881           0 :         return;
     882             : 
     883          75 :     cx->compartment()->lastCachedNativeIterator = iterobj;
     884             : }
     885             : 
     886             : using ReceiverGuardVector = Vector<ReceiverGuard, 8>;
     887             : 
     888             : static MOZ_ALWAYS_INLINE PropertyIteratorObject*
     889          93 : LookupInIteratorCache(JSContext* cx, JSObject* obj, ReceiverGuardVector& guards, uint32_t* keyArg)
     890             : {
     891          93 :     MOZ_ASSERT(guards.empty());
     892             : 
     893             :     // The iterator object for JSITER_ENUMERATE never escapes, so we don't
     894             :     // care that the "proper" prototype is set.  This also lets us reuse an
     895             :     // old, inactive iterator object.
     896             : 
     897          93 :     uint32_t key = 0;
     898          93 :     JSObject* pobj = obj;
     899          97 :     do {
     900         190 :         if (!CanCompareIterableObjectToCache(pobj)) {
     901           6 :             guards.clear();
     902          12 :             return nullptr;
     903             :         }
     904             : 
     905         184 :         ReceiverGuard guard(pobj);
     906         184 :         key = (key + (key << 16)) ^ guard.hash();
     907             : 
     908         184 :         if (MOZ_UNLIKELY(!guards.append(guard))) {
     909           0 :             cx->recoverFromOutOfMemory();
     910           0 :             guards.clear();
     911           0 :             return nullptr;
     912             :         }
     913             : 
     914         184 :         pobj = pobj->staticPrototype();
     915         184 :     } while (pobj);
     916             : 
     917          87 :     MOZ_ASSERT(!guards.empty());
     918          87 :     *keyArg = key;
     919             : 
     920          87 :     PropertyIteratorObject* iterobj = cx->caches().nativeIterCache.get(key);
     921          87 :     if (!iterobj)
     922          50 :         return nullptr;
     923             : 
     924          37 :     NativeIterator* ni = iterobj->getNativeIterator();
     925         109 :     if ((ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) ||
     926          65 :         ni->guard_key != key ||
     927          60 :         ni->guard_length != guards.length() ||
     928          30 :         !Compare(reinterpret_cast<ReceiverGuard*>(ni->guard_array),
     929          97 :                  guards.begin(), ni->guard_length) ||
     930          30 :         iterobj->compartment() != cx->compartment())
     931             :     {
     932           7 :         return nullptr;
     933             :     }
     934             : 
     935          30 :     UpdateNativeIterator(ni, obj);
     936          30 :     RegisterEnumerator(cx, iterobj, ni);
     937             : 
     938          30 :     UpdateLastCachedNativeIterator(cx, obj, iterobj);
     939             : 
     940          30 :     return iterobj;
     941             : }
     942             : 
     943             : static bool
     944         235 : CanStoreInIteratorCache(JSContext* cx, JSObject* obj)
     945             : {
     946         122 :     do {
     947         235 :         if (obj->isNative()) {
     948         235 :             MOZ_ASSERT(obj->as<NativeObject>().hasEmptyElements());
     949             : 
     950             :             // Typed arrays have indexed properties not captured by the Shape guard.
     951             :             // Enumerate hooks may add extra properties.
     952         235 :             const Class* clasp = obj->getClass();
     953         235 :             if (MOZ_UNLIKELY(IsTypedArrayClass(clasp)))
     954           0 :                 return false;
     955         235 :             if (MOZ_UNLIKELY(clasp->getNewEnumerate() || clasp->getEnumerate()))
     956           1 :                 return false;
     957             : 
     958         234 :             if (MOZ_UNLIKELY(obj->as<NativeObject>().containsPure(cx->names().iteratorIntrinsic)))
     959           0 :                 return false;
     960             :         } else {
     961           0 :             MOZ_ASSERT(obj->is<UnboxedPlainObject>());
     962             :         }
     963             : 
     964         234 :         obj = obj->staticPrototype();
     965         234 :     } while (obj);
     966             : 
     967         112 :     return true;
     968             : }
     969             : 
     970             : static void
     971          56 : StoreInIteratorCache(JSContext* cx, JSObject* obj, uint32_t key, PropertyIteratorObject* iterobj)
     972             : {
     973          56 :     MOZ_ASSERT(CanStoreInIteratorCache(cx, obj));
     974          56 :     MOZ_ASSERT(iterobj->getNativeIterator()->guard_length > 0);
     975             : 
     976          56 :     cx->caches().nativeIterCache.set(key, iterobj);
     977             : 
     978          56 :     UpdateLastCachedNativeIterator(cx, obj, iterobj);
     979          56 : }
     980             : 
     981             : JSObject*
     982         103 : js::GetIterator(JSContext* cx, HandleObject obj, unsigned flags)
     983             : {
     984         206 :     ReceiverGuardVector guards(cx);
     985         103 :     uint32_t key = 0;
     986         103 :     if (flags == JSITER_ENUMERATE) {
     987             :         // Check to see if this is the same as the most recent object which was
     988             :         // iterated over.
     989          98 :         if (PropertyIteratorObject* last = cx->compartment()->lastCachedNativeIterator) {
     990          64 :             NativeIterator* lastni = last->getNativeIterator();
     991         256 :             if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
     992         311 :                 CanCompareIterableObjectToCache(obj) &&
     993         308 :                 ReceiverGuard(obj) == lastni->guard_array[0])
     994             :             {
     995           6 :                 JSObject* proto = obj->staticPrototype();
     996          36 :                 if (CanCompareIterableObjectToCache(proto) &&
     997          41 :                     ReceiverGuard(proto) == lastni->guard_array[1] &&
     998           5 :                     !proto->staticPrototype())
     999             :                 {
    1000           5 :                     assertSameCompartment(cx, last);
    1001           5 :                     UpdateNativeIterator(lastni, obj);
    1002           5 :                     RegisterEnumerator(cx, last, lastni);
    1003           5 :                     return last;
    1004             :                 }
    1005             :             }
    1006             :         }
    1007             : 
    1008          93 :         if (PropertyIteratorObject* iterObj = LookupInIteratorCache(cx, obj, guards, &key))
    1009          30 :             return iterObj;
    1010             : 
    1011          63 :         if (!guards.empty() && !CanStoreInIteratorCache(cx, obj))
    1012           1 :             guards.clear();
    1013             :     }
    1014             : 
    1015          68 :     if (MOZ_UNLIKELY(obj->is<PropertyIteratorObject>() || obj->is<LegacyGeneratorObject>()))
    1016           0 :         return obj;
    1017             : 
    1018             :     // We should only call the enumerate trap for "for-in".
    1019             :     // Or when we call GetIterator from the Proxy [[Enumerate]] hook.
    1020             :     // JSITER_ENUMERATE is just an optimization and the same
    1021             :     // as flags == 0 otherwise.
    1022          68 :     if (flags == 0 || flags == JSITER_ENUMERATE) {
    1023          68 :         if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
    1024           5 :             return Proxy::enumerate(cx, obj);
    1025             :     }
    1026             : 
    1027         126 :     RootedObject res(cx);
    1028          63 :     if (!GetCustomIterator(cx, obj, flags, &res))
    1029           0 :         return nullptr;
    1030          63 :     if (res) {
    1031           0 :         assertSameCompartment(cx, res);
    1032           0 :         return res;
    1033             :     }
    1034             : 
    1035         126 :     AutoIdVector keys(cx);
    1036          63 :     if (!Snapshot(cx, obj, flags, &keys))
    1037           0 :         return nullptr;
    1038             : 
    1039          63 :     if (flags & JSITER_FOREACH) {
    1040           0 :         MOZ_ASSERT(guards.empty());
    1041           0 :         res = VectorToValueIterator(cx, obj, flags, keys);
    1042           0 :         if (!res)
    1043           0 :             return nullptr;
    1044             :     } else {
    1045          63 :         res = VectorToKeyIterator(cx, obj, flags, keys, guards.length(), key);
    1046          63 :         if (!res)
    1047           0 :             return nullptr;
    1048             :     }
    1049             : 
    1050          63 :     PropertyIteratorObject* iterobj = &res->as<PropertyIteratorObject>();
    1051          63 :     assertSameCompartment(cx, iterobj);
    1052             : 
    1053             :     // Cache the iterator object.
    1054          63 :     if (!guards.empty())
    1055          56 :         StoreInIteratorCache(cx, obj, key, iterobj);
    1056             : 
    1057          63 :     return iterobj;
    1058             : }
    1059             : 
    1060             : // ES 2017 draft 7.4.7.
    1061             : JSObject*
    1062           0 : js::CreateIterResultObject(JSContext* cx, HandleValue value, bool done)
    1063             : {
    1064             :     // Step 1 (implicit).
    1065             : 
    1066             :     // Step 2.
    1067           0 :     RootedObject resultObj(cx, NewBuiltinClassInstance<PlainObject>(cx));
    1068           0 :     if (!resultObj)
    1069           0 :         return nullptr;
    1070             : 
    1071             :     // Step 3.
    1072           0 :     if (!DefineProperty(cx, resultObj, cx->names().value, value))
    1073           0 :         return nullptr;
    1074             : 
    1075             :     // Step 4.
    1076           0 :     if (!DefineProperty(cx, resultObj, cx->names().done,
    1077           0 :                         done ? TrueHandleValue : FalseHandleValue))
    1078             :     {
    1079           0 :         return nullptr;
    1080             :     }
    1081             : 
    1082             :     // Step 5.
    1083           0 :     return resultObj;
    1084             : }
    1085             : 
    1086             : bool
    1087           5 : js::ThrowStopIteration(JSContext* cx)
    1088             : {
    1089           5 :     MOZ_ASSERT(!JS_IsExceptionPending(cx));
    1090             : 
    1091             :     // StopIteration isn't a constructor, but it's stored in GlobalObject
    1092             :     // as one, out of laziness. Hence the GetBuiltinConstructor call here.
    1093          10 :     RootedObject ctor(cx);
    1094           5 :     if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
    1095           5 :         cx->setPendingException(ObjectValue(*ctor));
    1096          10 :     return false;
    1097             : }
    1098             : 
    1099             : /*** Iterator objects ****************************************************************************/
    1100             : 
    1101             : bool
    1102           0 : js::IteratorConstructor(JSContext* cx, unsigned argc, Value* vp)
    1103             : {
    1104           0 :     CallArgs args = CallArgsFromVp(argc, vp);
    1105           0 :     if (args.length() == 0) {
    1106           0 :         ReportMissingArg(cx, args.calleev(), 0);
    1107           0 :         return false;
    1108             :     }
    1109             : 
    1110           0 :     bool keyonly = false;
    1111           0 :     if (args.length() >= 2)
    1112           0 :         keyonly = ToBoolean(args[1]);
    1113           0 :     unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
    1114             : 
    1115           0 :     RootedObject iterobj(cx, ValueToIterator(cx, flags, args[0]));
    1116           0 :     if (!iterobj)
    1117           0 :         return false;
    1118           0 :     args.rval().setObject(*iterobj);
    1119           0 :     return true;
    1120             : }
    1121             : 
    1122             : MOZ_ALWAYS_INLINE bool
    1123         185 : NativeIteratorNext(JSContext* cx, NativeIterator* ni, MutableHandleValue rval, bool* done)
    1124             : {
    1125         185 :     *done = false;
    1126             : 
    1127         185 :     if (ni->props_cursor >= ni->props_end) {
    1128          27 :         *done = true;
    1129          27 :         return true;
    1130             :     }
    1131             : 
    1132         158 :     if (MOZ_LIKELY(ni->isKeyIter())) {
    1133         158 :         rval.setString(*ni->current());
    1134         158 :         ni->incCursor();
    1135         158 :         return true;
    1136             :     }
    1137             : 
    1138             :     // Non-standard Iterator for "for each"
    1139           0 :     RootedId id(cx);
    1140           0 :     RootedValue current(cx, StringValue(*ni->current()));
    1141           0 :     if (!ValueToId<CanGC>(cx, current, &id))
    1142           0 :         return false;
    1143           0 :     ni->incCursor();
    1144           0 :     RootedObject obj(cx, ni->obj);
    1145           0 :     if (!GetProperty(cx, obj, obj, id, rval))
    1146           0 :         return false;
    1147             : 
    1148             :     // JS 1.7 only: for each (let [k, v] in obj)
    1149           0 :     if (ni->flags & JSITER_KEYVALUE)
    1150           0 :         return NewKeyValuePair(cx, id, rval, rval);
    1151           0 :     return true;
    1152             : }
    1153             : 
    1154             : MOZ_ALWAYS_INLINE bool
    1155          40 : IsIterator(HandleValue v)
    1156             : {
    1157          40 :     return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_);
    1158             : }
    1159             : 
    1160             : MOZ_ALWAYS_INLINE bool
    1161          20 : legacy_iterator_next_impl(JSContext* cx, const CallArgs& args)
    1162             : {
    1163          20 :     MOZ_ASSERT(IsIterator(args.thisv()));
    1164             : 
    1165          40 :     RootedObject thisObj(cx, &args.thisv().toObject());
    1166             : 
    1167          20 :     NativeIterator* ni = thisObj.as<PropertyIteratorObject>()->getNativeIterator();
    1168          40 :     RootedValue value(cx);
    1169             :     bool done;
    1170          20 :     if (!NativeIteratorNext(cx, ni, &value, &done))
    1171           0 :          return false;
    1172             : 
    1173             :     // Use old iterator protocol for compatibility reasons.
    1174          20 :     if (done) {
    1175           5 :         ThrowStopIteration(cx);
    1176           5 :         return false;
    1177             :     }
    1178             : 
    1179          15 :     args.rval().set(value);
    1180          15 :     return true;
    1181             : }
    1182             : 
    1183             : static bool
    1184          20 : legacy_iterator_next(JSContext* cx, unsigned argc, Value* vp)
    1185             : {
    1186          20 :     CallArgs args = CallArgsFromVp(argc, vp);
    1187          20 :     return CallNonGenericMethod<IsIterator, legacy_iterator_next_impl>(cx, args);
    1188             : }
    1189             : 
    1190             : static const JSFunctionSpec legacy_iterator_methods[] = {
    1191             :     JS_SELF_HOSTED_SYM_FN(iterator, "LegacyIteratorShim", 0, 0),
    1192             :     JS_FN("next",      legacy_iterator_next,       0, 0),
    1193             :     JS_FS_END
    1194             : };
    1195             : 
    1196             : size_t
    1197           0 : PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
    1198             : {
    1199           0 :     return mallocSizeOf(getPrivate());
    1200             : }
    1201             : 
    1202             : void
    1203           0 : PropertyIteratorObject::trace(JSTracer* trc, JSObject* obj)
    1204             : {
    1205           0 :     if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
    1206           0 :         ni->trace(trc);
    1207           0 : }
    1208             : 
    1209             : void
    1210           0 : PropertyIteratorObject::finalize(FreeOp* fop, JSObject* obj)
    1211             : {
    1212           0 :     if (NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator())
    1213           0 :         fop->free_(ni);
    1214           0 : }
    1215             : 
    1216             : const ClassOps PropertyIteratorObject::classOps_ = {
    1217             :     nullptr, /* addProperty */
    1218             :     nullptr, /* delProperty */
    1219             :     nullptr, /* getProperty */
    1220             :     nullptr, /* setProperty */
    1221             :     nullptr, /* enumerate */
    1222             :     nullptr, /* newEnumerate */
    1223             :     nullptr, /* resolve */
    1224             :     nullptr, /* mayResolve */
    1225             :     finalize,
    1226             :     nullptr, /* call        */
    1227             :     nullptr, /* hasInstance */
    1228             :     nullptr, /* construct   */
    1229             :     trace
    1230             : };
    1231             : 
    1232             : const Class PropertyIteratorObject::class_ = {
    1233             :     "Iterator",
    1234             :     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
    1235             :     JSCLASS_HAS_PRIVATE |
    1236             :     JSCLASS_BACKGROUND_FINALIZE,
    1237             :     &PropertyIteratorObject::classOps_
    1238             : };
    1239             : 
    1240             : static const Class ArrayIteratorPrototypeClass = {
    1241             :     "Array Iterator",
    1242             :     0
    1243             : };
    1244             : 
    1245             : enum {
    1246             :     ArrayIteratorSlotIteratedObject,
    1247             :     ArrayIteratorSlotNextIndex,
    1248             :     ArrayIteratorSlotItemKind,
    1249             :     ArrayIteratorSlotCount
    1250             : };
    1251             : 
    1252             : const Class ArrayIteratorObject::class_ = {
    1253             :     "Array Iterator",
    1254             :     JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount)
    1255             : };
    1256             : 
    1257             : 
    1258             : ArrayIteratorObject*
    1259        2115 : js::NewArrayIteratorObject(JSContext* cx, NewObjectKind newKind)
    1260             : {
    1261        4230 :     RootedObject proto(cx, GlobalObject::getOrCreateArrayIteratorPrototype(cx, cx->global()));
    1262        2115 :     if (!proto)
    1263           0 :         return nullptr;
    1264             : 
    1265        2115 :     return NewObjectWithGivenProto<ArrayIteratorObject>(cx, proto, newKind);
    1266             : }
    1267             : 
    1268             : static const JSFunctionSpec array_iterator_methods[] = {
    1269             :     JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
    1270             :     JS_FS_END
    1271             : };
    1272             : 
    1273             : static const Class StringIteratorPrototypeClass = {
    1274             :     "String Iterator",
    1275             :     0
    1276             : };
    1277             : 
    1278             : enum {
    1279             :     StringIteratorSlotIteratedObject,
    1280             :     StringIteratorSlotNextIndex,
    1281             :     StringIteratorSlotCount
    1282             : };
    1283             : 
    1284             : const Class StringIteratorObject::class_ = {
    1285             :     "String Iterator",
    1286             :     JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount)
    1287             : };
    1288             : 
    1289             : static const JSFunctionSpec string_iterator_methods[] = {
    1290             :     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
    1291             :     JS_FS_END
    1292             : };
    1293             : 
    1294             : StringIteratorObject*
    1295           0 : js::NewStringIteratorObject(JSContext* cx, NewObjectKind newKind)
    1296             : {
    1297           0 :     RootedObject proto(cx, GlobalObject::getOrCreateStringIteratorPrototype(cx, cx->global()));
    1298           0 :     if (!proto)
    1299           0 :         return nullptr;
    1300             : 
    1301           0 :     return NewObjectWithGivenProto<StringIteratorObject>(cx, proto, newKind);
    1302             : }
    1303             : 
    1304             : JSObject*
    1305          98 : js::ValueToIterator(JSContext* cx, unsigned flags, HandleValue vp)
    1306             : {
    1307             :     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
    1308          98 :     MOZ_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
    1309             : 
    1310         196 :     RootedObject obj(cx);
    1311          98 :     if (vp.isObject()) {
    1312             :         /* Common case. */
    1313          98 :         obj = &vp.toObject();
    1314           0 :     } else if ((flags & JSITER_ENUMERATE) && vp.isNullOrUndefined()) {
    1315             :         /*
    1316             :          * Enumerating over null and undefined gives an empty enumerator, so
    1317             :          * that |for (var p in <null or undefined>) <loop>;| never executes
    1318             :          * <loop>, per ES5 12.6.4.
    1319             :          */
    1320           0 :         return NewEmptyPropertyIterator(cx, flags);
    1321             :     } else {
    1322           0 :         obj = ToObject(cx, vp);
    1323           0 :         if (!obj)
    1324           0 :             return nullptr;
    1325             :     }
    1326             : 
    1327          98 :     return GetIterator(cx, obj, flags);
    1328             : }
    1329             : 
    1330             : bool
    1331          98 : js::CloseIterator(JSContext* cx, HandleObject obj)
    1332             : {
    1333          98 :     if (obj->is<PropertyIteratorObject>()) {
    1334             :         /* Remove enumerators from the active list, which is a stack. */
    1335          93 :         NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
    1336             : 
    1337          93 :         if (ni->flags & JSITER_ENUMERATE) {
    1338          93 :             ni->unlink();
    1339             : 
    1340          93 :             MOZ_ASSERT(ni->flags & JSITER_ACTIVE);
    1341          93 :             ni->flags &= ~JSITER_ACTIVE;
    1342             : 
    1343             :             /*
    1344             :              * Reset the enumerator; it may still be in the cached iterators
    1345             :              * for this thread, and can be reused.
    1346             :              */
    1347          93 :             ni->props_cursor = ni->props_array;
    1348             :         }
    1349           5 :     } else if (obj->is<LegacyGeneratorObject>()) {
    1350           0 :         Rooted<LegacyGeneratorObject*> genObj(cx, &obj->as<LegacyGeneratorObject>());
    1351           0 :         if (genObj->isClosed())
    1352           0 :             return true;
    1353           0 :         if (genObj->isRunning() || genObj->isClosing()) {
    1354             :             // Nothing sensible to do.
    1355           0 :             return true;
    1356             :         }
    1357           0 :         return LegacyGeneratorObject::close(cx, obj);
    1358             :     }
    1359             : 
    1360          98 :     return true;
    1361             : }
    1362             : 
    1363             : bool
    1364           0 : js::UnwindIteratorForException(JSContext* cx, HandleObject obj)
    1365             : {
    1366           0 :     RootedValue v(cx);
    1367           0 :     bool getOk = cx->getPendingException(&v);
    1368           0 :     cx->clearPendingException();
    1369           0 :     if (!CloseIterator(cx, obj))
    1370           0 :         return false;
    1371           0 :     if (!getOk)
    1372           0 :         return false;
    1373           0 :     cx->setPendingException(v);
    1374           0 :     return true;
    1375             : }
    1376             : 
    1377             : bool
    1378           0 : js::IteratorCloseForException(JSContext* cx, HandleObject obj)
    1379             : {
    1380           0 :     MOZ_ASSERT(cx->isExceptionPending());
    1381             : 
    1382           0 :     bool isClosingGenerator = cx->isClosingGenerator();
    1383           0 :     JS::AutoSaveExceptionState savedExc(cx);
    1384             : 
    1385             :     // Implements IteratorClose (ES 7.4.6) for exception unwinding. See
    1386             :     // also the bytecode generated by BytecodeEmitter::emitIteratorClose.
    1387             : 
    1388             :     // Step 3.
    1389             :     //
    1390             :     // Get the "return" method.
    1391           0 :     RootedValue returnMethod(cx);
    1392           0 :     if (!GetProperty(cx, obj, obj, cx->names().return_, &returnMethod))
    1393           0 :         return false;
    1394             : 
    1395             :     // Step 4.
    1396             :     //
    1397             :     // Do nothing if "return" is null or undefined. Throw a TypeError if the
    1398             :     // method is not IsCallable.
    1399           0 :     if (returnMethod.isNullOrUndefined())
    1400           0 :         return true;
    1401           0 :     if (!IsCallable(returnMethod))
    1402           0 :         return ReportIsNotFunction(cx, returnMethod);
    1403             : 
    1404             :     // Step 5, 6, 8.
    1405             :     //
    1406             :     // Call "return" if it is not null or undefined.
    1407           0 :     RootedValue rval(cx);
    1408           0 :     bool ok = Call(cx, returnMethod, obj, &rval);
    1409           0 :     if (isClosingGenerator) {
    1410             :         // Closing an iterator is implemented as an exception, but in spec
    1411             :         // terms it is a Completion value with [[Type]] return. In this case
    1412             :         // we *do* care if the call threw and if it returned an object.
    1413           0 :         if (!ok)
    1414           0 :             return false;
    1415           0 :         if (!rval.isObject())
    1416           0 :             return ThrowCheckIsObject(cx, CheckIsObjectKind::IteratorReturn);
    1417             :     } else {
    1418             :         // We don't care if the call threw or that it returned an Object, as
    1419             :         // Step 6 says if IteratorClose is being called during a throw, the
    1420             :         // original throw has primacy.
    1421           0 :         savedExc.restore();
    1422             :     }
    1423             : 
    1424           0 :     return true;
    1425             : }
    1426             : 
    1427             : void
    1428           0 : js::UnwindIteratorForUncatchableException(JSContext* cx, JSObject* obj)
    1429             : {
    1430           0 :     if (obj->is<PropertyIteratorObject>()) {
    1431           0 :         NativeIterator* ni = obj->as<PropertyIteratorObject>().getNativeIterator();
    1432           0 :         if (ni->flags & JSITER_ENUMERATE)
    1433           0 :             ni->unlink();
    1434             :     }
    1435           0 : }
    1436             : 
    1437             : /*
    1438             :  * Suppress enumeration of deleted properties. This function must be called
    1439             :  * when a property is deleted and there might be active enumerators.
    1440             :  *
    1441             :  * We maintain a list of active non-escaping for-in enumerators. To suppress
    1442             :  * a property, we check whether each active enumerator contains the (obj, id)
    1443             :  * pair and has not yet enumerated |id|. If so, and |id| is the next property,
    1444             :  * we simply advance the cursor. Otherwise, we delete |id| from the list.
    1445             :  *
    1446             :  * We do not suppress enumeration of a property deleted along an object's
    1447             :  * prototype chain. Only direct deletions on the object are handled.
    1448             :  *
    1449             :  * This function can suppress multiple properties at once. The |predicate|
    1450             :  * argument is an object which can be called on an id and returns true or
    1451             :  * false. It also must have a method |matchesAtMostOne| which allows us to
    1452             :  * stop searching after the first deletion if true.
    1453             :  */
    1454             : template<typename StringPredicate>
    1455             : static bool
    1456           2 : SuppressDeletedPropertyHelper(JSContext* cx, HandleObject obj, StringPredicate predicate)
    1457             : {
    1458           2 :     NativeIterator* enumeratorList = cx->compartment()->enumerators;
    1459           2 :     NativeIterator* ni = enumeratorList->next();
    1460             : 
    1461           6 :     while (ni != enumeratorList) {
    1462             :       again:
    1463             :         /* This only works for identified suppressed keys, not values. */
    1464           2 :         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
    1465             :             /* Check whether id is still to come. */
    1466           2 :             GCPtrFlatString* props_cursor = ni->current();
    1467           2 :             GCPtrFlatString* props_end = ni->end();
    1468         275 :             for (GCPtrFlatString* idp = props_cursor; idp < props_end; ++idp) {
    1469         273 :                 if (predicate(*idp)) {
    1470             :                     /*
    1471             :                      * Check whether another property along the prototype chain
    1472             :                      * became visible as a result of this deletion.
    1473             :                      */
    1474           0 :                     RootedObject proto(cx);
    1475           0 :                     if (!GetPrototype(cx, obj, &proto))
    1476           0 :                         return false;
    1477           0 :                     if (proto) {
    1478           0 :                         RootedId id(cx);
    1479           0 :                         RootedValue idv(cx, StringValue(*idp));
    1480           0 :                         if (!ValueToId<CanGC>(cx, idv, &id))
    1481           0 :                             return false;
    1482             : 
    1483           0 :                         Rooted<PropertyDescriptor> desc(cx);
    1484           0 :                         if (!GetPropertyDescriptor(cx, proto, id, &desc))
    1485           0 :                             return false;
    1486             : 
    1487           0 :                         if (desc.object()) {
    1488           0 :                             if (desc.enumerable())
    1489           0 :                                 continue;
    1490             :                         }
    1491             :                     }
    1492             : 
    1493             :                     /*
    1494             :                      * If GetPropertyDescriptorById above removed a property from
    1495             :                      * ni, start over.
    1496             :                      */
    1497           0 :                     if (props_end != ni->props_end || props_cursor != ni->props_cursor)
    1498             :                         goto again;
    1499             : 
    1500             :                     /*
    1501             :                      * No property along the prototype chain stepped in to take the
    1502             :                      * property's place, so go ahead and delete id from the list.
    1503             :                      * If it is the next property to be enumerated, just skip it.
    1504             :                      */
    1505           0 :                     if (idp == props_cursor) {
    1506           0 :                         ni->incCursor();
    1507             :                     } else {
    1508           0 :                         for (GCPtrFlatString* p = idp; p + 1 != props_end; p++)
    1509           0 :                             *p = *(p + 1);
    1510           0 :                         ni->props_end = ni->end() - 1;
    1511             : 
    1512             :                         /*
    1513             :                          * This invokes the pre barrier on this element, since
    1514             :                          * it's no longer going to be marked, and ensures that
    1515             :                          * any existing remembered set entry will be dropped.
    1516             :                          */
    1517           0 :                         *ni->props_end = nullptr;
    1518             :                     }
    1519             : 
    1520             :                     /* Don't reuse modified native iterators. */
    1521           0 :                     ni->flags |= JSITER_UNREUSABLE;
    1522             : 
    1523           0 :                     if (predicate.matchesAtMostOne())
    1524           0 :                         break;
    1525             :                 }
    1526             :             }
    1527             :         }
    1528           2 :         ni = ni->next();
    1529             :     }
    1530           2 :     return true;
    1531             : }
    1532             : 
    1533             : namespace {
    1534             : 
    1535             : class SingleStringPredicate {
    1536             :     Handle<JSFlatString*> str;
    1537             : public:
    1538           2 :     explicit SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {}
    1539             : 
    1540         273 :     bool operator()(JSFlatString* str) { return EqualStrings(str, this->str); }
    1541           0 :     bool matchesAtMostOne() { return true; }
    1542             : };
    1543             : 
    1544             : } /* anonymous namespace */
    1545             : 
    1546             : bool
    1547         834 : js::SuppressDeletedProperty(JSContext* cx, HandleObject obj, jsid id)
    1548             : {
    1549         834 :     if (MOZ_LIKELY(!cx->compartment()->objectMaybeInIteration(obj)))
    1550         832 :         return true;
    1551             : 
    1552           2 :     if (JSID_IS_SYMBOL(id))
    1553           0 :         return true;
    1554             : 
    1555           4 :     Rooted<JSFlatString*> str(cx, IdToString(cx, id));
    1556           2 :     if (!str)
    1557           0 :         return false;
    1558           2 :     return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
    1559             : }
    1560             : 
    1561             : bool
    1562           1 : js::SuppressDeletedElement(JSContext* cx, HandleObject obj, uint32_t index)
    1563             : {
    1564           1 :     if (MOZ_LIKELY(!cx->compartment()->objectMaybeInIteration(obj)))
    1565           1 :         return true;
    1566             : 
    1567           0 :     RootedId id(cx);
    1568           0 :     if (!IndexToId(cx, index, &id))
    1569           0 :         return false;
    1570             : 
    1571           0 :     Rooted<JSFlatString*> str(cx, IdToString(cx, id));
    1572           0 :     if (!str)
    1573           0 :         return false;
    1574           0 :     return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
    1575             : }
    1576             : 
    1577             : bool
    1578         185 : js::IteratorMore(JSContext* cx, HandleObject iterobj, MutableHandleValue rval)
    1579             : {
    1580             :     // Fast path for native iterators.
    1581         185 :     if (iterobj->is<PropertyIteratorObject>()) {
    1582         165 :         NativeIterator* ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
    1583             :         bool done;
    1584         165 :         if (!NativeIteratorNext(cx, ni, rval, &done))
    1585           0 :             return false;
    1586             : 
    1587         165 :         if (done)
    1588          22 :             rval.setMagic(JS_NO_ITER_VALUE);
    1589         165 :         return true;
    1590             :     }
    1591             : 
    1592             :     // We're reentering below and can call anything.
    1593          20 :     if (!CheckRecursionLimit(cx))
    1594           0 :         return false;
    1595             : 
    1596             :     // Call the iterator object's .next method.
    1597          20 :     if (!GetProperty(cx, iterobj, iterobj, cx->names().next, rval))
    1598           0 :         return false;
    1599             : 
    1600             :     // Call the .next method.  Fall through to the error-handling cases in the
    1601             :     // unlikely event that either one of the fallible operations performed
    1602             :     // during the call process fails.
    1603          40 :     FixedInvokeArgs<0> args(cx);
    1604          40 :     RootedValue iterval(cx, ObjectValue(*iterobj));
    1605          20 :     if (!js::Call(cx, rval, iterval, args, rval)) {
    1606             :         // Check for StopIteration.
    1607           5 :         if (!cx->isExceptionPending())
    1608           0 :             return false;
    1609          10 :         RootedValue exception(cx);
    1610           5 :         if (!cx->getPendingException(&exception))
    1611           0 :             return false;
    1612           5 :         if (!JS_IsStopIteration(exception))
    1613           0 :             return false;
    1614             : 
    1615           5 :         cx->clearPendingException();
    1616           5 :         rval.setMagic(JS_NO_ITER_VALUE);
    1617             :     }
    1618             : 
    1619          20 :     return true;
    1620             : }
    1621             : 
    1622             : static bool
    1623           0 : stopiter_hasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp)
    1624             : {
    1625           0 :     *bp = JS_IsStopIteration(v);
    1626           0 :     return true;
    1627             : }
    1628             : 
    1629             : static const ClassOps StopIterationObjectClassOps = {
    1630             :     nullptr, /* addProperty */
    1631             :     nullptr, /* delProperty */
    1632             :     nullptr, /* getProperty */
    1633             :     nullptr, /* setProperty */
    1634             :     nullptr, /* enumerate */
    1635             :     nullptr, /* enumerate */
    1636             :     nullptr, /* resolve */
    1637             :     nullptr, /* mayResolve */
    1638             :     nullptr, /* finalize */
    1639             :     nullptr, /* call */
    1640             :     stopiter_hasInstance
    1641             : };
    1642             : 
    1643             : const Class StopIterationObject::class_ = {
    1644             :     "StopIteration",
    1645             :     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
    1646             :     &StopIterationObjectClassOps
    1647             : };
    1648             : 
    1649             : static const JSFunctionSpec iterator_proto_methods[] = {
    1650             :     JS_SELF_HOSTED_SYM_FN(iterator, "IteratorIdentity", 0, 0),
    1651             :     JS_FS_END
    1652             : };
    1653             : 
    1654             : /* static */ bool
    1655         144 : GlobalObject::initIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
    1656             : {
    1657         144 :     if (global->getReservedSlot(ITERATOR_PROTO).isObject())
    1658           0 :         return true;
    1659             : 
    1660         288 :     RootedObject proto(cx, GlobalObject::createBlankPrototype<PlainObject>(cx, global));
    1661         144 :     if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, iterator_proto_methods))
    1662           0 :         return false;
    1663             : 
    1664         144 :     global->setReservedSlot(ITERATOR_PROTO, ObjectValue(*proto));
    1665         144 :     return true;
    1666             : }
    1667             : 
    1668             : /* static */ bool
    1669          74 : GlobalObject::initArrayIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
    1670             : {
    1671          74 :     if (global->getReservedSlot(ARRAY_ITERATOR_PROTO).isObject())
    1672           0 :         return true;
    1673             : 
    1674         148 :     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
    1675          74 :     if (!iteratorProto)
    1676           0 :         return false;
    1677             : 
    1678          74 :     const Class* cls = &ArrayIteratorPrototypeClass;
    1679         148 :     RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls,
    1680         148 :                                                                         iteratorProto));
    1681         370 :     if (!proto ||
    1682         518 :         !DefinePropertiesAndFunctions(cx, proto, nullptr, array_iterator_methods) ||
    1683         222 :         !DefineToStringTag(cx, proto, cx->names().ArrayIterator))
    1684             :     {
    1685           0 :         return false;
    1686             :     }
    1687             : 
    1688          74 :     global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
    1689          74 :     return true;
    1690             : }
    1691             : 
    1692             : /* static */ bool
    1693           0 : GlobalObject::initStringIteratorProto(JSContext* cx, Handle<GlobalObject*> global)
    1694             : {
    1695           0 :     if (global->getReservedSlot(STRING_ITERATOR_PROTO).isObject())
    1696           0 :         return true;
    1697             : 
    1698           0 :     RootedObject iteratorProto(cx, GlobalObject::getOrCreateIteratorPrototype(cx, global));
    1699           0 :     if (!iteratorProto)
    1700           0 :         return false;
    1701             : 
    1702           0 :     const Class* cls = &StringIteratorPrototypeClass;
    1703           0 :     RootedObject proto(cx, GlobalObject::createBlankPrototypeInheriting(cx, global, cls,
    1704           0 :                                                                         iteratorProto));
    1705           0 :     if (!proto ||
    1706           0 :         !DefinePropertiesAndFunctions(cx, proto, nullptr, string_iterator_methods) ||
    1707           0 :         !DefineToStringTag(cx, proto, cx->names().StringIterator))
    1708             :     {
    1709           0 :         return false;
    1710             :     }
    1711             : 
    1712           0 :     global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
    1713           0 :     return true;
    1714             : }
    1715             : 
    1716             : JSObject*
    1717          28 : js::InitLegacyIteratorClass(JSContext* cx, HandleObject obj)
    1718             : {
    1719          28 :     Handle<GlobalObject*> global = obj.as<GlobalObject>();
    1720             : 
    1721          28 :     if (global->getPrototype(JSProto_Iterator).isObject())
    1722           0 :         return &global->getPrototype(JSProto_Iterator).toObject();
    1723             : 
    1724          56 :     RootedObject iteratorProto(cx);
    1725          56 :     iteratorProto = GlobalObject::createBlankPrototype(cx, global,
    1726          28 :                                                        &PropertyIteratorObject::class_);
    1727          28 :     if (!iteratorProto)
    1728           0 :         return nullptr;
    1729             : 
    1730          28 :     NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, 0);
    1731          28 :     if (!ni)
    1732           0 :         return nullptr;
    1733             : 
    1734          28 :     iteratorProto->as<PropertyIteratorObject>().setNativeIterator(ni);
    1735          28 :     ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
    1736             : 
    1737          56 :     Rooted<JSFunction*> ctor(cx);
    1738          28 :     ctor = GlobalObject::createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
    1739          28 :     if (!ctor)
    1740           0 :         return nullptr;
    1741          28 :     if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
    1742           0 :         return nullptr;
    1743          28 :     if (!DefinePropertiesAndFunctions(cx, iteratorProto, nullptr, legacy_iterator_methods))
    1744           0 :         return nullptr;
    1745          28 :     if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator,
    1746             :                                               ctor, iteratorProto))
    1747             :     {
    1748           0 :         return nullptr;
    1749             :     }
    1750             : 
    1751          28 :     return &global->getPrototype(JSProto_Iterator).toObject();
    1752             : }
    1753             : 
    1754             : JSObject*
    1755          16 : js::InitStopIterationClass(JSContext* cx, HandleObject obj)
    1756             : {
    1757          16 :     Handle<GlobalObject*> global = obj.as<GlobalObject>();
    1758          16 :     if (!global->getPrototype(JSProto_StopIteration).isObject()) {
    1759          32 :         RootedObject proto(cx, GlobalObject::createBlankPrototype(cx, global,
    1760          32 :                                                                   &StopIterationObject::class_));
    1761          16 :         if (!proto || !FreezeObject(cx, proto))
    1762           0 :             return nullptr;
    1763             : 
    1764             :         // This should use a non-JSProtoKey'd slot, but this is easier for now.
    1765          16 :         if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
    1766           0 :             return nullptr;
    1767             : 
    1768          16 :         global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
    1769             :     }
    1770             : 
    1771          16 :     return &global->getPrototype(JSProto_StopIteration).toObject();
    1772             : }

Generated by: LCOV version 1.13