js/src/jsiter.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     7 /* JavaScript iterators. */
     9 #include "jsiter.h"
    11 #include "mozilla/ArrayUtils.h"
    12 #include "mozilla/MemoryReporting.h"
    13 #include "mozilla/PodOperations.h"
    15 #include "jsarray.h"
    16 #include "jsatom.h"
    17 #include "jscntxt.h"
    18 #include "jsgc.h"
    19 #include "jsobj.h"
    20 #include "jsopcode.h"
    21 #include "jsproxy.h"
    22 #include "jsscript.h"
    23 #include "jstypes.h"
    24 #include "jsutil.h"
    26 #include "ds/Sort.h"
    27 #include "gc/Marking.h"
    28 #include "vm/GeneratorObject.h"
    29 #include "vm/GlobalObject.h"
    30 #include "vm/Interpreter.h"
    31 #include "vm/Shape.h"
    32 #include "vm/StopIterationObject.h"
    34 #include "jsinferinlines.h"
    35 #include "jsobjinlines.h"
    36 #include "jsscriptinlines.h"
    38 #include "vm/Stack-inl.h"
    39 #include "vm/String-inl.h"
    41 using namespace js;
    42 using namespace js::gc;
    43 using JS::ForOfIterator;
    45 using mozilla::ArrayLength;
    46 #ifdef JS_MORE_DETERMINISTIC
    47 using mozilla::PodCopy;
    48 #endif
    49 using mozilla::PodZero;
    51 typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
    53 static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2_BACKGROUND;
    55 void
    56 NativeIterator::mark(JSTracer *trc)
    57 {
    58     for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
    59         MarkString(trc, str, "prop");
    60     if (obj)
    61         MarkObject(trc, &obj, "obj");
    63     // The SuppressDeletedPropertyHelper loop can GC, so make sure that if the
    64     // GC removes any elements from the list, it won't remove this one.
    65     if (iterObj_)
    66         MarkObjectUnbarriered(trc, &iterObj_, "iterObj");
    67 }
    69 struct IdHashPolicy {
    70     typedef jsid Lookup;
    71     static HashNumber hash(jsid id) {
    72         return JSID_BITS(id);
    73     }
    74     static bool match(jsid id1, jsid id2) {
    75         return id1 == id2;
    76     }
    77 };
    79 typedef HashSet<jsid, IdHashPolicy> IdSet;
    81 static inline bool
    82 NewKeyValuePair(JSContext *cx, jsid id, const Value &val, MutableHandleValue rval)
    83 {
    84     JS::AutoValueArray<2> vec(cx);
    85     vec[0].set(IdToValue(id));
    86     vec[1].set(val);
    88     JSObject *aobj = NewDenseCopiedArray(cx, 2, vec.begin());
    89     if (!aobj)
    90         return false;
    91     rval.setObject(*aobj);
    92     return true;
    93 }
    95 static inline bool
    96 Enumerate(JSContext *cx, HandleObject pobj, jsid id,
    97           bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
    98 {
    99     /*
   100      * We implement __proto__ using a property on |Object.prototype|, but
   101      * because __proto__ is highly deserving of removal, we don't want it to
   102      * show up in property enumeration, even if only for |Object.prototype|
   103      * (think introspection by Prototype-like frameworks that add methods to
   104      * the built-in prototypes).  So exclude __proto__ if the object where the
   105      * property was found has no [[Prototype]] and might be |Object.prototype|.
   106      */
   107     if (MOZ_UNLIKELY(!pobj->getTaggedProto().isObject() && JSID_IS_ATOM(id, cx->names().proto)))
   108         return true;
   110     if (!(flags & JSITER_OWNONLY) || pobj->is<ProxyObject>() || pobj->getOps()->enumerate) {
   111         /* If we've already seen this, we definitely won't add it. */
   112         IdSet::AddPtr p = ht.lookupForAdd(id);
   113         if (MOZ_UNLIKELY(!!p))
   114             return true;
   116         /*
   117          * It's not necessary to add properties to the hash table at the end of
   118          * the prototype chain, but custom enumeration behaviors might return
   119          * duplicated properties, so always add in such cases.
   120          */
   121         if ((pobj->is<ProxyObject>() || pobj->getProto() || pobj->getOps()->enumerate) && !ht.add(p, id))
   122             return false;
   123     }
   125     if (enumerable || (flags & JSITER_HIDDEN))
   126         return props->append(id);
   128     return true;
   129 }
   131 static bool
   132 EnumerateNativeProperties(JSContext *cx, HandleObject pobj, unsigned flags, IdSet &ht,
   133                           AutoIdVector *props)
   134 {
   135     /* Collect any dense elements from this object. */
   136     size_t initlen = pobj->getDenseInitializedLength();
   137     const Value *vp = pobj->getDenseElements();
   138     for (size_t i = 0; i < initlen; ++i, ++vp) {
   139         if (!vp->isMagic(JS_ELEMENTS_HOLE)) {
   140             /* Dense arrays never get so large that i would not fit into an integer id. */
   141             if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
   142                 return false;
   143         }
   144     }
   146     /* Collect any typed array elements from this object. */
   147     if (pobj->is<TypedArrayObject>()) {
   148         size_t len = pobj->as<TypedArrayObject>().length();
   149         for (size_t i = 0; i < len; i++) {
   150             if (!Enumerate(cx, pobj, INT_TO_JSID(i), /* enumerable = */ true, flags, ht, props))
   151                 return false;
   152         }
   153     }
   155     size_t initialLength = props->length();
   157     /* Collect all unique properties from this object's scope. */
   158     Shape::Range<NoGC> r(pobj->lastProperty());
   159     for (; !r.empty(); r.popFront()) {
   160         Shape &shape = r.front();
   162         if (!Enumerate(cx, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
   163             return false;
   164     }
   166     ::Reverse(props->begin() + initialLength, props->end());
   167     return true;
   168 }
   170 #ifdef JS_MORE_DETERMINISTIC
   172 struct SortComparatorIds
   173 {
   174     JSContext   *const cx;
   176     SortComparatorIds(JSContext *cx)
   177       : cx(cx) {}
   179     bool operator()(jsid a, jsid b, bool *lessOrEqualp)
   180     {
   181         /* Pick an arbitrary total order on jsids that is stable across executions. */
   182         RootedString astr(cx, IdToString(cx, a));
   183 	if (!astr)
   184 	    return false;
   185         RootedString bstr(cx, IdToString(cx, b));
   186         if (!bstr)
   187             return false;
   189         int32_t result;
   190         if (!CompareStrings(cx, astr, bstr, &result))
   191             return false;
   193         *lessOrEqualp = (result <= 0);
   194         return true;
   195     }
   196 };
   198 #endif /* JS_MORE_DETERMINISTIC */
   200 static bool
   201 Snapshot(JSContext *cx, JSObject *pobj_, unsigned flags, AutoIdVector *props)
   202 {
   203     IdSet ht(cx);
   204     if (!ht.init(32))
   205         return false;
   207     RootedObject pobj(cx, pobj_);
   209     do {
   210         const Class *clasp = pobj->getClass();
   211         if (pobj->isNative() &&
   212             !pobj->getOps()->enumerate &&
   213             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
   214             if (!clasp->enumerate(cx, pobj))
   215                 return false;
   216             if (!EnumerateNativeProperties(cx, pobj, flags, ht, props))
   217                 return false;
   218         } else {
   219             if (pobj->is<ProxyObject>()) {
   220                 AutoIdVector proxyProps(cx);
   221                 if (flags & JSITER_OWNONLY) {
   222                     if (flags & JSITER_HIDDEN) {
   223                         if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps))
   224                             return false;
   225                     } else {
   226                         if (!Proxy::keys(cx, pobj, proxyProps))
   227                             return false;
   228                     }
   229                 } else {
   230                     if (!Proxy::enumerate(cx, pobj, proxyProps))
   231                         return false;
   232                 }
   233                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
   234                     if (!Enumerate(cx, pobj, proxyProps[n], true, flags, ht, props))
   235                         return false;
   236                 }
   237                 /* Proxy objects enumerate the prototype on their own, so we are done here. */
   238                 break;
   239             }
   240             RootedValue state(cx);
   241             RootedId id(cx);
   242             JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
   243             if (!JSObject::enumerate(cx, pobj, op, &state, &id))
   244                 return false;
   245             if (state.isMagic(JS_NATIVE_ENUMERATE)) {
   246                 if (!EnumerateNativeProperties(cx, pobj, flags, ht, props))
   247                     return false;
   248             } else {
   249                 while (true) {
   250                     RootedId id(cx);
   251                     if (!JSObject::enumerate(cx, pobj, JSENUMERATE_NEXT, &state, &id))
   252                         return false;
   253                     if (state.isNull())
   254                         break;
   255                     if (!Enumerate(cx, pobj, id, true, flags, ht, props))
   256                         return false;
   257                 }
   258             }
   259         }
   261         if (flags & JSITER_OWNONLY)
   262             break;
   264     } while ((pobj = pobj->getProto()) != nullptr);
   266 #ifdef JS_MORE_DETERMINISTIC
   268     /*
   269      * In some cases the enumeration order for an object depends on the
   270      * execution mode (interpreter vs. JIT), especially for native objects
   271      * with a class enumerate hook (where resolving a property changes the
   272      * resulting enumeration order). These aren't really bugs, but the
   273      * differences can change the generated output and confuse correctness
   274      * fuzzers, so we sort the ids if such a fuzzer is running.
   275      *
   276      * We don't do this in the general case because (a) doing so is slow,
   277      * and (b) it also breaks the web, which expects enumeration order to
   278      * follow the order in which properties are added, in certain cases.
   279      * Since ECMA does not specify an enumeration order for objects, both
   280      * behaviors are technically correct to do.
   281      */
   283     jsid *ids = props->begin();
   284     size_t n = props->length();
   286     AutoIdVector tmp(cx);
   287     if (!tmp.resize(n))
   288         return false;
   289     PodCopy(tmp.begin(), ids, n);
   291     if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
   292         return false;
   294 #endif /* JS_MORE_DETERMINISTIC */
   296     return true;
   297 }
   299 bool
   300 js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
   301 {
   302     JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
   303     size_t len = props.length();
   304     size_t idsz = len * sizeof(jsid);
   305     size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
   306     JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz));
   307     if (!ida)
   308         return false;
   310     ida->length = static_cast<int>(len);
   311     jsid *v = props.begin();
   312     for (int i = 0; i < ida->length; i++)
   313         ida->vector[i].init(v[i]);
   314     *idap = ida;
   315     return true;
   316 }
   318 JS_FRIEND_API(bool)
   319 js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
   320 {
   321     return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
   322 }
   324 size_t sCustomIteratorCount = 0;
   326 static inline bool
   327 GetCustomIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
   328 {
   329     JS_CHECK_RECURSION(cx, return false);
   331     /* Check whether we have a valid __iterator__ method. */
   332     HandlePropertyName name = cx->names().iteratorIntrinsic;
   333     if (!JSObject::getProperty(cx, obj, obj, name, vp))
   334         return false;
   336     /* If there is no custom __iterator__ method, we are done here. */
   337     if (!vp.isObject()) {
   338         vp.setUndefined();
   339         return true;
   340     }
   342     if (!cx->runningWithTrustedPrincipals())
   343         ++sCustomIteratorCount;
   345     /* Otherwise call it and return that object. */
   346     Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
   347     if (!Invoke(cx, ObjectValue(*obj), vp, 1, &arg, vp))
   348         return false;
   349     if (vp.isPrimitive()) {
   350         /*
   351          * We are always coming from js::ValueToIterator, and we are no longer on
   352          * trace, so the object we are iterating over is on top of the stack (-1).
   353          */
   354         JSAutoByteString bytes;
   355         if (!AtomToPrintableString(cx, name, &bytes))
   356             return false;
   357         RootedValue val(cx, ObjectValue(*obj));
   358         js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
   359                              -1, val, js::NullPtr(), bytes.ptr());
   360         return false;
   361     }
   362     return true;
   363 }
   365 template <typename T>
   366 static inline bool
   367 Compare(T *a, T *b, size_t c)
   368 {
   369     size_t n = (c + size_t(7)) / size_t(8);
   370     switch (c % 8) {
   371       case 0: do { if (*a++ != *b++) return false;
   372       case 7:      if (*a++ != *b++) return false;
   373       case 6:      if (*a++ != *b++) return false;
   374       case 5:      if (*a++ != *b++) return false;
   375       case 4:      if (*a++ != *b++) return false;
   376       case 3:      if (*a++ != *b++) return false;
   377       case 2:      if (*a++ != *b++) return false;
   378       case 1:      if (*a++ != *b++) return false;
   379               } while (--n > 0);
   380     }
   381     return true;
   382 }
   384 static inline PropertyIteratorObject *
   385 NewPropertyIteratorObject(JSContext *cx, unsigned flags)
   386 {
   387     if (flags & JSITER_ENUMERATE) {
   388         RootedTypeObject type(cx, cx->getNewType(&PropertyIteratorObject::class_, nullptr));
   389         if (!type)
   390             return nullptr;
   392         JSObject *metadata = nullptr;
   393         if (!NewObjectMetadata(cx, &metadata))
   394             return nullptr;
   396         const Class *clasp = &PropertyIteratorObject::class_;
   397         RootedShape shape(cx, EmptyShape::getInitialShape(cx, clasp, nullptr, nullptr, metadata,
   398                                                           ITERATOR_FINALIZE_KIND));
   399         if (!shape)
   400             return nullptr;
   402         JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
   403                                          GetInitialHeap(GenericObject, clasp), shape, type);
   404         if (!obj)
   405             return nullptr;
   407         JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
   408         return &obj->as<PropertyIteratorObject>();
   409     }
   411     JSObject *obj = NewBuiltinClassInstance(cx, &PropertyIteratorObject::class_);
   412     if (!obj)
   413         return nullptr;
   415     return &obj->as<PropertyIteratorObject>();
   416 }
   418 NativeIterator *
   419 NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
   420 {
   421     size_t plength = props.length();
   422     NativeIterator *ni = (NativeIterator *)
   423         cx->malloc_(sizeof(NativeIterator)
   424                     + plength * sizeof(JSString *)
   425                     + slength * sizeof(Shape *));
   426     if (!ni)
   427         return nullptr;
   428     AutoValueVector strings(cx);
   429     ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
   430     ni->props_end = ni->props_array + plength;
   431     if (plength) {
   432         for (size_t i = 0; i < plength; i++) {
   433             JSFlatString *str = IdToString(cx, props[i]);
   434             if (!str || !strings.append(StringValue(str)))
   435                 return nullptr;
   436             ni->props_array[i].init(str);
   437         }
   438     }
   439     ni->next_ = nullptr;
   440     ni->prev_ = nullptr;
   441     return ni;
   442 }
   444 NativeIterator *
   445 NativeIterator::allocateSentinel(JSContext *cx)
   446 {
   447     NativeIterator *ni = (NativeIterator *)js_malloc(sizeof(NativeIterator));
   448     if (!ni)
   449         return nullptr;
   451     PodZero(ni);
   453     ni->next_ = ni;
   454     ni->prev_ = ni;
   455     return ni;
   456 }
   458 inline void
   459 NativeIterator::init(JSObject *obj, JSObject *iterObj, unsigned flags, uint32_t slength, uint32_t key)
   460 {
   461     this->obj.init(obj);
   462     this->iterObj_ = iterObj;
   463     this->flags = flags;
   464     this->shapes_array = (Shape **) this->props_end;
   465     this->shapes_length = slength;
   466     this->shapes_key = key;
   467 }
   469 static inline void
   470 RegisterEnumerator(JSContext *cx, PropertyIteratorObject *iterobj, NativeIterator *ni)
   471 {
   472     /* Register non-escaping native enumerators (for-in) with the current context. */
   473     if (ni->flags & JSITER_ENUMERATE) {
   474         ni->link(cx->compartment()->enumerators);
   476         JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
   477         ni->flags |= JSITER_ACTIVE;
   478     }
   479 }
   481 static inline bool
   482 VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
   483                     uint32_t slength, uint32_t key, MutableHandleValue vp)
   484 {
   485     JS_ASSERT(!(flags & JSITER_FOREACH));
   487     if (obj) {
   488         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
   489             return false;
   490         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
   491     }
   493     Rooted<PropertyIteratorObject *> iterobj(cx, NewPropertyIteratorObject(cx, flags));
   494     if (!iterobj)
   495         return false;
   497     NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
   498     if (!ni)
   499         return false;
   500     ni->init(obj, iterobj, flags, slength, key);
   502     if (slength) {
   503         /*
   504          * Fill in the shape array from scratch.  We can't use the array that was
   505          * computed for the cache lookup earlier, as constructing iterobj could
   506          * have triggered a shape-regenerating GC.  Don't bother with regenerating
   507          * the shape key; if such a GC *does* occur, we can only get hits through
   508          * the one-slot lastNativeIterator cache.
   509          */
   510         JSObject *pobj = obj;
   511         size_t ind = 0;
   512         do {
   513             ni->shapes_array[ind++] = pobj->lastProperty();
   514             pobj = pobj->getProto();
   515         } while (pobj);
   516         JS_ASSERT(ind == slength);
   517     }
   519     iterobj->setNativeIterator(ni);
   520     vp.setObject(*iterobj);
   522     RegisterEnumerator(cx, iterobj, ni);
   523     return true;
   524 }
   526 bool
   527 js::VectorToKeyIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &props,
   528                         MutableHandleValue vp)
   529 {
   530     return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
   531 }
   533 bool
   534 js::VectorToValueIterator(JSContext *cx, HandleObject obj, unsigned flags, AutoIdVector &keys,
   535                           MutableHandleValue vp)
   536 {
   537     JS_ASSERT(flags & JSITER_FOREACH);
   539     if (obj) {
   540         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
   541             return false;
   542         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
   543     }
   545     Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx, flags));
   546     if (!iterobj)
   547         return false;
   549     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
   550     if (!ni)
   551         return false;
   552     ni->init(obj, iterobj, flags, 0, 0);
   554     iterobj->setNativeIterator(ni);
   555     vp.setObject(*iterobj);
   557     RegisterEnumerator(cx, iterobj, ni);
   558     return true;
   559 }
   561 bool
   562 js::EnumeratedIdVectorToIterator(JSContext *cx, HandleObject obj, unsigned flags,
   563                                  AutoIdVector &props, MutableHandleValue vp)
   564 {
   565     if (!(flags & JSITER_FOREACH))
   566         return VectorToKeyIterator(cx, obj, flags, props, vp);
   568     return VectorToValueIterator(cx, obj, flags, props, vp);
   569 }
   571 static inline void
   572 UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
   573 {
   574     // Update the object for which the native iterator is associated, so
   575     // SuppressDeletedPropertyHelper will recognize the iterator as a match.
   576     ni->obj = obj;
   577 }
   579 bool
   580 js::GetIterator(JSContext *cx, HandleObject obj, unsigned flags, MutableHandleValue vp)
   581 {
   582     Vector<Shape *, 8> shapes(cx);
   583     uint32_t key = 0;
   585     bool keysOnly = (flags == JSITER_ENUMERATE);
   587     if (obj) {
   588         if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
   589             JSObject *iterobj = op(cx, obj, !(flags & JSITER_FOREACH));
   590             if (!iterobj)
   591                 return false;
   592             vp.setObject(*iterobj);
   593             return true;
   594         }
   596         if (keysOnly) {
   597             /*
   598              * Check to see if this is the same as the most recent object which
   599              * was iterated over.  We don't explicitly check for shapeless
   600              * objects here, as they are not inserted into the cache and
   601              * will result in a miss.
   602              */
   603             PropertyIteratorObject *last = cx->runtime()->nativeIterCache.last;
   604             if (last) {
   605                 NativeIterator *lastni = last->getNativeIterator();
   606                 if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
   607                     obj->isNative() &&
   608                     obj->hasEmptyElements() &&
   609                     obj->lastProperty() == lastni->shapes_array[0])
   610                 {
   611                     JSObject *proto = obj->getProto();
   612                     if (proto->isNative() &&
   613                         proto->hasEmptyElements() &&
   614                         proto->lastProperty() == lastni->shapes_array[1] &&
   615                         !proto->getProto())
   616                     {
   617                         vp.setObject(*last);
   618                         UpdateNativeIterator(lastni, obj);
   619                         RegisterEnumerator(cx, last, lastni);
   620                         return true;
   621                     }
   622                 }
   623             }
   625             /*
   626              * The iterator object for JSITER_ENUMERATE never escapes, so we
   627              * don't care for the proper parent/proto to be set. This also
   628              * allows us to re-use a previous iterator object that is not
   629              * currently active.
   630              */
   631             {
   632                 JSObject *pobj = obj;
   633                 do {
   634                     if (!pobj->isNative() ||
   635                         !pobj->hasEmptyElements() ||
   636                         pobj->is<TypedArrayObject>() ||
   637                         pobj->hasUncacheableProto() ||
   638                         pobj->getOps()->enumerate ||
   639                         pobj->getClass()->enumerate != JS_EnumerateStub ||
   640                         pobj->nativeContainsPure(cx->names().iteratorIntrinsic))
   641                     {
   642                         shapes.clear();
   643                         goto miss;
   644                     }
   645                     Shape *shape = pobj->lastProperty();
   646                     key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
   647                     if (!shapes.append(shape))
   648                         return false;
   649                     pobj = pobj->getProto();
   650                 } while (pobj);
   651             }
   653             PropertyIteratorObject *iterobj = cx->runtime()->nativeIterCache.get(key);
   654             if (iterobj) {
   655                 NativeIterator *ni = iterobj->getNativeIterator();
   656                 if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
   657                     ni->shapes_key == key &&
   658                     ni->shapes_length == shapes.length() &&
   659                     Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
   660                     vp.setObject(*iterobj);
   662                     UpdateNativeIterator(ni, obj);
   663                     RegisterEnumerator(cx, iterobj, ni);
   664                     if (shapes.length() == 2)
   665                         cx->runtime()->nativeIterCache.last = iterobj;
   666                     return true;
   667                 }
   668             }
   669         }
   671       miss:
   672         if (obj->is<ProxyObject>())
   673             return Proxy::iterate(cx, obj, flags, vp);
   675         if (!GetCustomIterator(cx, obj, flags, vp))
   676             return false;
   677         if (!vp.isUndefined())
   678             return true;
   679     }
   681     /* NB: for (var p in null) succeeds by iterating over no properties. */
   683     AutoIdVector keys(cx);
   684     if (flags & JSITER_FOREACH) {
   685         if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys))
   686             return false;
   687         JS_ASSERT(shapes.empty());
   688         if (!VectorToValueIterator(cx, obj, flags, keys, vp))
   689             return false;
   690     } else {
   691         if (MOZ_LIKELY(obj != nullptr) && !Snapshot(cx, obj, flags, &keys))
   692             return false;
   693         if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
   694             return false;
   695     }
   697     PropertyIteratorObject *iterobj = &vp.toObject().as<PropertyIteratorObject>();
   699     /* Cache the iterator object if possible. */
   700     if (shapes.length())
   701         cx->runtime()->nativeIterCache.set(key, iterobj);
   703     if (shapes.length() == 2)
   704         cx->runtime()->nativeIterCache.last = iterobj;
   705     return true;
   706 }
   708 JSObject *
   709 js::GetIteratorObject(JSContext *cx, HandleObject obj, uint32_t flags)
   710 {
   711     RootedValue value(cx);
   712     if (!GetIterator(cx, obj, flags, &value))
   713         return nullptr;
   714     return &value.toObject();
   715 }
   717 JSObject *
   718 js::CreateItrResultObject(JSContext *cx, HandleValue value, bool done)
   719 {
   720     // FIXME: We can cache the iterator result object shape somewhere.
   721     AssertHeapIsIdle(cx);
   723     RootedObject proto(cx, cx->global()->getOrCreateObjectPrototype(cx));
   724     if (!proto)
   725         return nullptr;
   727     RootedObject obj(cx, NewObjectWithGivenProto(cx, &JSObject::class_, proto, cx->global()));
   728     if (!obj)
   729         return nullptr;
   731     if (!JSObject::defineProperty(cx, obj, cx->names().value, value))
   732         return nullptr;
   734     RootedValue doneBool(cx, BooleanValue(done));
   735     if (!JSObject::defineProperty(cx, obj, cx->names().done, doneBool))
   736         return nullptr;
   738     return obj;
   739 }
   741 bool
   742 js_ThrowStopIteration(JSContext *cx)
   743 {
   744     JS_ASSERT(!JS_IsExceptionPending(cx));
   746     // StopIteration isn't a constructor, but it's stored in GlobalObject
   747     // as one, out of laziness. Hence the GetBuiltinConstructor call here.
   748     RootedObject ctor(cx);
   749     if (GetBuiltinConstructor(cx, JSProto_StopIteration, &ctor))
   750         cx->setPendingException(ObjectValue(*ctor));
   751     return false;
   752 }
   754 /*** Iterator objects ****************************************************************************/
   756 bool
   757 js::IteratorConstructor(JSContext *cx, unsigned argc, Value *vp)
   758 {
   759     CallArgs args = CallArgsFromVp(argc, vp);
   760     if (args.length() == 0) {
   761         js_ReportMissingArg(cx, args.calleev(), 0);
   762         return false;
   763     }
   765     bool keyonly = false;
   766     if (args.length() >= 2)
   767         keyonly = ToBoolean(args[1]);
   768     unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
   770     if (!ValueToIterator(cx, flags, args[0]))
   771         return false;
   772     args.rval().set(args[0]);
   773     return true;
   774 }
   776 MOZ_ALWAYS_INLINE bool
   777 IsIterator(HandleValue v)
   778 {
   779     return v.isObject() && v.toObject().hasClass(&PropertyIteratorObject::class_);
   780 }
   782 MOZ_ALWAYS_INLINE bool
   783 iterator_next_impl(JSContext *cx, CallArgs args)
   784 {
   785     JS_ASSERT(IsIterator(args.thisv()));
   787     RootedObject thisObj(cx, &args.thisv().toObject());
   789     if (!js_IteratorMore(cx, thisObj, args.rval()))
   790         return false;
   792     if (!args.rval().toBoolean()) {
   793         js_ThrowStopIteration(cx);
   794         return false;
   795     }
   797     return js_IteratorNext(cx, thisObj, args.rval());
   798 }
   800 static bool
   801 iterator_next(JSContext *cx, unsigned argc, Value *vp)
   802 {
   803     CallArgs args = CallArgsFromVp(argc, vp);
   804     return CallNonGenericMethod<IsIterator, iterator_next_impl>(cx, args);
   805 }
   807 static const JSFunctionSpec iterator_methods[] = {
   808     JS_SELF_HOSTED_FN("@@iterator", "LegacyIteratorShim", 0, 0),
   809     JS_FN("next",      iterator_next,       0, 0),
   810     JS_FS_END
   811 };
   813 static JSObject *
   814 iterator_iteratorObject(JSContext *cx, HandleObject obj, bool keysonly)
   815 {
   816     return obj;
   817 }
   819 size_t
   820 PropertyIteratorObject::sizeOfMisc(mozilla::MallocSizeOf mallocSizeOf) const
   821 {
   822     return mallocSizeOf(getPrivate());
   823 }
   825 void
   826 PropertyIteratorObject::trace(JSTracer *trc, JSObject *obj)
   827 {
   828     if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator())
   829         ni->mark(trc);
   830 }
   832 void
   833 PropertyIteratorObject::finalize(FreeOp *fop, JSObject *obj)
   834 {
   835     if (NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator())
   836         fop->free_(ni);
   837 }
   839 const Class PropertyIteratorObject::class_ = {
   840     "Iterator",
   841     JSCLASS_IMPLEMENTS_BARRIERS |
   842     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator) |
   843     JSCLASS_HAS_PRIVATE |
   844     JSCLASS_BACKGROUND_FINALIZE,
   845     JS_PropertyStub,         /* addProperty */
   846     JS_DeletePropertyStub,   /* delProperty */
   847     JS_PropertyStub,         /* getProperty */
   848     JS_StrictPropertyStub,   /* setProperty */
   849     JS_EnumerateStub,
   850     JS_ResolveStub,
   851     JS_ConvertStub,
   852     finalize,
   853     nullptr,                 /* call        */
   854     nullptr,                 /* hasInstance */
   855     nullptr,                 /* construct   */
   856     trace,
   857     JS_NULL_CLASS_SPEC,
   858     {
   859         nullptr,             /* outerObject    */
   860         nullptr,             /* innerObject    */
   861         iterator_iteratorObject,
   862     }
   863 };
   865 enum {
   866     ArrayIteratorSlotIteratedObject,
   867     ArrayIteratorSlotNextIndex,
   868     ArrayIteratorSlotItemKind,
   869     ArrayIteratorSlotCount
   870 };
   872 const Class ArrayIteratorObject::class_ = {
   873     "Array Iterator",
   874     JSCLASS_IMPLEMENTS_BARRIERS |
   875     JSCLASS_HAS_RESERVED_SLOTS(ArrayIteratorSlotCount),
   876     JS_PropertyStub,         /* addProperty */
   877     JS_DeletePropertyStub,   /* delProperty */
   878     JS_PropertyStub,         /* getProperty */
   879     JS_StrictPropertyStub,   /* setProperty */
   880     JS_EnumerateStub,
   881     JS_ResolveStub,
   882     JS_ConvertStub,
   883     nullptr                  /* finalize    */
   884 };
   886 static const JSFunctionSpec array_iterator_methods[] = {
   887     JS_SELF_HOSTED_FN("@@iterator", "ArrayIteratorIdentity", 0, 0),
   888     JS_SELF_HOSTED_FN("next", "ArrayIteratorNext", 0, 0),
   889     JS_FS_END
   890 };
   892 static const Class StringIteratorPrototypeClass = {
   893     "String Iterator",
   894     JSCLASS_IMPLEMENTS_BARRIERS,
   895     JS_PropertyStub,         /* addProperty */
   896     JS_DeletePropertyStub,   /* delProperty */
   897     JS_PropertyStub,         /* getProperty */
   898     JS_StrictPropertyStub,   /* setProperty */
   899     JS_EnumerateStub,
   900     JS_ResolveStub,
   901     JS_ConvertStub,
   902     nullptr                  /* finalize    */
   903 };
   905 enum {
   906     StringIteratorSlotIteratedObject,
   907     StringIteratorSlotNextIndex,
   908     StringIteratorSlotCount
   909 };
   911 const Class StringIteratorObject::class_ = {
   912     "String Iterator",
   913     JSCLASS_IMPLEMENTS_BARRIERS |
   914     JSCLASS_HAS_RESERVED_SLOTS(StringIteratorSlotCount),
   915     JS_PropertyStub,         /* addProperty */
   916     JS_DeletePropertyStub,   /* delProperty */
   917     JS_PropertyStub,         /* getProperty */
   918     JS_StrictPropertyStub,   /* setProperty */
   919     JS_EnumerateStub,
   920     JS_ResolveStub,
   921     JS_ConvertStub,
   922     nullptr                  /* finalize    */
   923 };
   925 static const JSFunctionSpec string_iterator_methods[] = {
   926     JS_SELF_HOSTED_FN("@@iterator", "StringIteratorIdentity", 0, 0),
   927     JS_SELF_HOSTED_FN("next", "StringIteratorNext", 0, 0),
   928     JS_FS_END
   929 };
   931 static bool
   932 CloseLegacyGenerator(JSContext *cx, HandleObject genobj);
   934 bool
   935 js::ValueToIterator(JSContext *cx, unsigned flags, MutableHandleValue vp)
   936 {
   937     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
   938     JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
   940     /*
   941      * Make sure the more/next state machine doesn't get stuck. A value might
   942      * be left in iterValue when a trace is left due to an interrupt after
   943      * JSOP_MOREITER but before the value is picked up by FOR*.
   944      */
   945     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
   947     RootedObject obj(cx);
   948     if (vp.isObject()) {
   949         /* Common case. */
   950         obj = &vp.toObject();
   951     } else {
   952         /*
   953          * Enumerating over null and undefined gives an empty enumerator, so
   954          * that |for (var p in <null or undefined>) <loop>;| never executes
   955          * <loop>, per ES5 12.6.4.
   956          */
   957         if (!(flags & JSITER_ENUMERATE) || !vp.isNullOrUndefined()) {
   958             obj = ToObject(cx, vp);
   959             if (!obj)
   960                 return false;
   961         }
   962     }
   964     return GetIterator(cx, obj, flags, vp);
   965 }
   967 bool
   968 js::CloseIterator(JSContext *cx, HandleObject obj)
   969 {
   970     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
   972     if (obj->is<PropertyIteratorObject>()) {
   973         /* Remove enumerators from the active list, which is a stack. */
   974         NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
   976         if (ni->flags & JSITER_ENUMERATE) {
   977             ni->unlink();
   979             JS_ASSERT(ni->flags & JSITER_ACTIVE);
   980             ni->flags &= ~JSITER_ACTIVE;
   982             /*
   983              * Reset the enumerator; it may still be in the cached iterators
   984              * for this thread, and can be reused.
   985              */
   986             ni->props_cursor = ni->props_array;
   987         }
   988     } else if (obj->is<LegacyGeneratorObject>()) {
   989         return CloseLegacyGenerator(cx, obj);
   990     }
   991     return true;
   992 }
   994 bool
   995 js::UnwindIteratorForException(JSContext *cx, HandleObject obj)
   996 {
   997     RootedValue v(cx);
   998     bool getOk = cx->getPendingException(&v);
   999     cx->clearPendingException();
  1000     if (!CloseIterator(cx, obj))
  1001         return false;
  1002     if (!getOk)
  1003         return false;
  1004     cx->setPendingException(v);
  1005     return true;
  1008 void
  1009 js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
  1011     if (obj->is<PropertyIteratorObject>()) {
  1012         NativeIterator *ni = obj->as<PropertyIteratorObject>().getNativeIterator();
  1013         if (ni->flags & JSITER_ENUMERATE)
  1014             ni->unlink();
  1018 /*
  1019  * Suppress enumeration of deleted properties. This function must be called
  1020  * when a property is deleted and there might be active enumerators.
  1022  * We maintain a list of active non-escaping for-in enumerators. To suppress
  1023  * a property, we check whether each active enumerator contains the (obj, id)
  1024  * pair and has not yet enumerated |id|. If so, and |id| is the next property,
  1025  * we simply advance the cursor. Otherwise, we delete |id| from the list.
  1027  * We do not suppress enumeration of a property deleted along an object's
  1028  * prototype chain. Only direct deletions on the object are handled.
  1030  * This function can suppress multiple properties at once. The |predicate|
  1031  * argument is an object which can be called on an id and returns true or
  1032  * false. It also must have a method |matchesAtMostOne| which allows us to
  1033  * stop searching after the first deletion if true.
  1034  */
  1035 template<typename StringPredicate>
  1036 static bool
  1037 SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate predicate)
  1039     NativeIterator *enumeratorList = cx->compartment()->enumerators;
  1040     NativeIterator *ni = enumeratorList->next();
  1042     while (ni != enumeratorList) {
  1043       again:
  1044         /* This only works for identified suppressed keys, not values. */
  1045         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
  1046             /* Check whether id is still to come. */
  1047             HeapPtr<JSFlatString> *props_cursor = ni->current();
  1048             HeapPtr<JSFlatString> *props_end = ni->end();
  1049             for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
  1050                 if (predicate(*idp)) {
  1051                     /*
  1052                      * Check whether another property along the prototype chain
  1053                      * became visible as a result of this deletion.
  1054                      */
  1055                     RootedObject proto(cx);
  1056                     if (!JSObject::getProto(cx, obj, &proto))
  1057                         return false;
  1058                     if (proto) {
  1059                         RootedObject obj2(cx);
  1060                         RootedShape prop(cx);
  1061                         RootedId id(cx);
  1062                         RootedValue idv(cx, StringValue(*idp));
  1063                         if (!ValueToId<CanGC>(cx, idv, &id))
  1064                             return false;
  1065                         if (!JSObject::lookupGeneric(cx, proto, id, &obj2, &prop))
  1066                             return false;
  1067                         if (prop) {
  1068                             unsigned attrs;
  1069                             if (obj2->isNative())
  1070                                 attrs = GetShapeAttributes(obj2, prop);
  1071                             else if (!JSObject::getGenericAttributes(cx, obj2, id, &attrs))
  1072                                 return false;
  1074                             if (attrs & JSPROP_ENUMERATE)
  1075                                 continue;
  1079                     /*
  1080                      * If lookupProperty or getAttributes above removed a property from
  1081                      * ni, start over.
  1082                      */
  1083                     if (props_end != ni->props_end || props_cursor != ni->props_cursor)
  1084                         goto again;
  1086                     /*
  1087                      * No property along the prototype chain stepped in to take the
  1088                      * property's place, so go ahead and delete id from the list.
  1089                      * If it is the next property to be enumerated, just skip it.
  1090                      */
  1091                     if (idp == props_cursor) {
  1092                         ni->incCursor();
  1093                     } else {
  1094                         for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
  1095                             *p = *(p + 1);
  1096                         ni->props_end = ni->end() - 1;
  1098                         /*
  1099                          * This invokes the pre barrier on this element, since
  1100                          * it's no longer going to be marked, and ensures that
  1101                          * any existing remembered set entry will be dropped.
  1102                          */
  1103                         *ni->props_end = nullptr;
  1106                     /* Don't reuse modified native iterators. */
  1107                     ni->flags |= JSITER_UNREUSABLE;
  1109                     if (predicate.matchesAtMostOne())
  1110                         break;
  1114         ni = ni->next();
  1116     return true;
  1119 namespace {
  1121 class SingleStringPredicate {
  1122     Handle<JSFlatString*> str;
  1123 public:
  1124     SingleStringPredicate(Handle<JSFlatString*> str) : str(str) {}
  1126     bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
  1127     bool matchesAtMostOne() { return true; }
  1128 };
  1130 } /* anonymous namespace */
  1132 bool
  1133 js_SuppressDeletedProperty(JSContext *cx, HandleObject obj, jsid id)
  1135     Rooted<JSFlatString*> str(cx, IdToString(cx, id));
  1136     if (!str)
  1137         return false;
  1138     return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
  1141 bool
  1142 js_SuppressDeletedElement(JSContext *cx, HandleObject obj, uint32_t index)
  1144     RootedId id(cx);
  1145     if (!IndexToId(cx, index, &id))
  1146         return false;
  1147     return js_SuppressDeletedProperty(cx, obj, id);
  1150 namespace {
  1152 class IndexRangePredicate {
  1153     uint32_t begin, end;
  1155   public:
  1156     IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
  1158     bool operator()(JSFlatString *str) {
  1159         uint32_t index;
  1160         return str->isIndex(&index) && begin <= index && index < end;
  1163     bool matchesAtMostOne() { return false; }
  1164 };
  1166 } /* anonymous namespace */
  1168 bool
  1169 js_SuppressDeletedElements(JSContext *cx, HandleObject obj, uint32_t begin, uint32_t end)
  1171     return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
  1174 bool
  1175 js_IteratorMore(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
  1177     /* Fast path for native iterators */
  1178     NativeIterator *ni = nullptr;
  1179     if (iterobj->is<PropertyIteratorObject>()) {
  1180         /* Key iterators are handled by fast-paths. */
  1181         ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
  1182         bool more = ni->props_cursor < ni->props_end;
  1183         if (ni->isKeyIter() || !more) {
  1184             rval.setBoolean(more);
  1185             return true;
  1189     /* We might still have a pending value. */
  1190     if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
  1191         rval.setBoolean(true);
  1192         return true;
  1195     /* We're reentering below and can call anything. */
  1196     JS_CHECK_RECURSION(cx, return false);
  1198     /* Fetch and cache the next value from the iterator. */
  1199     if (ni) {
  1200         JS_ASSERT(!ni->isKeyIter());
  1201         RootedId id(cx);
  1202         RootedValue current(cx, StringValue(*ni->current()));
  1203         if (!ValueToId<CanGC>(cx, current, &id))
  1204             return false;
  1205         ni->incCursor();
  1206         RootedObject obj(cx, ni->obj);
  1207         if (!JSObject::getGeneric(cx, obj, obj, id, rval))
  1208             return false;
  1209         if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, rval, rval))
  1210             return false;
  1211     } else {
  1212         /* Call the iterator object's .next method. */
  1213         if (!JSObject::getProperty(cx, iterobj, iterobj, cx->names().next, rval))
  1214             return false;
  1215         if (!Invoke(cx, ObjectValue(*iterobj), rval, 0, nullptr, rval)) {
  1216             /* Check for StopIteration. */
  1217             if (!cx->isExceptionPending())
  1218                 return false;
  1219             RootedValue exception(cx);
  1220             if (!cx->getPendingException(&exception))
  1221                 return false;
  1222             if (!JS_IsStopIteration(exception))
  1223                 return false;
  1225             cx->clearPendingException();
  1226             cx->iterValue.setMagic(JS_NO_ITER_VALUE);
  1227             rval.setBoolean(false);
  1228             return true;
  1232     /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
  1233     JS_ASSERT(!rval.isMagic(JS_NO_ITER_VALUE));
  1234     cx->iterValue = rval;
  1235     rval.setBoolean(true);
  1236     return true;
  1239 bool
  1240 js_IteratorNext(JSContext *cx, HandleObject iterobj, MutableHandleValue rval)
  1242     /* Fast path for native iterators */
  1243     if (iterobj->is<PropertyIteratorObject>()) {
  1244         /*
  1245          * Implement next directly as all the methods of the native iterator are
  1246          * read-only and permanent.
  1247          */
  1248         NativeIterator *ni = iterobj->as<PropertyIteratorObject>().getNativeIterator();
  1249         if (ni->isKeyIter()) {
  1250             JS_ASSERT(ni->props_cursor < ni->props_end);
  1251             rval.setString(*ni->current());
  1252             ni->incCursor();
  1253             return true;
  1257     JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
  1258     rval.set(cx->iterValue);
  1259     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
  1261     return true;
  1264 static bool
  1265 stopiter_hasInstance(JSContext *cx, HandleObject obj, MutableHandleValue v, bool *bp)
  1267     *bp = JS_IsStopIteration(v);
  1268     return true;
  1271 const Class StopIterationObject::class_ = {
  1272     "StopIteration",
  1273     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
  1274     JS_PropertyStub,         /* addProperty */
  1275     JS_DeletePropertyStub,   /* delProperty */
  1276     JS_PropertyStub,         /* getProperty */
  1277     JS_StrictPropertyStub,   /* setProperty */
  1278     JS_EnumerateStub,
  1279     JS_ResolveStub,
  1280     JS_ConvertStub,
  1281     nullptr,                 /* finalize    */
  1282     nullptr,                 /* call        */
  1283     stopiter_hasInstance,
  1284     nullptr                  /* construct   */
  1285 };
  1287 bool
  1288 ForOfIterator::init(HandleValue iterable, NonIterableBehavior nonIterableBehavior)
  1290     JSContext *cx = cx_;
  1291     RootedObject iterableObj(cx, ToObject(cx, iterable));
  1292     if (!iterableObj)
  1293         return false;
  1295     JS_ASSERT(index == NOT_ARRAY);
  1297     // Check the PIC first for a match.
  1298     if (iterableObj->is<ArrayObject>()) {
  1299         ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx);
  1300         if (!stubChain)
  1301             return false;
  1303         bool optimized;
  1304         if (!stubChain->tryOptimizeArray(cx, iterableObj, &optimized))
  1305             return false;
  1307         if (optimized) {
  1308             // Got optimized stub.  Array is optimizable.
  1309             index = 0;
  1310             iterator = iterableObj;
  1311             return true;
  1315     JS_ASSERT(index == NOT_ARRAY);
  1317     // The iterator is the result of calling obj[@@iterator]().
  1318     InvokeArgs args(cx);
  1319     if (!args.init(0))
  1320         return false;
  1321     args.setThis(ObjectValue(*iterableObj));
  1323     RootedValue callee(cx);
  1324     if (!JSObject::getProperty(cx, iterableObj, iterableObj, cx->names().std_iterator, &callee))
  1325         return false;
  1327     // Throw if obj[@@iterator] isn't callable if we were asked to do so.
  1328     // js::Invoke is about to check for this kind of error anyway, but it would
  1329     // throw an inscrutable error message about |method| rather than this nice
  1330     // one about |obj|.
  1331     if (!callee.isObject() || !callee.toObject().isCallable()) {
  1332         if (nonIterableBehavior == AllowNonIterable)
  1333             return true;
  1334         char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, iterable, NullPtr());
  1335         if (!bytes)
  1336             return false;
  1337         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_ITERABLE, bytes);
  1338         js_free(bytes);
  1339         return false;
  1342     args.setCallee(callee);
  1343     if (!Invoke(cx, args))
  1344         return false;
  1346     iterator = ToObject(cx, args.rval());
  1347     if (!iterator)
  1348         return false;
  1350     return true;
  1353 inline bool
  1354 ForOfIterator::nextFromOptimizedArray(MutableHandleValue vp, bool *done)
  1356     JS_ASSERT(index != NOT_ARRAY);
  1358     if (!CheckForInterrupt(cx_))
  1359         return false;
  1361     JS_ASSERT(iterator->isNative());
  1362     JS_ASSERT(iterator->is<ArrayObject>());
  1364     if (index >= iterator->as<ArrayObject>().length()) {
  1365         vp.setUndefined();
  1366         *done = true;
  1367         return true;
  1369     *done = false;
  1371     // Try to get array element via direct access.
  1372     if (index < iterator->getDenseInitializedLength()) {
  1373         vp.set(iterator->getDenseElement(index));
  1374         if (!vp.isMagic(JS_ELEMENTS_HOLE)) {
  1375             ++index;
  1376             return true;
  1380     return JSObject::getElement(cx_, iterator, iterator, index++, vp);
  1383 bool
  1384 ForOfIterator::next(MutableHandleValue vp, bool *done)
  1386     JS_ASSERT(iterator);
  1388     if (index != NOT_ARRAY) {
  1389         ForOfPIC::Chain *stubChain = ForOfPIC::getOrCreate(cx_);
  1390         if (!stubChain)
  1391             return false;
  1393         if (stubChain->isArrayNextStillSane())
  1394             return nextFromOptimizedArray(vp, done);
  1396         // ArrayIterator.prototype.next changed, materialize a proper
  1397         // ArrayIterator instance and fall through to slowpath case.
  1398         if (!materializeArrayIterator())
  1399             return false;
  1402     RootedValue method(cx_);
  1403     if (!JSObject::getProperty(cx_, iterator, iterator, cx_->names().next, &method))
  1404         return false;
  1406     InvokeArgs args(cx_);
  1407     if (!args.init(1))
  1408         return false;
  1409     args.setCallee(method);
  1410     args.setThis(ObjectValue(*iterator));
  1411     args[0].setUndefined();
  1412     if (!Invoke(cx_, args))
  1413         return false;
  1415     RootedObject resultObj(cx_, ToObject(cx_, args.rval()));
  1416     if (!resultObj)
  1417         return false;
  1418     RootedValue doneVal(cx_);
  1419     if (!JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().done, &doneVal))
  1420         return false;
  1421     *done = ToBoolean(doneVal);
  1422     if (*done) {
  1423         vp.setUndefined();
  1424         return true;
  1426     return JSObject::getProperty(cx_, resultObj, resultObj, cx_->names().value, vp);
  1429 bool
  1430 ForOfIterator::materializeArrayIterator()
  1432     JS_ASSERT(index != NOT_ARRAY);
  1434     const char *nameString = "ArrayValuesAt";
  1436     RootedAtom name(cx_, Atomize(cx_, nameString, strlen(nameString)));
  1437     if (!name)
  1438         return false;
  1440     RootedValue val(cx_);
  1441     if (!cx_->global()->getSelfHostedFunction(cx_, name, name, 1, &val))
  1442         return false;
  1444     InvokeArgs args(cx_);
  1445     if (!args.init(1))
  1446         return false;
  1447     args.setCallee(val);
  1448     args.setThis(ObjectValue(*iterator));
  1449     args[0].set(Int32Value(index));
  1450     if (!Invoke(cx_, args))
  1451         return false;
  1453     index = NOT_ARRAY;
  1454     // Result of call to ArrayValuesAt must be an object.
  1455     iterator = &args.rval().toObject();
  1456     return true;
  1459 /*** Generators **********************************************************************************/
  1461 template<typename T>
  1462 static void
  1463 FinalizeGenerator(FreeOp *fop, JSObject *obj)
  1465     JS_ASSERT(obj->is<T>());
  1466     JSGenerator *gen = obj->as<T>().getGenerator();
  1467     JS_ASSERT(gen);
  1468     // gen is open when a script has not called its close method while
  1469     // explicitly manipulating it.
  1470     JS_ASSERT(gen->state == JSGEN_NEWBORN ||
  1471               gen->state == JSGEN_CLOSED ||
  1472               gen->state == JSGEN_OPEN);
  1473     // If gen->state is JSGEN_CLOSED, gen->fp may be nullptr.
  1474     if (gen->fp)
  1475         JS_POISON(gen->fp, JS_SWEPT_FRAME_PATTERN, sizeof(InterpreterFrame));
  1476     JS_POISON(gen, JS_SWEPT_FRAME_PATTERN, sizeof(JSGenerator));
  1477     fop->free_(gen);
  1480 static void
  1481 MarkGeneratorFrame(JSTracer *trc, JSGenerator *gen)
  1483     MarkValueRange(trc,
  1484                    HeapValueify(gen->fp->generatorArgsSnapshotBegin()),
  1485                    HeapValueify(gen->fp->generatorArgsSnapshotEnd()),
  1486                    "Generator Floating Args");
  1487     gen->fp->mark(trc);
  1488     MarkValueRange(trc,
  1489                    HeapValueify(gen->fp->generatorSlotsSnapshotBegin()),
  1490                    HeapValueify(gen->regs.sp),
  1491                    "Generator Floating Stack");
  1494 static void
  1495 GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
  1497     JS::Zone *zone = cx->zone();
  1498     if (zone->needsBarrier())
  1499         MarkGeneratorFrame(zone->barrierTracer(), gen);
  1502 static void
  1503 GeneratorWriteBarrierPost(JSContext *cx, JSGenerator *gen)
  1505 #ifdef JSGC_GENERATIONAL
  1506     cx->runtime()->gcStoreBuffer.putWholeCell(gen->obj);
  1507 #endif
  1510 /*
  1511  * Only mark generator frames/slots when the generator is not active on the
  1512  * stack or closed. Barriers when copying onto the stack or closing preserve
  1513  * gc invariants.
  1514  */
  1515 static bool
  1516 GeneratorHasMarkableFrame(JSGenerator *gen)
  1518     return gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN;
  1521 /*
  1522  * When a generator is closed, the GC things reachable from the contained frame
  1523  * and slots become unreachable and thus require a write barrier.
  1524  */
  1525 static void
  1526 SetGeneratorClosed(JSContext *cx, JSGenerator *gen)
  1528     JS_ASSERT(gen->state != JSGEN_CLOSED);
  1529     if (GeneratorHasMarkableFrame(gen))
  1530         GeneratorWriteBarrierPre(cx, gen);
  1531     gen->state = JSGEN_CLOSED;
  1533 #ifdef DEBUG
  1534     MakeRangeGCSafe(gen->fp->generatorArgsSnapshotBegin(),
  1535                     gen->fp->generatorArgsSnapshotEnd());
  1536     MakeRangeGCSafe(gen->fp->generatorSlotsSnapshotBegin(),
  1537                     gen->regs.sp);
  1538     PodZero(&gen->regs, 1);
  1539     gen->fp = nullptr;
  1540 #endif
  1543 template<typename T>
  1544 static void
  1545 TraceGenerator(JSTracer *trc, JSObject *obj)
  1547     JS_ASSERT(obj->is<T>());
  1548     JSGenerator *gen = obj->as<T>().getGenerator();
  1549     JS_ASSERT(gen);
  1550     if (GeneratorHasMarkableFrame(gen))
  1551         MarkGeneratorFrame(trc, gen);
  1554 GeneratorState::GeneratorState(JSContext *cx, JSGenerator *gen, JSGeneratorState futureState)
  1555   : RunState(cx, Generator, gen->fp->script()),
  1556     cx_(cx),
  1557     gen_(gen),
  1558     futureState_(futureState),
  1559     entered_(false)
  1560 { }
  1562 GeneratorState::~GeneratorState()
  1564     gen_->fp->setSuspended();
  1566     if (entered_)
  1567         cx_->leaveGenerator(gen_);
  1570 InterpreterFrame *
  1571 GeneratorState::pushInterpreterFrame(JSContext *cx)
  1573     /*
  1574      * Write barrier is needed since the generator stack can be updated,
  1575      * and it's not barriered in any other way. We need to do it before
  1576      * gen->state changes, which can cause us to trace the generator
  1577      * differently.
  1579      * We could optimize this by setting a bit on the generator to signify
  1580      * that it has been marked. If this bit has already been set, there is no
  1581      * need to mark again. The bit would have to be reset before the next GC,
  1582      * or else some kind of epoch scheme would have to be used.
  1583      */
  1584     GeneratorWriteBarrierPre(cx, gen_);
  1585     gen_->state = futureState_;
  1587     gen_->fp->clearSuspended();
  1589     cx->enterGenerator(gen_);   /* OOM check above. */
  1590     entered_ = true;
  1591     return gen_->fp;
  1594 const Class LegacyGeneratorObject::class_ = {
  1595     "Generator",
  1596     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
  1597     JS_PropertyStub,         /* addProperty */
  1598     JS_DeletePropertyStub,   /* delProperty */
  1599     JS_PropertyStub,         /* getProperty */
  1600     JS_StrictPropertyStub,   /* setProperty */
  1601     JS_EnumerateStub,
  1602     JS_ResolveStub,
  1603     JS_ConvertStub,
  1604     FinalizeGenerator<LegacyGeneratorObject>,
  1605     nullptr,                 /* call        */
  1606     nullptr,                 /* hasInstance */
  1607     nullptr,                 /* construct   */
  1608     TraceGenerator<LegacyGeneratorObject>,
  1609     JS_NULL_CLASS_SPEC,
  1611         nullptr,             /* outerObject    */
  1612         nullptr,             /* innerObject    */
  1613         iterator_iteratorObject,
  1615 };
  1617 const Class StarGeneratorObject::class_ = {
  1618     "Generator",
  1619     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
  1620     JS_PropertyStub,         /* addProperty */
  1621     JS_DeletePropertyStub,   /* delProperty */
  1622     JS_PropertyStub,         /* getProperty */
  1623     JS_StrictPropertyStub,   /* setProperty */
  1624     JS_EnumerateStub,
  1625     JS_ResolveStub,
  1626     JS_ConvertStub,
  1627     FinalizeGenerator<StarGeneratorObject>,
  1628     nullptr,                 /* call        */
  1629     nullptr,                 /* hasInstance */
  1630     nullptr,                 /* construct   */
  1631     TraceGenerator<StarGeneratorObject>,
  1632     JS_NULL_CLASS_SPEC,
  1634         nullptr,             /* outerObject    */
  1635         nullptr,             /* innerObject    */
  1636         iterator_iteratorObject,
  1638 };
  1640 /*
  1641  * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
  1642  * to the frame by which the generator function was activated.  Create a new
  1643  * JSGenerator object, which contains its own InterpreterFrame that we populate
  1644  * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
  1645  * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
  1646  * if they are non-null.
  1647  */
  1648 JSObject *
  1649 js_NewGenerator(JSContext *cx, const InterpreterRegs &stackRegs)
  1651     JS_ASSERT(stackRegs.stackDepth() == 0);
  1652     InterpreterFrame *stackfp = stackRegs.fp();
  1654     JS_ASSERT(stackfp->script()->isGenerator());
  1656     Rooted<GlobalObject*> global(cx, &stackfp->global());
  1657     RootedObject obj(cx);
  1658     if (stackfp->script()->isStarGenerator()) {
  1659         RootedValue pval(cx);
  1660         RootedObject fun(cx, stackfp->fun());
  1661         // FIXME: This would be faster if we could avoid doing a lookup to get
  1662         // the prototype for the instance.  Bug 906600.
  1663         if (!JSObject::getProperty(cx, fun, fun, cx->names().prototype, &pval))
  1664             return nullptr;
  1665         JSObject *proto = pval.isObject() ? &pval.toObject() : nullptr;
  1666         if (!proto) {
  1667             proto = GlobalObject::getOrCreateStarGeneratorObjectPrototype(cx, global);
  1668             if (!proto)
  1669                 return nullptr;
  1671         obj = NewObjectWithGivenProto(cx, &StarGeneratorObject::class_, proto, global);
  1672     } else {
  1673         JS_ASSERT(stackfp->script()->isLegacyGenerator());
  1674         JSObject *proto = GlobalObject::getOrCreateLegacyGeneratorObjectPrototype(cx, global);
  1675         if (!proto)
  1676             return nullptr;
  1677         obj = NewObjectWithGivenProto(cx, &LegacyGeneratorObject::class_, proto, global);
  1679     if (!obj)
  1680         return nullptr;
  1682     /* Load and compute stack slot counts. */
  1683     Value *stackvp = stackfp->generatorArgsSnapshotBegin();
  1684     unsigned vplen = stackfp->generatorArgsSnapshotEnd() - stackvp;
  1686     /* Compute JSGenerator size. */
  1687     unsigned nbytes = sizeof(JSGenerator) +
  1688                    (-1 + /* one Value included in JSGenerator */
  1689                     vplen +
  1690                     VALUES_PER_STACK_FRAME +
  1691                     stackfp->script()->nslots()) * sizeof(HeapValue);
  1693     JS_ASSERT(nbytes % sizeof(Value) == 0);
  1694     JS_STATIC_ASSERT(sizeof(InterpreterFrame) % sizeof(HeapValue) == 0);
  1696     JSGenerator *gen = (JSGenerator *) cx->calloc_(nbytes);
  1697     if (!gen)
  1698         return nullptr;
  1700     /* Cut up floatingStack space. */
  1701     HeapValue *genvp = gen->stackSnapshot;
  1702     SetValueRangeToUndefined((Value *)genvp, vplen);
  1704     InterpreterFrame *genfp = reinterpret_cast<InterpreterFrame *>(genvp + vplen);
  1706     /* Initialize JSGenerator. */
  1707     gen->obj.init(obj);
  1708     gen->state = JSGEN_NEWBORN;
  1709     gen->fp = genfp;
  1710     gen->prevGenerator = nullptr;
  1712     /* Copy from the stack to the generator's floating frame. */
  1713     gen->regs.rebaseFromTo(stackRegs, *genfp);
  1714     genfp->copyFrameAndValues<InterpreterFrame::DoPostBarrier>(cx, (Value *)genvp, stackfp,
  1715                                                          stackvp, stackRegs.sp);
  1716     genfp->setSuspended();
  1717     obj->setPrivate(gen);
  1718     return obj;
  1721 static void
  1722 SetGeneratorClosed(JSContext *cx, JSGenerator *gen);
  1724 typedef enum JSGeneratorOp {
  1725     JSGENOP_NEXT,
  1726     JSGENOP_SEND,
  1727     JSGENOP_THROW,
  1728     JSGENOP_CLOSE
  1729 } JSGeneratorOp;
  1731 /*
  1732  * Start newborn or restart yielding generator and perform the requested
  1733  * operation inside its frame.
  1734  */
  1735 static bool
  1736 SendToGenerator(JSContext *cx, JSGeneratorOp op, HandleObject obj,
  1737                 JSGenerator *gen, HandleValue arg, GeneratorKind generatorKind,
  1738                 MutableHandleValue rval)
  1740     JS_ASSERT(generatorKind == LegacyGenerator || generatorKind == StarGenerator);
  1742     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
  1743         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NESTING_GENERATOR);
  1744         return false;
  1747     JSGeneratorState futureState;
  1748     JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
  1749     switch (op) {
  1750       case JSGENOP_NEXT:
  1751       case JSGENOP_SEND:
  1752         if (gen->state == JSGEN_OPEN) {
  1753             /*
  1754              * Store the argument to send as the result of the yield
  1755              * expression. The generator stack is not barriered, so we need
  1756              * write barriers here.
  1757              */
  1758             HeapValue::writeBarrierPre(gen->regs.sp[-1]);
  1759             gen->regs.sp[-1] = arg;
  1760             HeapValue::writeBarrierPost(cx->runtime(), gen->regs.sp[-1], &gen->regs.sp[-1]);
  1762         futureState = JSGEN_RUNNING;
  1763         break;
  1765       case JSGENOP_THROW:
  1766         cx->setPendingException(arg);
  1767         futureState = JSGEN_RUNNING;
  1768         break;
  1770       default:
  1771         JS_ASSERT(op == JSGENOP_CLOSE);
  1772         JS_ASSERT(generatorKind == LegacyGenerator);
  1773         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
  1774         futureState = JSGEN_CLOSING;
  1775         break;
  1778     bool ok;
  1780         GeneratorState state(cx, gen, futureState);
  1781         ok = RunScript(cx, state);
  1782         if (!ok && gen->state == JSGEN_CLOSED)
  1783             return false;
  1786     if (gen->fp->isYielding()) {
  1787         /*
  1788          * Yield is ordinarily infallible, but ok can be false here if a
  1789          * Debugger.Frame.onPop hook fails.
  1790          */
  1791         JS_ASSERT(gen->state == JSGEN_RUNNING);
  1792         JS_ASSERT(op != JSGENOP_CLOSE);
  1793         gen->fp->clearYielding();
  1794         gen->state = JSGEN_OPEN;
  1795         GeneratorWriteBarrierPost(cx, gen);
  1796         rval.set(gen->fp->returnValue());
  1797         return ok;
  1800     if (ok) {
  1801         if (generatorKind == StarGenerator) {
  1802             // Star generators return a {value:FOO, done:true} object.
  1803             rval.set(gen->fp->returnValue());
  1804         } else {
  1805             JS_ASSERT(generatorKind == LegacyGenerator);
  1807             // Otherwise we discard the return value and throw a StopIteration
  1808             // if needed.
  1809             rval.setUndefined();
  1810             if (op != JSGENOP_CLOSE)
  1811                 ok = js_ThrowStopIteration(cx);
  1815     SetGeneratorClosed(cx, gen);
  1816     return ok;
  1819 MOZ_ALWAYS_INLINE bool
  1820 star_generator_next(JSContext *cx, CallArgs args)
  1822     RootedObject thisObj(cx, &args.thisv().toObject());
  1823     JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
  1825     if (gen->state == JSGEN_CLOSED) {
  1826         RootedObject obj(cx, CreateItrResultObject(cx, JS::UndefinedHandleValue, true));
  1827         if (!obj)
  1828             return false;
  1829         args.rval().setObject(*obj);
  1830         return true;
  1833     if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
  1834         RootedValue val(cx, args[0]);
  1835         js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
  1836                             JSDVG_SEARCH_STACK, val, js::NullPtr());
  1837         return false;
  1840     return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), StarGenerator,
  1841                            args.rval());
  1844 MOZ_ALWAYS_INLINE bool
  1845 star_generator_throw(JSContext *cx, CallArgs args)
  1847     RootedObject thisObj(cx, &args.thisv().toObject());
  1849     JSGenerator *gen = thisObj->as<StarGeneratorObject>().getGenerator();
  1850     if (gen->state == JSGEN_CLOSED) {
  1851         cx->setPendingException(args.get(0));
  1852         return false;
  1855     return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), StarGenerator,
  1856                            args.rval());
  1859 MOZ_ALWAYS_INLINE bool
  1860 legacy_generator_next(JSContext *cx, CallArgs args)
  1862     RootedObject thisObj(cx, &args.thisv().toObject());
  1864     JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
  1865     if (gen->state == JSGEN_CLOSED)
  1866         return js_ThrowStopIteration(cx);
  1868     if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
  1869         RootedValue val(cx, args[0]);
  1870         js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
  1871                             JSDVG_SEARCH_STACK, val, js::NullPtr());
  1872         return false;
  1875     return SendToGenerator(cx, JSGENOP_SEND, thisObj, gen, args.get(0), LegacyGenerator,
  1876                            args.rval());
  1879 MOZ_ALWAYS_INLINE bool
  1880 legacy_generator_throw(JSContext *cx, CallArgs args)
  1882     RootedObject thisObj(cx, &args.thisv().toObject());
  1884     JSGenerator *gen = thisObj->as<LegacyGeneratorObject>().getGenerator();
  1885     if (gen->state == JSGEN_CLOSED) {
  1886         cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
  1887         return false;
  1890     return SendToGenerator(cx, JSGENOP_THROW, thisObj, gen, args.get(0), LegacyGenerator,
  1891                            args.rval());
  1894 static bool
  1895 CloseLegacyGenerator(JSContext *cx, HandleObject obj, MutableHandleValue rval)
  1897     JS_ASSERT(obj->is<LegacyGeneratorObject>());
  1899     JSGenerator *gen = obj->as<LegacyGeneratorObject>().getGenerator();
  1901     if (gen->state == JSGEN_CLOSED) {
  1902         rval.setUndefined();
  1903         return true;
  1906     if (gen->state == JSGEN_NEWBORN) {
  1907         SetGeneratorClosed(cx, gen);
  1908         rval.setUndefined();
  1909         return true;
  1912     return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JS::UndefinedHandleValue, LegacyGenerator,
  1913                            rval);
  1916 static bool
  1917 CloseLegacyGenerator(JSContext *cx, HandleObject obj)
  1919     RootedValue rval(cx);
  1920     return CloseLegacyGenerator(cx, obj, &rval);
  1923 MOZ_ALWAYS_INLINE bool
  1924 legacy_generator_close(JSContext *cx, CallArgs args)
  1926     RootedObject thisObj(cx, &args.thisv().toObject());
  1928     return CloseLegacyGenerator(cx, thisObj, args.rval());
  1931 template<typename T>
  1932 MOZ_ALWAYS_INLINE bool
  1933 IsObjectOfType(HandleValue v)
  1935     return v.isObject() && v.toObject().is<T>();
  1938 template<typename T, NativeImpl Impl>
  1939 static bool
  1940 NativeMethod(JSContext *cx, unsigned argc, Value *vp)
  1942     CallArgs args = CallArgsFromVp(argc, vp);
  1943     return CallNonGenericMethod<IsObjectOfType<T>, Impl>(cx, args);
  1946 #define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
  1947 #define JS_METHOD(name, T, impl, len, attrs) JS_FN(name, (NativeMethod<T,impl>), len, attrs)
  1949 static const JSFunctionSpec star_generator_methods[] = {
  1950     JS_SELF_HOSTED_FN("@@iterator", "IteratorIdentity", 0, 0),
  1951     JS_METHOD("next", StarGeneratorObject, star_generator_next, 1, 0),
  1952     JS_METHOD("throw", StarGeneratorObject, star_generator_throw, 1, 0),
  1953     JS_FS_END
  1954 };
  1956 static const JSFunctionSpec legacy_generator_methods[] = {
  1957     JS_SELF_HOSTED_FN("@@iterator", "LegacyGeneratorIteratorShim", 0, 0),
  1958     // "send" is an alias for "next".
  1959     JS_METHOD("next", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
  1960     JS_METHOD("send", LegacyGeneratorObject, legacy_generator_next, 1, JSPROP_ROPERM),
  1961     JS_METHOD("throw", LegacyGeneratorObject, legacy_generator_throw, 1, JSPROP_ROPERM),
  1962     JS_METHOD("close", LegacyGeneratorObject, legacy_generator_close, 0, JSPROP_ROPERM),
  1963     JS_FS_END
  1964 };
  1966 static JSObject*
  1967 NewSingletonObjectWithObjectPrototype(JSContext *cx, Handle<GlobalObject *> global)
  1969     JSObject *proto = global->getOrCreateObjectPrototype(cx);
  1970     if (!proto)
  1971         return nullptr;
  1972     return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
  1975 static JSObject*
  1976 NewSingletonObjectWithFunctionPrototype(JSContext *cx, Handle<GlobalObject *> global)
  1978     JSObject *proto = global->getOrCreateFunctionPrototype(cx);
  1979     if (!proto)
  1980         return nullptr;
  1981     return NewObjectWithGivenProto(cx, &JSObject::class_, proto, global, SingletonObject);
  1984 /* static */ bool
  1985 GlobalObject::initIteratorClasses(JSContext *cx, Handle<GlobalObject *> global)
  1987     RootedObject iteratorProto(cx);
  1988     Value iteratorProtoVal = global->getPrototype(JSProto_Iterator);
  1989     if (iteratorProtoVal.isObject()) {
  1990         iteratorProto = &iteratorProtoVal.toObject();
  1991     } else {
  1992         iteratorProto = global->createBlankPrototype(cx, &PropertyIteratorObject::class_);
  1993         if (!iteratorProto)
  1994             return false;
  1996         AutoIdVector blank(cx);
  1997         NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
  1998         if (!ni)
  1999             return false;
  2000         ni->init(nullptr, nullptr, 0 /* flags */, 0, 0);
  2002         iteratorProto->as<PropertyIteratorObject>().setNativeIterator(ni);
  2004         Rooted<JSFunction*> ctor(cx);
  2005         ctor = global->createConstructor(cx, IteratorConstructor, cx->names().Iterator, 2);
  2006         if (!ctor)
  2007             return false;
  2008         if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
  2009             return false;
  2010         if (!DefinePropertiesAndBrand(cx, iteratorProto, nullptr, iterator_methods))
  2011             return false;
  2012         if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_Iterator,
  2013                                                   ctor, iteratorProto))
  2015             return false;
  2019     RootedObject proto(cx);
  2020     if (global->getSlot(ARRAY_ITERATOR_PROTO).isUndefined()) {
  2021         const Class *cls = &ArrayIteratorObject::class_;
  2022         proto = global->createBlankPrototypeInheriting(cx, cls, *iteratorProto);
  2023         if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, array_iterator_methods))
  2024             return false;
  2025         global->setReservedSlot(ARRAY_ITERATOR_PROTO, ObjectValue(*proto));
  2028     if (global->getSlot(STRING_ITERATOR_PROTO).isUndefined()) {
  2029         const Class *cls = &StringIteratorPrototypeClass;
  2030         proto = global->createBlankPrototype(cx, cls);
  2031         if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, string_iterator_methods))
  2032             return false;
  2033         global->setReservedSlot(STRING_ITERATOR_PROTO, ObjectValue(*proto));
  2036     if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
  2037         proto = NewSingletonObjectWithObjectPrototype(cx, global);
  2038         if (!proto || !DefinePropertiesAndBrand(cx, proto, nullptr, legacy_generator_methods))
  2039             return false;
  2040         global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
  2043     if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
  2044         RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
  2045         if (!genObjectProto)
  2046             return false;
  2047         if (!DefinePropertiesAndBrand(cx, genObjectProto, nullptr, star_generator_methods))
  2048             return false;
  2050         RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
  2051         if (!genFunctionProto)
  2052             return false;
  2053         if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
  2054             return false;
  2056         RootedValue function(cx, global->getConstructor(JSProto_Function));
  2057         if (!function.toObjectOrNull())
  2058             return false;
  2059         RootedAtom name(cx, cx->names().GeneratorFunction);
  2060         RootedObject genFunction(cx, NewFunctionWithProto(cx, NullPtr(), Generator, 1,
  2061                                                           JSFunction::NATIVE_CTOR, global, name,
  2062                                                           &function.toObject()));
  2063         if (!genFunction)
  2064             return false;
  2065         if (!LinkConstructorAndPrototype(cx, genFunction, genFunctionProto))
  2066             return false;
  2068         global->setSlot(STAR_GENERATOR_OBJECT_PROTO, ObjectValue(*genObjectProto));
  2069         global->setConstructor(JSProto_GeneratorFunction, ObjectValue(*genFunction));
  2070         global->setPrototype(JSProto_GeneratorFunction, ObjectValue(*genFunctionProto));
  2073     if (global->getPrototype(JSProto_StopIteration).isUndefined()) {
  2074         proto = global->createBlankPrototype(cx, &StopIterationObject::class_);
  2075         if (!proto || !JSObject::freeze(cx, proto))
  2076             return false;
  2078         // This should use a non-JSProtoKey'd slot, but this is easier for now.
  2079         if (!GlobalObject::initBuiltinConstructor(cx, global, JSProto_StopIteration, proto, proto))
  2080             return false;
  2082         global->setConstructor(JSProto_StopIteration, ObjectValue(*proto));
  2085     return true;
  2088 JSObject *
  2089 js_InitIteratorClasses(JSContext *cx, HandleObject obj)
  2091     Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
  2092     if (!GlobalObject::initIteratorClasses(cx, global))
  2093         return nullptr;
  2094     return global->getIteratorPrototype();

mercurial