LCOV - code coverage report
Current view: directory - js/src - jsiter.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 770 639 83.0 %
Date: 2012-06-02 Functions: 69 64 92.8 %

       1                 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=8 sw=4 et tw=78:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is Mozilla Communicator client code, released
      18                 :  * March 31, 1998.
      19                 :  *
      20                 :  * The Initial Developer of the Original Code is
      21                 :  * Netscape Communications Corporation.
      22                 :  * Portions created by the Initial Developer are Copyright (C) 1998
      23                 :  * the Initial Developer. All Rights Reserved.
      24                 :  *
      25                 :  * Contributor(s):
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either of the GNU General Public License Version 2 or later (the "GPL"),
      29                 :  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      30                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      31                 :  * of those above. If you wish to allow use of your version of this file only
      32                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      33                 :  * use your version of this file under the terms of the MPL, indicate your
      34                 :  * decision by deleting the provisions above and replace them with the notice
      35                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      36                 :  * the provisions above, a recipient may use your version of this file under
      37                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      38                 :  *
      39                 :  * ***** END LICENSE BLOCK ***** */
      40                 : 
      41                 : /*
      42                 :  * JavaScript iterators.
      43                 :  */
      44                 : #include "mozilla/Util.h"
      45                 : 
      46                 : #include "jstypes.h"
      47                 : #include "jsutil.h"
      48                 : #include "jsapi.h"
      49                 : #include "jsarray.h"
      50                 : #include "jsatom.h"
      51                 : #include "jsbool.h"
      52                 : #include "jscntxt.h"
      53                 : #include "jsversion.h"
      54                 : #include "jsexn.h"
      55                 : #include "jsfun.h"
      56                 : #include "jsgc.h"
      57                 : #include "jsgcmark.h"
      58                 : #include "jsinterp.h"
      59                 : #include "jsiter.h"
      60                 : #include "jslock.h"
      61                 : #include "jsnum.h"
      62                 : #include "jsobj.h"
      63                 : #include "jsopcode.h"
      64                 : #include "jsproxy.h"
      65                 : #include "jsscope.h"
      66                 : #include "jsscript.h"
      67                 : 
      68                 : #if JS_HAS_XML_SUPPORT
      69                 : #include "jsxml.h"
      70                 : #endif
      71                 : 
      72                 : #include "ds/Sort.h"
      73                 : #include "frontend/TokenStream.h"
      74                 : #include "vm/GlobalObject.h"
      75                 : 
      76                 : #include "jsinferinlines.h"
      77                 : #include "jsobjinlines.h"
      78                 : 
      79                 : #include "vm/MethodGuard-inl.h"
      80                 : #include "vm/Stack-inl.h"
      81                 : #include "vm/String-inl.h"
      82                 : 
      83                 : using namespace mozilla;
      84                 : using namespace js;
      85                 : using namespace js::gc;
      86                 : 
      87                 : static void iterator_finalize(JSContext *cx, JSObject *obj);
      88                 : static void iterator_trace(JSTracer *trc, JSObject *obj);
      89                 : static JSObject *iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly);
      90                 : 
      91                 : Class js::IteratorClass = {
      92                 :     "Iterator",
      93                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
      94                 :     JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
      95                 :     JS_PropertyStub,         /* addProperty */
      96                 :     JS_PropertyStub,         /* delProperty */
      97                 :     JS_PropertyStub,         /* getProperty */
      98                 :     JS_StrictPropertyStub,   /* setProperty */
      99                 :     JS_EnumerateStub,
     100                 :     JS_ResolveStub,
     101                 :     JS_ConvertStub,
     102                 :     iterator_finalize,
     103                 :     NULL,                    /* checkAccess */
     104                 :     NULL,                    /* call        */
     105                 :     NULL,                    /* construct   */
     106                 :     NULL,                    /* hasInstance */
     107                 :     iterator_trace,
     108                 :     {
     109                 :         NULL,                /* equality       */
     110                 :         NULL,                /* outerObject    */
     111                 :         NULL,                /* innerObject    */
     112                 :         iterator_iterator,
     113                 :         NULL                 /* unused  */
     114                 :     }
     115                 : };
     116                 : 
     117                 : Class js::ElementIteratorClass = {
     118                 :     "ElementIterator",
     119                 :     JSCLASS_HAS_RESERVED_SLOTS(ElementIteratorObject::NumSlots),
     120                 :     JS_PropertyStub,         /* addProperty */
     121                 :     JS_PropertyStub,         /* delProperty */
     122                 :     JS_PropertyStub,         /* getProperty */
     123                 :     JS_StrictPropertyStub,   /* setProperty */
     124                 :     JS_EnumerateStub,
     125                 :     JS_ResolveStub,
     126                 :     JS_ConvertStub,
     127                 :     NULL,                    /* finalize    */
     128                 :     NULL,                    /* checkAccess */
     129                 :     NULL,                    /* call        */
     130                 :     NULL,                    /* construct   */
     131                 :     NULL,                    /* hasInstance */
     132                 :     NULL,                    /* trace       */
     133                 :     {
     134                 :         NULL,                /* equality       */
     135                 :         NULL,                /* outerObject    */
     136                 :         NULL,                /* innerObject    */
     137                 :         iterator_iterator,
     138                 :         NULL                 /* unused  */
     139                 :     }
     140                 : };
     141                 : 
     142                 : static const gc::AllocKind ITERATOR_FINALIZE_KIND = gc::FINALIZE_OBJECT2;
     143                 : 
     144                 : void
     145           17167 : NativeIterator::mark(JSTracer *trc)
     146                 : {
     147           24812 :     for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
     148            7645 :         MarkString(trc, str, "prop");
     149           17167 :     if (obj)
     150            4137 :         MarkObject(trc, &obj, "obj");
     151           17167 : }
     152                 : 
     153                 : static void
     154          568601 : iterator_finalize(JSContext *cx, JSObject *obj)
     155                 : {
     156          568601 :     JS_ASSERT(obj->isIterator());
     157                 : 
     158          568601 :     NativeIterator *ni = obj->getNativeIterator();
     159          568601 :     if (ni) {
     160          568592 :         obj->setPrivate(NULL);
     161          568592 :         cx->free_(ni);
     162                 :     }
     163          568601 : }
     164                 : 
     165                 : static void
     166           17185 : iterator_trace(JSTracer *trc, JSObject *obj)
     167                 : {
     168           17185 :     NativeIterator *ni = obj->getNativeIterator();
     169                 : 
     170           17185 :     if (ni)
     171           17167 :         ni->mark(trc);
     172           17185 : }
     173                 : 
     174                 : struct IdHashPolicy {
     175                 :     typedef jsid Lookup;
     176        75513976 :     static HashNumber hash(jsid id) {
     177        75513976 :         return JSID_BITS(id);
     178                 :     }
     179          905679 :     static bool match(jsid id1, jsid id2) {
     180          905679 :         return id1 == id2;
     181                 :     }
     182                 : };
     183                 : 
     184                 : typedef HashSet<jsid, IdHashPolicy> IdSet;
     185                 : 
     186                 : static inline bool
     187           72757 : NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
     188                 : {
     189           72757 :     Value vec[2] = { IdToValue(id), val };
     190          145514 :     AutoArrayRooter tvr(cx, ArrayLength(vec), vec);
     191                 : 
     192           72757 :     JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
     193           72757 :     if (!aobj)
     194               0 :         return false;
     195           72757 :     rval->setObject(*aobj);
     196           72757 :     return true;
     197                 : }
     198                 : 
     199                 : static inline bool
     200        80196792 : Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
     201                 :           bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
     202                 : {
     203        80196792 :     JS_ASSERT_IF(flags & JSITER_OWNONLY, obj == pobj);
     204                 : 
     205                 :     /*
     206                 :      * We implement __proto__ using a property on |Object.prototype|, but
     207                 :      * because __proto__ is highly deserving of removal, we don't want it to
     208                 :      * show up in property enumeration, even if only for |Object.prototype|
     209                 :      * (think introspection by Prototype-like frameworks that add methods to
     210                 :      * the built-in prototypes).  So exclude __proto__ if the object where the
     211                 :      * property was found has no [[Prototype]] and might be |Object.prototype|.
     212                 :      */
     213        80196792 :     if (JS_UNLIKELY(!pobj->getProto() && JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)))
     214          562041 :         return true;
     215                 : 
     216        79634751 :     if (!(flags & JSITER_OWNONLY) || pobj->isProxy() || pobj->getOps()->enumerate) {
     217                 :         /* If we've already seen this, we definitely won't add it. */
     218        75513976 :         IdSet::AddPtr p = ht.lookupForAdd(id);
     219        75513976 :         if (JS_UNLIKELY(!!p))
     220          905679 :             return true;
     221                 : 
     222                 :         /*
     223                 :          * It's not necessary to add properties to the hash table at the end of
     224                 :          * the prototype chain, but custom enumeration behaviors might return
     225                 :          * duplicated properties, so always add in such cases.
     226                 :          */
     227        74608297 :         if ((pobj->getProto() || pobj->isProxy() || pobj->getOps()->enumerate) && !ht.add(p, id))
     228               0 :             return false;
     229                 :     }
     230                 : 
     231        78729072 :     if (enumerable || (flags & JSITER_HIDDEN))
     232        58703445 :         return props->append(id);
     233                 : 
     234        20025627 :     return true;
     235                 : }
     236                 : 
     237                 : static bool
     238         2455835 : EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, unsigned flags, IdSet &ht,
     239                 :                           AutoIdVector *props)
     240                 : {
     241         4911670 :     RootObject objRoot(cx, &obj);
     242         4911670 :     RootObject pobjRoot(cx, &pobj);
     243                 : 
     244         2455835 :     size_t initialLength = props->length();
     245                 : 
     246                 :     /* Collect all unique properties from this object's scope. */
     247         2455835 :     Shape::Range r = pobj->lastProperty()->all();
     248         4911670 :     Shape::Range::Root root(cx, &r);
     249        43071539 :     for (; !r.empty(); r.popFront()) {
     250        40615704 :         const Shape &shape = r.front();
     251                 : 
     252        81231390 :         if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) &&
     253        40615686 :             !Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
     254                 :         {
     255               0 :             return false;
     256                 :         }
     257                 :     }
     258                 : 
     259         2455835 :     ::Reverse(props->begin() + initialLength, props->end());
     260         2455835 :     return true;
     261                 : }
     262                 : 
     263                 : static bool
     264          113418 : EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, unsigned flags,
     265                 :                               IdSet &ht, AutoIdVector *props)
     266                 : {
     267          226836 :     if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false,
     268          113418 :                    flags, ht, props)) {
     269               0 :         return false;
     270                 :     }
     271                 : 
     272          113418 :     if (pobj->getArrayLength() > 0) {
     273          105347 :         size_t initlen = pobj->getDenseArrayInitializedLength();
     274          105347 :         const Value *vp = pobj->getDenseArrayElements();
     275          889307 :         for (size_t i = 0; i < initlen; ++i, ++vp) {
     276          783960 :             if (!vp->isMagic(JS_ARRAY_HOLE)) {
     277                 :                 /* Dense arrays never get so large that i would not fit into an integer id. */
     278          765274 :                 if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, flags, ht, props))
     279               0 :                     return false;
     280                 :             }
     281                 :         }
     282                 :     }
     283                 : 
     284          113418 :     return true;
     285                 : }
     286                 : 
     287                 : #ifdef JS_MORE_DETERMINISTIC
     288                 : 
     289                 : struct SortComparatorIds
     290                 : {
     291                 :     JSContext   *const cx;
     292                 : 
     293                 :     SortComparatorIds(JSContext *cx)
     294                 :       : cx(cx) {}
     295                 : 
     296                 :     bool operator()(jsid a, jsid b, bool *lessOrEqualp)
     297                 :     {
     298                 :         /* Pick an arbitrary total order on jsids that is stable across executions. */
     299                 :         JSString *astr = IdToString(cx, a);
     300                 :         if (!astr)
     301                 :             return false;
     302                 :         JSString *bstr = IdToString(cx, b);
     303                 :         if (!bstr)
     304                 :             return false;
     305                 : 
     306                 :         int32_t result;
     307                 :         if (!CompareStrings(cx, astr, bstr, &result))
     308                 :             return false;
     309                 : 
     310                 :         *lessOrEqualp = (result <= 0);
     311                 :         return true;
     312                 :     }
     313                 : };
     314                 : 
     315                 : #endif /* JS_MORE_DETERMINISTIC */
     316                 : 
     317                 : static bool
     318         1870990 : Snapshot(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
     319                 : {
     320         3741980 :     IdSet ht(cx);
     321         1870990 :     if (!ht.init(32))
     322               0 :         return NULL;
     323                 : 
     324         3741980 :     RootObject objRoot(cx, &obj);
     325         3741980 :     RootedVarObject pobj(cx);
     326         1870990 :     pobj = obj;
     327                 : 
     328         1267230 :     do {
     329         2569443 :         Class *clasp = pobj->getClass();
     330         7480922 :         if (pobj->isNative() &&
     331         2455974 :             !pobj->getOps()->enumerate &&
     332         2455505 :             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
     333         2455505 :             if (!clasp->enumerate(cx, pobj))
     334               0 :                 return false;
     335         2455505 :             if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
     336               0 :                 return false;
     337          113938 :         } else if (pobj->isDenseArray()) {
     338          113418 :             if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
     339               0 :                 return false;
     340                 :         } else {
     341             520 :             if (pobj->isProxy()) {
     342              36 :                 AutoIdVector proxyProps(cx);
     343              18 :                 if (flags & JSITER_OWNONLY) {
     344              18 :                     if (flags & JSITER_HIDDEN) {
     345               9 :                         if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps))
     346               0 :                             return false;
     347                 :                     } else {
     348               9 :                         if (!Proxy::keys(cx, pobj, proxyProps))
     349               0 :                             return false;
     350                 :                     }
     351                 :                 } else {
     352               0 :                     if (!Proxy::enumerate(cx, pobj, proxyProps))
     353               0 :                         return false;
     354                 :                 }
     355              36 :                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
     356              18 :                     if (!Enumerate(cx, obj, pobj, proxyProps[n], true, flags, ht, props))
     357               0 :                         return false;
     358                 :                 }
     359                 :                 /* Proxy objects enumerate the prototype on their own, so we are done here. */
     360              18 :                 break;
     361                 :             }
     362                 :             Value state;
     363             502 :             JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
     364             502 :             if (!pobj->enumerate(cx, op, &state, NULL))
     365               0 :                 return false;
     366             502 :             if (state.isMagic(JS_NATIVE_ENUMERATE)) {
     367             330 :                 if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
     368               0 :                     return false;
     369                 :             } else {
     370        38702396 :                 while (true) {
     371                 :                     jsid id;
     372        38702568 :                     if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
     373               0 :                         return false;
     374        38702568 :                     if (state.isNull())
     375             172 :                         break;
     376        38702396 :                     if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props))
     377               0 :                         return false;
     378                 :                 }
     379                 :             }
     380                 :         }
     381                 : 
     382         2569425 :         if ((flags & JSITER_OWNONLY) || pobj->isXML())
     383         1302195 :             break;
     384         1267230 :     } while ((pobj = pobj->getProto()) != NULL);
     385                 : 
     386                 : #ifdef JS_MORE_DETERMINISTIC
     387                 : 
     388                 :     /*
     389                 :      * In some cases the enumeration order for an object depends on the
     390                 :      * execution mode (interpreter vs. JIT), especially for native objects
     391                 :      * with a class enumerate hook (where resolving a property changes the
     392                 :      * resulting enumeration order). These aren't really bugs, but the
     393                 :      * differences can change the generated output and confuse correctness
     394                 :      * fuzzers, so we sort the ids if such a fuzzer is running.
     395                 :      *
     396                 :      * We don't do this in the general case because (a) doing so is slow,
     397                 :      * and (b) it also breaks the web, which expects enumeration order to
     398                 :      * follow the order in which properties are added, in certain cases.
     399                 :      * Since ECMA does not specify an enumeration order for objects, both
     400                 :      * behaviors are technically correct to do.
     401                 :      */
     402                 : 
     403                 :     jsid *ids = props->begin();
     404                 :     size_t n = props->length();
     405                 : 
     406                 :     Vector<jsid> tmp(cx);
     407                 :     if (!tmp.resizeUninitialized(n))
     408                 :         return false;
     409                 : 
     410                 :     if (!MergeSort(ids, n, tmp.begin(), SortComparatorIds(cx)))
     411                 :         return false;
     412                 : 
     413                 : #endif /* JS_MORE_DETERMINISTIC */
     414                 : 
     415         1870990 :     return true;
     416                 : }
     417                 : 
     418                 : bool
     419            2449 : js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
     420                 : {
     421                 :     JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
     422            2449 :     size_t len = props.length();
     423            2449 :     size_t idsz = len * sizeof(jsid);
     424            2449 :     size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
     425            2449 :     JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz));
     426            2449 :     if (!ida)
     427               0 :         return false;
     428                 : 
     429            2449 :     ida->length = static_cast<int>(len);
     430            2449 :     jsid *v = props.begin();
     431            5339 :     for (int i = 0; i < ida->length; i++)
     432            2890 :         ida->vector[i].init(v[i]);
     433            2449 :     *idap = ida;
     434            2449 :     return true;
     435                 : }
     436                 : 
     437                 : JS_FRIEND_API(bool)
     438         1305449 : js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
     439                 : {
     440         1305449 :     return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
     441                 : }
     442                 : 
     443                 : size_t sCustomIteratorCount = 0;
     444                 : 
     445                 : static inline bool
     446          571711 : GetCustomIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
     447                 : {
     448          571711 :     JS_CHECK_RECURSION(cx, return false);
     449                 : 
     450                 :     /*
     451                 :      * for-of iteration does not fall back on __iterator__ or property
     452                 :      * enumeration. This is more conservative than the current proposed spec.
     453                 :      */
     454          571702 :     if (flags == JSITER_FOR_OF) {
     455                 :         js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_NOT_ITERABLE,
     456             243 :                                  JSDVG_SEARCH_STACK, ObjectValue(*obj), NULL, NULL, NULL);
     457             243 :         return false;
     458                 :     }
     459                 : 
     460                 :     /* Check whether we have a valid __iterator__ method. */
     461          571459 :     JSAtom *atom = cx->runtime->atomState.iteratorAtom;
     462          571459 :     if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), JSGET_NO_METHOD_BARRIER, vp))
     463               0 :         return false;
     464                 : 
     465                 :     /* If there is no custom __iterator__ method, we are done here. */
     466          571459 :     if (!vp->isObject()) {
     467          565541 :         vp->setUndefined();
     468          565541 :         return true;
     469                 :     }
     470                 : 
     471            5918 :     if (!cx->runningWithTrustedPrincipals())
     472            5918 :         ++sCustomIteratorCount;
     473                 : 
     474                 :     /* Otherwise call it and return that object. */
     475            5918 :     Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
     476            5918 :     if (!Invoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
     477            5711 :         return false;
     478             207 :     if (vp->isPrimitive()) {
     479                 :         /*
     480                 :          * We are always coming from js::ValueToIterator, and we are no longer on
     481                 :          * trace, so the object we are iterating over is on top of the stack (-1).
     482                 :          */
     483               0 :         JSAutoByteString bytes;
     484               0 :         if (!js_AtomToPrintableString(cx, atom, &bytes))
     485               0 :             return false;
     486               0 :         js_ReportValueError2(cx, JSMSG_BAD_TRAP_RETURN_VALUE,
     487               0 :                              -1, ObjectValue(*obj), NULL, bytes.ptr());
     488               0 :         return false;
     489                 :     }
     490             207 :     return true;
     491                 : }
     492                 : 
     493                 : template <typename T>
     494                 : static inline bool
     495            6915 : Compare(T *a, T *b, size_t c)
     496                 : {
     497            6915 :     size_t n = (c + size_t(7)) / size_t(8);
     498            6915 :     switch (c % 8) {
     499            6915 :       case 0: do { if (*a++ != *b++) return false;
     500               0 :       case 7:      if (*a++ != *b++) return false;
     501               0 :       case 6:      if (*a++ != *b++) return false;
     502               0 :       case 5:      if (*a++ != *b++) return false;
     503               0 :       case 4:      if (*a++ != *b++) return false;
     504              10 :       case 3:      if (*a++ != *b++) return false;
     505            6885 :       case 2:      if (*a++ != *b++) return false;
     506            6915 :       case 1:      if (*a++ != *b++) return false;
     507                 :               } while (--n > 0);
     508                 :     }
     509            6915 :     return true;
     510                 : }
     511                 : 
     512                 : static inline JSObject *
     513          566095 : NewIteratorObject(JSContext *cx, unsigned flags)
     514                 : {
     515          566095 :     if (flags & JSITER_ENUMERATE) {
     516         1124784 :         RootedVarTypeObject type(cx);
     517          562392 :         type = cx->compartment->getEmptyType(cx);
     518          562392 :         if (!type)
     519               0 :             return NULL;
     520                 : 
     521         1124784 :         RootedVarShape emptyEnumeratorShape(cx);
     522                 :         emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
     523          562392 :                                                            ITERATOR_FINALIZE_KIND);
     524          562392 :         if (!emptyEnumeratorShape)
     525               0 :             return NULL;
     526                 : 
     527                 :         JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
     528          562392 :                                          emptyEnumeratorShape, type, NULL);
     529          562392 :         if (!obj)
     530               0 :             return NULL;
     531                 : 
     532          562392 :         JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
     533          562392 :         return obj;
     534                 :     }
     535                 : 
     536            3703 :     return NewBuiltinClassInstance(cx, &IteratorClass);
     537                 : }
     538                 : 
     539                 : NativeIterator *
     540          568602 : NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
     541                 : {
     542          568602 :     size_t plength = props.length();
     543                 :     NativeIterator *ni = (NativeIterator *)
     544                 :         cx->malloc_(sizeof(NativeIterator)
     545                 :                     + plength * sizeof(JSString *)
     546          568602 :                     + slength * sizeof(Shape *));
     547          568602 :     if (!ni)
     548               0 :         return NULL;
     549         1137204 :     AutoValueVector strings(cx);
     550          568602 :     ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
     551          568602 :     ni->props_end = ni->props_array + plength;
     552          568602 :     if (plength) {
     553        54788870 :         for (size_t i = 0; i < plength; i++) {
     554        54246212 :             JSFlatString *str = IdToString(cx, props[i]);
     555        54246212 :             if (!str || !strings.append(StringValue(str)))
     556               9 :                 return NULL;
     557        54246203 :             ni->props_array[i].init(str);
     558                 :         }
     559                 :     }
     560          568593 :     return ni;
     561                 : }
     562                 : 
     563                 : inline void
     564          568593 : NativeIterator::init(JSObject *obj, unsigned flags, uint32_t slength, uint32_t key)
     565                 : {
     566          568593 :     this->obj.init(obj);
     567          568593 :     this->flags = flags;
     568          568593 :     this->shapes_array = (const Shape **) this->props_end;
     569          568593 :     this->shapes_length = slength;
     570          568593 :     this->shapes_key = key;
     571          568593 : }
     572                 : 
     573                 : static inline void
     574          632105 : RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
     575                 : {
     576                 :     /* Register non-escaping native enumerators (for-in) with the current context. */
     577          632105 :     if (ni->flags & JSITER_ENUMERATE) {
     578          628402 :         ni->next = cx->enumerators;
     579          628402 :         cx->enumerators = iterobj;
     580                 : 
     581          628402 :         JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
     582          628402 :         ni->flags |= JSITER_ACTIVE;
     583                 :     }
     584          632105 : }
     585                 : 
     586                 : static inline bool
     587          288674 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys,
     588                 :                     uint32_t slength, uint32_t key, Value *vp)
     589                 : {
     590          288674 :     JS_ASSERT(!(flags & JSITER_FOREACH));
     591                 : 
     592          288674 :     if (obj) {
     593          288349 :         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
     594               0 :             return false;
     595          288349 :         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
     596                 :     }
     597                 : 
     598          288674 :     JSObject *iterobj = NewIteratorObject(cx, flags);
     599          288674 :     if (!iterobj)
     600               0 :         return false;
     601                 : 
     602          288674 :     NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
     603          288674 :     if (!ni)
     604               9 :         return false;
     605          288665 :     ni->init(obj, flags, slength, key);
     606                 : 
     607          288665 :     if (slength) {
     608                 :         /*
     609                 :          * Fill in the shape array from scratch.  We can't use the array that was
     610                 :          * computed for the cache lookup earlier, as constructing iterobj could
     611                 :          * have triggered a shape-regenerating GC.  Don't bother with regenerating
     612                 :          * the shape key; if such a GC *does* occur, we can only get hits through
     613                 :          * the one-slot lastNativeIterator cache.
     614                 :          */
     615            1675 :         JSObject *pobj = obj;
     616            1675 :         size_t ind = 0;
     617            3320 :         do {
     618            3320 :             ni->shapes_array[ind++] = pobj->lastProperty();
     619            3320 :             pobj = pobj->getProto();
     620                 :         } while (pobj);
     621            1675 :         JS_ASSERT(ind == slength);
     622                 :     }
     623                 : 
     624          288665 :     iterobj->setNativeIterator(ni);
     625          288665 :     vp->setObject(*iterobj);
     626                 : 
     627          288665 :     RegisterEnumerator(cx, iterobj, ni);
     628          288665 :     return true;
     629                 : }
     630                 : 
     631                 : namespace js {
     632                 : 
     633                 : bool
     634              18 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp)
     635                 : {
     636              18 :     return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
     637                 : }
     638                 : 
     639                 : bool
     640          277421 : VectorToValueIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys,
     641                 :                       Value *vp)
     642                 : {
     643          277421 :     JS_ASSERT(flags & JSITER_FOREACH);
     644                 : 
     645          277421 :     if (obj) {
     646          277210 :         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
     647               0 :             return false;
     648          277210 :         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
     649                 :     }
     650                 : 
     651          277421 :     JSObject *iterobj = NewIteratorObject(cx, flags);
     652          277421 :     if (!iterobj)
     653               0 :         return false;
     654                 : 
     655          277421 :     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
     656          277421 :     if (!ni)
     657               0 :         return false;
     658          277421 :     ni->init(obj, flags, 0, 0);
     659                 : 
     660          277421 :     iterobj->setNativeIterator(ni);
     661          277421 :     vp->setObject(*iterobj);
     662                 : 
     663          277421 :     RegisterEnumerator(cx, iterobj, ni);
     664          277421 :     return true;
     665                 : }
     666                 : 
     667                 : bool
     668               0 : EnumeratedIdVectorToIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp)
     669                 : {
     670               0 :     if (!(flags & JSITER_FOREACH))
     671               0 :         return VectorToKeyIterator(cx, obj, flags, props, vp);
     672                 : 
     673               0 :     return VectorToValueIterator(cx, obj, flags, props, vp);
     674                 : }
     675                 : 
     676                 : static inline void
     677           66019 : UpdateNativeIterator(NativeIterator *ni, JSObject *obj)
     678                 : {
     679                 :     // Update the object for which the native iterator is associated, so
     680                 :     // SuppressDeletedPropertyHelper will recognize the iterator as a match.
     681           66019 :     ni->obj = obj;
     682           66019 : }
     683                 : 
     684                 : bool
     685          650037 : GetIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
     686                 : {
     687         1300074 :     Vector<const Shape *, 8> shapes(cx);
     688          650037 :     uint32_t key = 0;
     689                 : 
     690          650037 :     bool keysOnly = (flags == JSITER_ENUMERATE);
     691                 : 
     692          650037 :     if (obj) {
     693                 :         /* Enumerate Iterator.prototype directly. */
     694          649501 :         if (JSIteratorOp op = obj->getClass()->ext.iteratorObject) {
     695                 :             /*
     696                 :              * Arrays and other classes representing iterable collections have
     697                 :              * the JSCLASS_FOR_OF_ITERATION flag. This flag means that the
     698                 :              * object responds to all other kinds of enumeration (for-in,
     699                 :              * for-each, Object.keys, Object.getOwnPropertyNames, etc.) in the
     700                 :              * default way, ignoring the hook. The hook is used only when
     701                 :              * iterating in the style of a for-of loop.
     702                 :              */
     703          124520 :             if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
     704           11690 :                 JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
     705           11690 :                 if (!iterobj)
     706               0 :                     return false;
     707           11690 :                 vp->setObject(*iterobj);
     708           11690 :                 types::MarkIteratorUnknown(cx);
     709           11690 :                 return true;
     710                 :             }
     711                 :         }
     712                 : 
     713          637811 :         if (keysOnly) {
     714                 :             /*
     715                 :              * Check to see if this is the same as the most recent object which
     716                 :              * was iterated over.  We don't explicitly check for shapeless
     717                 :              * objects here, as they are not inserted into the cache and
     718                 :              * will result in a miss.
     719                 :              */
     720          354584 :             JSObject *last = cx->compartment->nativeIterCache.last;
     721          354584 :             JSObject *proto = obj->getProto();
     722          354584 :             if (last) {
     723           66924 :                 NativeIterator *lastni = last->getNativeIterator();
     724          376519 :                 if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
     725           66149 :                     obj->isNative() &&
     726           66134 :                     obj->lastProperty() == lastni->shapes_array[0] &&
     727           59104 :                     proto && proto->isNative() &&
     728           59104 :                     proto->lastProperty() == lastni->shapes_array[1] &&
     729           59104 :                     !proto->getProto()) {
     730           59104 :                     vp->setObject(*last);
     731           59104 :                     UpdateNativeIterator(lastni, obj);
     732           59104 :                     RegisterEnumerator(cx, last, lastni);
     733           59104 :                     return true;
     734                 :                 }
     735                 :             }
     736                 : 
     737                 :             /*
     738                 :              * The iterator object for JSITER_ENUMERATE never escapes, so we
     739                 :              * don't care for the proper parent/proto to be set. This also
     740                 :              * allows us to re-use a previous iterator object that is not
     741                 :              * currently active.
     742                 :              */
     743          295480 :             JSObject *pobj = obj;
     744          295132 :             do {
     745         1749793 :                 if (!pobj->isNative() ||
     746          574283 :                     pobj->hasUncacheableProto() ||
     747          296860 :                     obj->getOps()->enumerate ||
     748          296748 :                     pobj->getClass()->enumerate != JS_EnumerateStub) {
     749          286770 :                     shapes.clear();
     750          286770 :                     goto miss;
     751                 :                 }
     752          295132 :                 const Shape *shape = pobj->lastProperty();
     753          295132 :                 key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
     754          295132 :                 if (!shapes.append((Shape *) shape))
     755               0 :                     return false;
     756          295132 :                 pobj = pobj->getProto();
     757                 :             } while (pobj);
     758                 : 
     759            8710 :             JSObject *iterobj = cx->compartment->nativeIterCache.get(key);
     760            8710 :             if (iterobj) {
     761            6975 :                 NativeIterator *ni = iterobj->getNativeIterator();
     762           20805 :                 if (!(ni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
     763                 :                     ni->shapes_key == key &&
     764            6915 :                     ni->shapes_length == shapes.length() &&
     765            6915 :                     Compare(ni->shapes_array, shapes.begin(), ni->shapes_length)) {
     766            6915 :                     vp->setObject(*iterobj);
     767                 : 
     768            6915 :                     UpdateNativeIterator(ni, obj);
     769            6915 :                     RegisterEnumerator(cx, iterobj, ni);
     770            6915 :                     if (shapes.length() == 2)
     771            6875 :                         cx->compartment->nativeIterCache.last = iterobj;
     772            6915 :                     return true;
     773                 :                 }
     774                 :             }
     775                 :         }
     776                 : 
     777                 :       miss:
     778          571792 :         if (obj->isProxy()) {
     779              81 :             types::MarkIteratorUnknown(cx);
     780              81 :             return Proxy::iterate(cx, obj, flags, vp);
     781                 :         }
     782          571711 :         if (!GetCustomIterator(cx, obj, flags, vp))
     783            5963 :             return false;
     784          565748 :         if (!vp->isUndefined()) {
     785             207 :             types::MarkIteratorUnknown(cx);
     786             207 :             return true;
     787                 :         }
     788                 :     }
     789                 : 
     790                 :     /* NB: for (var p in null) succeeds by iterating over no properties. */
     791                 : 
     792         1132154 :     AutoIdVector keys(cx);
     793          566077 :     if (flags & JSITER_FOREACH) {
     794          277421 :         if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
     795               0 :             return false;
     796          277421 :         JS_ASSERT(shapes.empty());
     797          277421 :         if (!VectorToValueIterator(cx, obj, flags, keys, vp))
     798               0 :             return false;
     799                 :     } else {
     800          288656 :         if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
     801               0 :             return false;
     802          288656 :         if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
     803               9 :             return false;
     804                 :     }
     805                 : 
     806          566068 :     JSObject *iterobj = &vp->toObject();
     807                 : 
     808                 :     /* Cache the iterator object if possible. */
     809          566068 :     if (shapes.length())
     810            1675 :         cx->compartment->nativeIterCache.set(key, iterobj);
     811                 : 
     812          566068 :     if (shapes.length() == 2)
     813            1505 :         cx->compartment->nativeIterCache.last = iterobj;
     814          566068 :     return true;
     815                 : }
     816                 : 
     817                 : }
     818                 : 
     819                 : static JSObject *
     820           10762 : iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
     821                 : {
     822           10762 :     return obj;
     823                 : }
     824                 : 
     825                 : static JSBool
     826            9414 : Iterator(JSContext *cx, unsigned argc, Value *vp)
     827                 : {
     828            9414 :     Value *argv = JS_ARGV(cx, vp);
     829            9414 :     bool keyonly = argc >= 2 ? js_ValueToBoolean(argv[1]) : false;
     830            9414 :     unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
     831            9414 :     *vp = argc >= 1 ? argv[0] : UndefinedValue();
     832            9414 :     return ValueToIterator(cx, flags, vp);
     833                 : }
     834                 : 
     835                 : JSBool
     836            7183 : js_ThrowStopIteration(JSContext *cx)
     837                 : {
     838                 :     Value v;
     839                 : 
     840            7183 :     JS_ASSERT(!JS_IsExceptionPending(cx));
     841            7183 :     if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
     842            7183 :         cx->setPendingException(v);
     843            7183 :     return JS_FALSE;
     844                 : }
     845                 : 
     846                 : static JSBool
     847             117 : iterator_next(JSContext *cx, unsigned argc, Value *vp)
     848                 : {
     849             117 :     CallArgs args = CallArgsFromVp(argc, vp);
     850                 : 
     851                 :     bool ok;
     852             117 :     JSObject *obj = NonGenericMethodGuard(cx, args, iterator_next, &IteratorClass, &ok);
     853             117 :     if (!obj)
     854              63 :         return ok;
     855                 : 
     856              54 :     if (!js_IteratorMore(cx, obj, &args.rval()))
     857               0 :         return false;
     858                 : 
     859              54 :     if (!args.rval().toBoolean()) {
     860              27 :         js_ThrowStopIteration(cx);
     861              27 :         return false;
     862                 :     }
     863                 : 
     864              27 :     return js_IteratorNext(cx, obj, &args.rval());
     865                 : }
     866                 : 
     867                 : #define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
     868                 : 
     869                 : static JSFunctionSpec iterator_methods[] = {
     870                 :     JS_FN(js_next_str,      iterator_next,  0,JSPROP_ROPERM),
     871                 :     JS_FS_END
     872                 : };
     873                 : 
     874                 : #if JS_HAS_GENERATORS
     875                 : static JSBool
     876                 : CloseGenerator(JSContext *cx, JSObject *genobj);
     877                 : #endif
     878                 : 
     879                 : /*
     880                 :  * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
     881                 :  * Otherwise construct the default iterator.
     882                 :  */
     883                 : JSBool
     884          650037 : js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp)
     885                 : {
     886                 :     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
     887          650037 :     JS_ASSERT_IF(flags & JSITER_KEYVALUE, flags & JSITER_FOREACH);
     888                 : 
     889                 :     /*
     890                 :      * Make sure the more/next state machine doesn't get stuck. A value might be
     891                 :      * left in iterValue when a trace is left due to an operation time-out after
     892                 :      * JSOP_MOREITER but before the value is picked up by FOR*.
     893                 :      */
     894          650037 :     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
     895                 : 
     896                 :     JSObject *obj;
     897          650037 :     if (vp->isObject()) {
     898                 :         /* Common case. */
     899          620498 :         obj = &vp->toObject();
     900                 :     } else {
     901                 :         /*
     902                 :          * Enumerating over null and undefined gives an empty enumerator.
     903                 :          * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
     904                 :          * the first production in 12.6.4 and step 4 of the second production,
     905                 :          * but it's "web JS" compatible. ES5 fixed for-in to match this de-facto
     906                 :          * standard.
     907                 :          */
     908           29539 :         if ((flags & JSITER_ENUMERATE)) {
     909           23693 :             if (!js_ValueToObjectOrNull(cx, *vp, &obj))
     910               0 :                 return false;
     911                 :             /* fall through */
     912                 :         } else {
     913            5846 :             obj = js_ValueToNonNullObject(cx, *vp);
     914            5846 :             if (!obj)
     915              36 :                 return false;
     916                 :         }
     917                 :     }
     918                 : 
     919          650001 :     return GetIterator(cx, obj, flags, vp);
     920                 : }
     921                 : 
     922                 : bool
     923          335584 : js::CloseIterator(JSContext *cx, JSObject *obj)
     924                 : {
     925          335584 :     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
     926                 : 
     927          335584 :     if (obj->isIterator()) {
     928                 :         /* Remove enumerators from the active list, which is a stack. */
     929          327309 :         NativeIterator *ni = obj->getNativeIterator();
     930                 : 
     931          327309 :         if (ni->flags & JSITER_ENUMERATE) {
     932          323651 :             JS_ASSERT(cx->enumerators == obj);
     933          323651 :             cx->enumerators = ni->next;
     934                 : 
     935          323651 :             JS_ASSERT(ni->flags & JSITER_ACTIVE);
     936          323651 :             ni->flags &= ~JSITER_ACTIVE;
     937                 : 
     938                 :             /*
     939                 :              * Reset the enumerator; it may still be in the cached iterators
     940                 :              * for this thread, and can be reused.
     941                 :              */
     942          323651 :             ni->props_cursor = ni->props_array;
     943                 :         }
     944                 :     }
     945                 : #if JS_HAS_GENERATORS
     946            8275 :     else if (obj->isGenerator()) {
     947            7167 :         return CloseGenerator(cx, obj);
     948                 :     }
     949                 : #endif
     950          328417 :     return JS_TRUE;
     951                 : }
     952                 : 
     953                 : bool
     954             142 : js::UnwindIteratorForException(JSContext *cx, JSObject *obj)
     955                 : {
     956             142 :     Value v = cx->getPendingException();
     957             142 :     cx->clearPendingException();
     958             142 :     if (!CloseIterator(cx, obj))
     959               9 :         return false;
     960             133 :     cx->setPendingException(v);
     961             133 :     return true;
     962                 : }
     963                 : 
     964                 : void
     965               0 : js::UnwindIteratorForUncatchableException(JSContext *cx, JSObject *obj)
     966                 : {
     967               0 :     if (obj->isIterator()) {
     968               0 :         NativeIterator *ni = obj->getNativeIterator();
     969               0 :         if (ni->flags & JSITER_ENUMERATE) {
     970               0 :             JS_ASSERT(cx->enumerators == obj);
     971               0 :             cx->enumerators = ni->next;
     972                 :         }
     973                 :     }
     974               0 : }
     975                 : 
     976                 : /*
     977                 :  * Suppress enumeration of deleted properties. This function must be called
     978                 :  * when a property is deleted and there might be active enumerators.
     979                 :  *
     980                 :  * We maintain a list of active non-escaping for-in enumerators. To suppress
     981                 :  * a property, we check whether each active enumerator contains the (obj, id)
     982                 :  * pair and has not yet enumerated |id|. If so, and |id| is the next property,
     983                 :  * we simply advance the cursor. Otherwise, we delete |id| from the list.
     984                 :  *
     985                 :  * We do not suppress enumeration of a property deleted along an object's
     986                 :  * prototype chain. Only direct deletions on the object are handled.
     987                 :  *
     988                 :  * This function can suppress multiple properties at once. The |predicate|
     989                 :  * argument is an object which can be called on an id and returns true or
     990                 :  * false. It also must have a method |matchesAtMostOne| which allows us to
     991                 :  * stop searching after the first deletion if true.
     992                 :  */
     993                 : template<typename StringPredicate>
     994                 : static bool
     995         1713335 : SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate predicate)
     996                 : {
     997         1713335 :     JSObject *iterobj = cx->enumerators;
     998         3511104 :     while (iterobj) {
     999                 :       again:
    1000           84434 :         NativeIterator *ni = iterobj->getNativeIterator();
    1001                 :         /* This only works for identified surpressed keys, not values. */
    1002           84434 :         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
    1003                 :             /* Check whether id is still to come. */
    1004            3854 :             HeapPtr<JSFlatString> *props_cursor = ni->current();
    1005            3854 :             HeapPtr<JSFlatString> *props_end = ni->end();
    1006           23777 :             for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
    1007           20167 :                 if (predicate(*idp)) {
    1008                 :                     /*
    1009                 :                      * Check whether another property along the prototype chain
    1010                 :                      * became visible as a result of this deletion.
    1011                 :                      */
    1012             244 :                     if (obj->getProto()) {
    1013             244 :                         JSObject *proto = obj->getProto();
    1014                 :                         JSObject *obj2;
    1015                 :                         JSProperty *prop;
    1016                 :                         jsid id;
    1017             244 :                         if (!ValueToId(cx, StringValue(*idp), &id))
    1018               0 :                             return false;
    1019             244 :                         id = js_CheckForStringIndex(id);
    1020             244 :                         if (!proto->lookupGeneric(cx, id, &obj2, &prop))
    1021               0 :                             return false;
    1022             244 :                         if (prop) {
    1023                 :                             unsigned attrs;
    1024               0 :                             if (obj2->isNative())
    1025               0 :                                 attrs = ((Shape *) prop)->attributes();
    1026               0 :                             else if (!obj2->getGenericAttributes(cx, id, &attrs))
    1027               0 :                                 return false;
    1028                 : 
    1029               0 :                             if (attrs & JSPROP_ENUMERATE)
    1030               0 :                                 continue;
    1031                 :                         }
    1032                 :                     }
    1033                 : 
    1034                 :                     /*
    1035                 :                      * If lookupProperty or getAttributes above removed a property from
    1036                 :                      * ni, start over.
    1037                 :                      */
    1038             244 :                     if (props_end != ni->props_end || props_cursor != ni->props_cursor)
    1039                 :                         goto again;
    1040                 : 
    1041                 :                     /*
    1042                 :                      * No property along the prototype chain stepped in to take the
    1043                 :                      * property's place, so go ahead and delete id from the list.
    1044                 :                      * If it is the next property to be enumerated, just skip it.
    1045                 :                      */
    1046             244 :                     if (idp == props_cursor) {
    1047             127 :                         ni->incCursor();
    1048                 :                     } else {
    1049             162 :                         for (HeapPtr<JSFlatString> *p = idp; p + 1 != props_end; p++)
    1050              45 :                             *p = *(p + 1);
    1051             117 :                         ni->props_end = ni->end() - 1;
    1052                 : 
    1053                 :                         /*
    1054                 :                          * Invoke the write barrier on this element, since it's
    1055                 :                          * no longer going to be marked.
    1056                 :                          */
    1057             117 :                         ni->props_end->HeapPtr<JSFlatString>::~HeapPtr();
    1058                 :                     }
    1059                 : 
    1060                 :                     /* Don't reuse modified native iterators. */
    1061             244 :                     ni->flags |= JSITER_UNREUSABLE;
    1062                 : 
    1063             244 :                     if (predicate.matchesAtMostOne())
    1064             244 :                         break;
    1065                 :                 }
    1066                 :             }
    1067                 :         }
    1068           84434 :         iterobj = ni->next;
    1069                 :     }
    1070         1713335 :     return true;
    1071                 : }
    1072                 : 
    1073                 : class SingleStringPredicate {
    1074                 :     JSFlatString *str;
    1075                 : public:
    1076         1713283 :     SingleStringPredicate(JSFlatString *str) : str(str) {}
    1077                 : 
    1078           20167 :     bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
    1079             244 :     bool matchesAtMostOne() { return true; }
    1080                 : };
    1081                 : 
    1082                 : bool
    1083         1713283 : js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
    1084                 : {
    1085         1713283 :     JSFlatString *str = IdToString(cx, id);
    1086         1713283 :     if (!str)
    1087               0 :         return false;
    1088         1713283 :     return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
    1089                 : }
    1090                 : 
    1091                 : bool
    1092         1612389 : js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index)
    1093                 : {
    1094                 :     jsid id;
    1095         1612389 :     if (!IndexToId(cx, index, &id))
    1096               0 :         return false;
    1097         1612389 :     return js_SuppressDeletedProperty(cx, obj, id);
    1098                 : }
    1099                 : 
    1100                 : class IndexRangePredicate {
    1101                 :     uint32_t begin, end;
    1102                 : 
    1103                 :   public:
    1104              52 :     IndexRangePredicate(uint32_t begin, uint32_t end) : begin(begin), end(end) {}
    1105                 : 
    1106               0 :     bool operator()(JSFlatString *str) {
    1107                 :         uint32_t index;
    1108               0 :         return str->isIndex(&index) && begin <= index && index < end;
    1109                 :     }
    1110                 : 
    1111               0 :     bool matchesAtMostOne() { return false; }
    1112                 : };
    1113                 : 
    1114                 : bool
    1115              52 : js_SuppressDeletedElements(JSContext *cx, JSObject *obj, uint32_t begin, uint32_t end)
    1116                 : {
    1117              52 :     return SuppressDeletedPropertyHelper(cx, obj, IndexRangePredicate(begin, end));
    1118                 : }
    1119                 : 
    1120                 : const uint32_t CLOSED_INDEX = UINT32_MAX;
    1121                 : 
    1122                 : JSObject *
    1123             927 : ElementIteratorObject::create(JSContext *cx, JSObject *obj)
    1124                 : {
    1125             927 :     JS_ASSERT(obj);
    1126             927 :     JSObject *iterobj = NewObjectWithGivenProto(cx, &ElementIteratorClass, NULL, obj);
    1127             927 :     if (iterobj) {
    1128             927 :         iterobj->setReservedSlot(TargetSlot, ObjectValue(*obj));
    1129             927 :         iterobj->setReservedSlot(IndexSlot, Int32Value(0));
    1130                 :     }
    1131             927 :     return iterobj;
    1132                 : }
    1133                 : 
    1134                 : inline uint32_t
    1135            3942 : ElementIteratorObject::getIndex() const
    1136                 : {
    1137            3942 :     return uint32_t(getReservedSlot(IndexSlot).toInt32());
    1138                 : }
    1139                 : 
    1140                 : inline JSObject *
    1141            3942 : ElementIteratorObject::getTargetObject() const
    1142                 : {
    1143            3942 :     return &getReservedSlot(TargetSlot).toObject();
    1144                 : }
    1145                 : 
    1146                 : inline void
    1147            3942 : ElementIteratorObject::setIndex(uint32_t index)
    1148                 : {
    1149            3942 :     setReservedSlot(IndexSlot, Int32Value(int32_t(index)));
    1150            3942 : }
    1151                 : 
    1152                 : bool
    1153            3942 : ElementIteratorObject::iteratorNext(JSContext *cx, Value *vp)
    1154                 : {
    1155                 :     uint32_t i, length;
    1156            3942 :     JSObject *obj = getTargetObject();
    1157            3942 :     if (!js_GetLengthProperty(cx, obj, &length))
    1158               0 :         goto error;
    1159                 : 
    1160            3942 :     i = getIndex();
    1161            3942 :     if (i >= length) {
    1162             738 :         setIndex(CLOSED_INDEX);
    1163             738 :         vp->setMagic(JS_NO_ITER_VALUE);
    1164             738 :         return true;
    1165                 :     }
    1166                 : 
    1167            3204 :     JS_ASSERT(i + 1 > i);
    1168            3204 :     if (!obj->getElement(cx, obj, i, vp))
    1169               0 :         goto error;
    1170                 : 
    1171                 :     /* On success, bump the index. */
    1172            3204 :     setIndex(i + 1);
    1173            3204 :     return true;
    1174                 : 
    1175                 :   error:
    1176               0 :     setIndex(CLOSED_INDEX);
    1177               0 :     return false;
    1178                 : }
    1179                 : 
    1180                 : inline js::ElementIteratorObject *
    1181            3942 : JSObject::asElementIterator()
    1182                 : {
    1183            3942 :     JS_ASSERT(isElementIterator());
    1184            3942 :     return static_cast<js::ElementIteratorObject *>(this);
    1185                 : }
    1186                 : 
    1187                 : JSBool
    1188        13094970 : js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
    1189                 : {
    1190                 :     /* Fast path for native iterators */
    1191        13094970 :     NativeIterator *ni = NULL;
    1192        13094970 :     if (iterobj->isIterator()) {
    1193                 :         /* Key iterators are handled by fast-paths. */
    1194        13064000 :         ni = iterobj->getNativeIterator();
    1195        13064000 :         bool more = ni->props_cursor < ni->props_end;
    1196        13064000 :         if (ni->isKeyIter() || !more) {
    1197          256338 :             rval->setBoolean(more);
    1198          256338 :             return true;
    1199                 :         }
    1200                 :     }
    1201                 : 
    1202                 :     /* We might still have a pending value. */
    1203        12838632 :     if (!cx->iterValue.isMagic(JS_NO_ITER_VALUE)) {
    1204               0 :         rval->setBoolean(true);
    1205               0 :         return true;
    1206                 :     }
    1207                 : 
    1208                 :     /* We're reentering below and can call anything. */
    1209        12838632 :     JS_CHECK_RECURSION(cx, return false);
    1210                 : 
    1211                 :     /* Fetch and cache the next value from the iterator. */
    1212        12838631 :     if (ni) {
    1213        12807662 :         JS_ASSERT(!ni->isKeyIter());
    1214                 :         jsid id;
    1215        12807662 :         if (!ValueToId(cx, StringValue(*ni->current()), &id))
    1216               0 :             return false;
    1217        12807662 :         id = js_CheckForStringIndex(id);
    1218        12807662 :         ni->incCursor();
    1219        12807662 :         if (!ni->obj->getGeneric(cx, id, rval))
    1220              18 :             return false;
    1221        12807644 :         if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
    1222               0 :             return false;
    1223           30969 :     } else if (iterobj->isElementIterator()) {
    1224                 :         /*
    1225                 :          * Like native iterators, element iterators do not have a .next
    1226                 :          * method, so this fast path is necessary for correctness.
    1227                 :          */
    1228            3942 :         if (!iterobj->asElementIterator()->iteratorNext(cx, rval))
    1229               0 :             return false;
    1230            3942 :         if (rval->isMagic(JS_NO_ITER_VALUE)) {
    1231             738 :             cx->iterValue.setMagic(JS_NO_ITER_VALUE);
    1232             738 :             rval->setBoolean(false);
    1233             738 :             return true;
    1234                 :         }
    1235           27027 :     } else if (iterobj->isProxy()) {
    1236              54 :         if (!Proxy::iteratorNext(cx, iterobj, rval))
    1237               0 :             return false;
    1238              54 :         if (rval->isMagic(JS_NO_ITER_VALUE)) {
    1239               9 :             rval->setBoolean(false);
    1240               9 :             return true;
    1241                 :         }
    1242                 :     } else {
    1243                 :         /* Call the iterator object's .next method. */
    1244           26973 :         jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
    1245           26973 :         if (!js_GetMethod(cx, iterobj, id, JSGET_METHOD_BARRIER, rval))
    1246               0 :             return false;
    1247           26973 :         if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
    1248                 :             /* Check for StopIteration. */
    1249            7266 :             if (!cx->isExceptionPending() || !IsStopIteration(cx->getPendingException()))
    1250              22 :                 return false;
    1251                 : 
    1252            7244 :             cx->clearPendingException();
    1253            7244 :             cx->iterValue.setMagic(JS_NO_ITER_VALUE);
    1254            7244 :             rval->setBoolean(false);
    1255            7244 :             return true;
    1256                 :         }
    1257                 :     }
    1258                 : 
    1259                 :     /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
    1260        12830600 :     JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
    1261        12830600 :     cx->iterValue = *rval;
    1262        12830600 :     rval->setBoolean(true);
    1263        12830600 :     return true;
    1264                 : }
    1265                 : 
    1266                 : JSBool
    1267        12830555 : js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
    1268                 : {
    1269                 :     /* Fast path for native iterators */
    1270        12830555 :     if (iterobj->isIterator()) {
    1271                 :         /*
    1272                 :          * Implement next directly as all the methods of the native iterator are
    1273                 :          * read-only and permanent.
    1274                 :          */
    1275        12807644 :         NativeIterator *ni = iterobj->getNativeIterator();
    1276        12807644 :         if (ni->isKeyIter()) {
    1277               0 :             JS_ASSERT(ni->props_cursor < ni->props_end);
    1278               0 :             *rval = StringValue(*ni->current());
    1279               0 :             ni->incCursor();
    1280                 : 
    1281               0 :             if (rval->isString())
    1282               0 :                 return true;
    1283                 : 
    1284                 :             JSString *str;
    1285                 :             int i;
    1286               0 :             if (rval->isInt32() && StaticStrings::hasInt(i = rval->toInt32())) {
    1287               0 :                 str = cx->runtime->staticStrings.getInt(i);
    1288                 :             } else {
    1289               0 :                 str = ToString(cx, *rval);
    1290               0 :                 if (!str)
    1291               0 :                     return false;
    1292                 :             }
    1293                 : 
    1294               0 :             rval->setString(str);
    1295               0 :             return true;
    1296                 :         }
    1297                 :     }
    1298                 : 
    1299        12830555 :     JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
    1300        12830555 :     *rval = cx->iterValue;
    1301        12830555 :     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
    1302                 : 
    1303        12830555 :     return true;
    1304                 : }
    1305                 : 
    1306                 : static JSBool
    1307              63 : stopiter_hasInstance(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
    1308                 : {
    1309              63 :     *bp = IsStopIteration(*v);
    1310              63 :     return JS_TRUE;
    1311                 : }
    1312                 : 
    1313                 : Class js::StopIterationClass = {
    1314                 :     js_StopIteration_str,
    1315                 :     JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration) |
    1316                 :     JSCLASS_FREEZE_PROTO,
    1317                 :     JS_PropertyStub,         /* addProperty */
    1318                 :     JS_PropertyStub,         /* delProperty */
    1319                 :     JS_PropertyStub,         /* getProperty */
    1320                 :     JS_StrictPropertyStub,   /* setProperty */
    1321                 :     JS_EnumerateStub,
    1322                 :     JS_ResolveStub,
    1323                 :     JS_ConvertStub,
    1324                 :     NULL,                    /* finalize    */
    1325                 :     NULL,                    /* checkAccess */
    1326                 :     NULL,                    /* call        */
    1327                 :     NULL,                    /* construct   */
    1328                 :     stopiter_hasInstance
    1329                 : };
    1330                 : 
    1331                 : #if JS_HAS_GENERATORS
    1332                 : 
    1333                 : static void
    1334           14769 : generator_finalize(JSContext *cx, JSObject *obj)
    1335                 : {
    1336           14769 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
    1337           14769 :     if (!gen)
    1338            2506 :         return;
    1339                 : 
    1340                 :     /*
    1341                 :      * gen is open when a script has not called its close method while
    1342                 :      * explicitly manipulating it.
    1343                 :      */
    1344               0 :     JS_ASSERT(gen->state == JSGEN_NEWBORN ||
    1345                 :               gen->state == JSGEN_CLOSED ||
    1346           12263 :               gen->state == JSGEN_OPEN);
    1347           12263 :     cx->free_(gen);
    1348                 : }
    1349                 : 
    1350                 : static void
    1351            2880 : MarkGenerator(JSTracer *trc, JSGenerator *gen)
    1352                 : {
    1353            2880 :     StackFrame *fp = gen->floatingFrame();
    1354                 : 
    1355                 :     /*
    1356                 :      * MarkGenerator should only be called when regs is based on the floating frame.
    1357                 :      * See calls to RebaseRegsFromTo.
    1358                 :      */
    1359            2880 :     JS_ASSERT(size_t(gen->regs.sp - fp->slots()) <= fp->numSlots());
    1360                 : 
    1361                 :     /*
    1362                 :      * Currently, generators are not mjitted. Still, (overflow) args can be
    1363                 :      * pushed by the mjit and need to be conservatively marked. Technically, the
    1364                 :      * formal args and generator slots are safe for exact marking, but since the
    1365                 :      * plan is to eventually mjit generators, it makes sense to future-proof
    1366                 :      * this code and save someone an hour later.
    1367                 :      */
    1368            2880 :     MarkValueRange(trc, (HeapValue *)fp->formalArgsEnd() - gen->floatingStack,
    1369            5760 :                    gen->floatingStack, "Generator Floating Args");
    1370            2880 :     fp->mark(trc);
    1371            2880 :     MarkValueRange(trc, gen->regs.sp - fp->slots(),
    1372            5760 :                    (HeapValue *)fp->slots(), "Generator Floating Stack");
    1373            2880 : }
    1374                 : 
    1375                 : static void
    1376           30834 : GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
    1377                 : {
    1378           30834 :     JSCompartment *comp = cx->compartment;
    1379           30834 :     if (comp->needsBarrier())
    1380              18 :         MarkGenerator(comp->barrierTracer(), gen);
    1381           30834 : }
    1382                 : 
    1383                 : static void
    1384          241970 : generator_trace(JSTracer *trc, JSObject *obj)
    1385                 : {
    1386          241970 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
    1387          241970 :     if (!gen)
    1388           21185 :         return;
    1389                 : 
    1390                 :     /*
    1391                 :      * Do not mark if the generator is running; the contents may be trash and
    1392                 :      * will be replaced when the generator stops.
    1393                 :      */
    1394          220785 :     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING)
    1395          217923 :         return;
    1396                 : 
    1397            2862 :     JS_ASSERT(gen->liveFrame() == gen->floatingFrame());
    1398            2862 :     MarkGenerator(trc, gen);
    1399                 : }
    1400                 : 
    1401                 : Class js::GeneratorClass = {
    1402                 :     "Generator",
    1403                 :     JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS,
    1404                 :     JS_PropertyStub,         /* addProperty */
    1405                 :     JS_PropertyStub,         /* delProperty */
    1406                 :     JS_PropertyStub,         /* getProperty */
    1407                 :     JS_StrictPropertyStub,   /* setProperty */
    1408                 :     JS_EnumerateStub,
    1409                 :     JS_ResolveStub,
    1410                 :     JS_ConvertStub,
    1411                 :     generator_finalize,
    1412                 :     NULL,                    /* checkAccess */
    1413                 :     NULL,                    /* call        */
    1414                 :     NULL,                    /* construct   */
    1415                 :     NULL,                    /* hasInstance */
    1416                 :     generator_trace,
    1417                 :     {
    1418                 :         NULL,                /* equality       */
    1419                 :         NULL,                /* outerObject    */
    1420                 :         NULL,                /* innerObject    */
    1421                 :         iterator_iterator,
    1422                 :         NULL                 /* unused */
    1423                 :     }
    1424                 : };
    1425                 : 
    1426                 : /*
    1427                 :  * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
    1428                 :  * to the frame by which the generator function was activated.  Create a new
    1429                 :  * JSGenerator object, which contains its own StackFrame that we populate
    1430                 :  * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
    1431                 :  * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
    1432                 :  * if they are non-null.
    1433                 :  */
    1434                 : JSObject *
    1435           12263 : js_NewGenerator(JSContext *cx)
    1436                 : {
    1437           12263 :     FrameRegs &stackRegs = cx->regs();
    1438           12263 :     StackFrame *stackfp = stackRegs.fp();
    1439           12263 :     JS_ASSERT(stackfp->base() == cx->regs().sp);
    1440           12263 :     JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
    1441                 : 
    1442           12263 :     GlobalObject *global = &stackfp->scopeChain().global();
    1443           12263 :     JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
    1444           12263 :     if (!proto)
    1445               0 :         return NULL;
    1446           12263 :     JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global);
    1447           12263 :     if (!obj)
    1448               0 :         return NULL;
    1449                 : 
    1450                 :     /* Load and compute stack slot counts. */
    1451           12263 :     Value *stackvp = stackfp->actualArgs() - 2;
    1452           12263 :     unsigned vplen = stackfp->formalArgsEnd() - stackvp;
    1453                 : 
    1454                 :     /* Compute JSGenerator size. */
    1455                 :     unsigned nbytes = sizeof(JSGenerator) +
    1456                 :                    (-1 + /* one Value included in JSGenerator */
    1457                 :                     vplen +
    1458                 :                     VALUES_PER_STACK_FRAME +
    1459           12263 :                     stackfp->numSlots()) * sizeof(HeapValue);
    1460                 : 
    1461           12263 :     JS_ASSERT(nbytes % sizeof(Value) == 0);
    1462                 :     JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(HeapValue) == 0);
    1463                 : 
    1464           12263 :     JSGenerator *gen = (JSGenerator *) cx->malloc_(nbytes);
    1465           12263 :     if (!gen)
    1466               0 :         return NULL;
    1467           12263 :     SetValueRangeToUndefined((Value *)gen, nbytes / sizeof(Value));
    1468                 : 
    1469                 :     /* Cut up floatingStack space. */
    1470           12263 :     HeapValue *genvp = gen->floatingStack;
    1471           12263 :     StackFrame *genfp = reinterpret_cast<StackFrame *>(genvp + vplen);
    1472                 : 
    1473                 :     /* Initialize JSGenerator. */
    1474           12263 :     gen->obj.init(obj);
    1475           12263 :     gen->state = JSGEN_NEWBORN;
    1476           12263 :     gen->enumerators = NULL;
    1477           12263 :     gen->floating = genfp;
    1478                 : 
    1479                 :     /* Copy from the stack to the generator's floating frame. */
    1480           12263 :     gen->regs.rebaseFromTo(stackRegs, *genfp);
    1481                 :     genfp->stealFrameAndSlots<HeapValue, Value, StackFrame::DoPostBarrier>(
    1482           12263 :                               genfp, genvp, stackfp, stackvp, stackRegs.sp);
    1483           12263 :     genfp->initFloatingGenerator();
    1484                 : 
    1485           12263 :     obj->setPrivate(gen);
    1486           12263 :     return obj;
    1487                 : }
    1488                 : 
    1489                 : JSGenerator *
    1490           33338 : js_FloatingFrameToGenerator(StackFrame *fp)
    1491                 : {
    1492           33338 :     JS_ASSERT(fp->isGeneratorFrame() && fp->isFloatingGenerator());
    1493           33338 :     char *floatingStackp = (char *)(fp->actualArgs() - 2);
    1494           33338 :     char *p = floatingStackp - offsetof(JSGenerator, floatingStack);
    1495           33338 :     return reinterpret_cast<JSGenerator *>(p);
    1496                 : }
    1497                 : 
    1498                 : typedef enum JSGeneratorOp {
    1499                 :     JSGENOP_NEXT,
    1500                 :     JSGENOP_SEND,
    1501                 :     JSGENOP_THROW,
    1502                 :     JSGENOP_CLOSE
    1503                 : } JSGeneratorOp;
    1504                 : 
    1505                 : /*
    1506                 :  * Start newborn or restart yielding generator and perform the requested
    1507                 :  * operation inside its frame.
    1508                 :  */
    1509                 : static JSBool
    1510           30834 : SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
    1511                 :                 JSGenerator *gen, const Value &arg)
    1512                 : {
    1513           30834 :     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
    1514               0 :         js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
    1515                 :                             JSDVG_SEARCH_STACK, ObjectOrNullValue(obj),
    1516               0 :                             JS_GetFunctionId(gen->floatingFrame()->fun()));
    1517               0 :         return JS_FALSE;
    1518                 :     }
    1519                 : 
    1520                 :     /* Check for OOM errors here, where we can fail easily. */
    1521           30834 :     if (!cx->ensureGeneratorStackSpace())
    1522               0 :         return JS_FALSE;
    1523                 : 
    1524                 :     /*
    1525                 :      * Write barrier is needed since the generator stack can be updated,
    1526                 :      * and it's not barriered in any other way. We need to do it before
    1527                 :      * gen->state changes, which can cause us to trace the generator
    1528                 :      * differently.
    1529                 :      *
    1530                 :      * We could optimize this by setting a bit on the generator to signify
    1531                 :      * that it has been marked. If this bit has already been set, there is no
    1532                 :      * need to mark again. The bit would have to be reset before the next GC,
    1533                 :      * or else some kind of epoch scheme would have to be used.
    1534                 :      */
    1535           30834 :     GeneratorWriteBarrierPre(cx, gen);
    1536                 : 
    1537           30834 :     JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
    1538           30834 :     switch (op) {
    1539                 :       case JSGENOP_NEXT:
    1540                 :       case JSGENOP_SEND:
    1541           29329 :         if (gen->state == JSGEN_OPEN) {
    1542                 :             /*
    1543                 :              * Store the argument to send as the result of the yield
    1544                 :              * expression.
    1545                 :              */
    1546           21495 :             gen->regs.sp[-1] = arg;
    1547                 :         }
    1548           29329 :         gen->state = JSGEN_RUNNING;
    1549           29329 :         break;
    1550                 : 
    1551                 :       case JSGENOP_THROW:
    1552               0 :         cx->setPendingException(arg);
    1553               0 :         gen->state = JSGEN_RUNNING;
    1554               0 :         break;
    1555                 : 
    1556                 :       default:
    1557            1505 :         JS_ASSERT(op == JSGENOP_CLOSE);
    1558            1505 :         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
    1559            1505 :         gen->state = JSGEN_CLOSING;
    1560            1505 :         break;
    1561                 :     }
    1562                 : 
    1563           30834 :     StackFrame *genfp = gen->floatingFrame();
    1564                 : 
    1565                 :     JSBool ok;
    1566                 :     {
    1567           61668 :         GeneratorFrameGuard gfg;
    1568           30834 :         if (!cx->stack.pushGeneratorFrame(cx, gen, &gfg)) {
    1569               0 :             gen->state = JSGEN_CLOSED;
    1570               0 :             return JS_FALSE;
    1571                 :         }
    1572                 : 
    1573           30834 :         StackFrame *fp = gfg.fp();
    1574           30834 :         gen->regs = cx->regs();
    1575           30834 :         JS_ASSERT(gen->liveFrame() == fp);
    1576                 : 
    1577           30834 :         cx->enterGenerator(gen);   /* OOM check above. */
    1578           30834 :         JSObject *enumerators = cx->enumerators;
    1579           30834 :         cx->enumerators = gen->enumerators;
    1580                 : 
    1581           30834 :         ok = RunScript(cx, fp->script(), fp);
    1582                 : 
    1583           30834 :         gen->enumerators = cx->enumerators;
    1584           30834 :         cx->enumerators = enumerators;
    1585           61668 :         cx->leaveGenerator(gen);
    1586                 :     }
    1587                 : 
    1588           30834 :     if (gen->floatingFrame()->isYielding()) {
    1589                 :         /* Yield cannot fail, throw or be called on closing. */
    1590           23131 :         JS_ASSERT(ok);
    1591           23131 :         JS_ASSERT(!cx->isExceptionPending());
    1592           23131 :         JS_ASSERT(gen->state == JSGEN_RUNNING);
    1593           23131 :         JS_ASSERT(op != JSGENOP_CLOSE);
    1594           23131 :         genfp->clearYielding();
    1595           23131 :         gen->state = JSGEN_OPEN;
    1596           23131 :         return JS_TRUE;
    1597                 :     }
    1598                 : 
    1599            7703 :     genfp->clearReturnValue();
    1600            7703 :     gen->state = JSGEN_CLOSED;
    1601            7703 :     if (ok) {
    1602                 :         /* Returned, explicitly or by falling off the end. */
    1603            7661 :         if (op == JSGENOP_CLOSE)
    1604            1496 :             return JS_TRUE;
    1605            6165 :         return js_ThrowStopIteration(cx);
    1606                 :     }
    1607                 : 
    1608                 :     /*
    1609                 :      * An error, silent termination by operation callback or an exception.
    1610                 :      * Propagate the condition to the caller.
    1611                 :      */
    1612              42 :     return JS_FALSE;
    1613                 : }
    1614                 : 
    1615                 : static JSBool
    1616            7167 : CloseGenerator(JSContext *cx, JSObject *obj)
    1617                 : {
    1618            7167 :     JS_ASSERT(obj->isGenerator());
    1619                 : 
    1620            7167 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
    1621            7167 :     if (!gen) {
    1622                 :         /* Generator prototype object. */
    1623               0 :         return JS_TRUE;
    1624                 :     }
    1625                 : 
    1626            7167 :     if (gen->state == JSGEN_CLOSED)
    1627            7085 :         return JS_TRUE;
    1628                 : 
    1629              82 :     return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
    1630                 : }
    1631                 : 
    1632                 : /*
    1633                 :  * Common subroutine of generator_(next|send|throw|close) methods.
    1634                 :  */
    1635                 : static JSBool
    1636           31818 : generator_op(JSContext *cx, Native native, JSGeneratorOp op, Value *vp, unsigned argc)
    1637                 : {
    1638           31818 :     CallArgs args = CallArgsFromVp(argc, vp);
    1639                 : 
    1640                 :     bool ok;
    1641           31818 :     JSObject *obj = NonGenericMethodGuard(cx, args, native, &GeneratorClass, &ok);
    1642           31818 :     if (!obj)
    1643              63 :         return ok;
    1644                 : 
    1645           31755 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
    1646           31755 :     if (!gen) {
    1647                 :         /* This happens when obj is the generator prototype. See bug 352885. */
    1648               0 :         goto closed_generator;
    1649                 :     }
    1650                 : 
    1651           31755 :     if (gen->state == JSGEN_NEWBORN) {
    1652            7834 :         switch (op) {
    1653                 :           case JSGENOP_NEXT:
    1654                 :           case JSGENOP_THROW:
    1655            7834 :             break;
    1656                 : 
    1657                 :           case JSGENOP_SEND:
    1658               0 :             if (args.hasDefined(0)) {
    1659               0 :                 js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
    1660               0 :                                     JSDVG_SEARCH_STACK, args[0], NULL);
    1661               0 :                 return false;
    1662                 :             }
    1663               0 :             break;
    1664                 : 
    1665                 :           default:
    1666               0 :             JS_ASSERT(op == JSGENOP_CLOSE);
    1667               0 :             gen->state = JSGEN_CLOSED;
    1668               0 :             args.rval().setUndefined();
    1669               0 :             return true;
    1670                 :         }
    1671           23921 :     } else if (gen->state == JSGEN_CLOSED) {
    1672                 :       closed_generator:
    1673            1003 :         switch (op) {
    1674                 :           case JSGENOP_NEXT:
    1675                 :           case JSGENOP_SEND:
    1676             990 :             return js_ThrowStopIteration(cx);
    1677                 :           case JSGENOP_THROW:
    1678               0 :             cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
    1679               0 :             return false;
    1680                 :           default:
    1681              13 :             JS_ASSERT(op == JSGENOP_CLOSE);
    1682              13 :             args.rval().setUndefined();
    1683              13 :             return true;
    1684                 :         }
    1685                 :     }
    1686                 : 
    1687           30752 :     bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && args.length() != 0);
    1688           30752 :     if (!SendToGenerator(cx, op, obj, gen, undef ? args[0] : UndefinedValue()))
    1689            6198 :         return false;
    1690                 : 
    1691           24554 :     args.rval() = gen->floatingFrame()->returnValue();
    1692           24554 :     return true;
    1693                 : }
    1694                 : 
    1695                 : static JSBool
    1696            1012 : generator_send(JSContext *cx, unsigned argc, Value *vp)
    1697                 : {
    1698            1012 :     return generator_op(cx, generator_send, JSGENOP_SEND, vp, argc);
    1699                 : }
    1700                 : 
    1701                 : static JSBool
    1702           29370 : generator_next(JSContext *cx, unsigned argc, Value *vp)
    1703                 : {
    1704           29370 :     return generator_op(cx, generator_next, JSGENOP_NEXT, vp, argc);
    1705                 : }
    1706                 : 
    1707                 : static JSBool
    1708               0 : generator_throw(JSContext *cx, unsigned argc, Value *vp)
    1709                 : {
    1710               0 :     return generator_op(cx, generator_throw, JSGENOP_THROW, vp, argc);
    1711                 : }
    1712                 : 
    1713                 : static JSBool
    1714            1436 : generator_close(JSContext *cx, unsigned argc, Value *vp)
    1715                 : {
    1716            1436 :     return generator_op(cx, generator_close, JSGENOP_CLOSE, vp, argc);
    1717                 : }
    1718                 : 
    1719                 : static JSFunctionSpec generator_methods[] = {
    1720                 :     JS_FN(js_next_str,      generator_next,     0,JSPROP_ROPERM),
    1721                 :     JS_FN(js_send_str,      generator_send,     1,JSPROP_ROPERM),
    1722                 :     JS_FN(js_throw_str,     generator_throw,    1,JSPROP_ROPERM),
    1723                 :     JS_FN(js_close_str,     generator_close,    0,JSPROP_ROPERM),
    1724                 :     JS_FS_END
    1725                 : };
    1726                 : 
    1727                 : #endif /* JS_HAS_GENERATORS */
    1728                 : 
    1729                 : static bool
    1730            2507 : InitIteratorClass(JSContext *cx, GlobalObject *global)
    1731                 : {
    1732            2507 :     JSObject *iteratorProto = global->createBlankPrototype(cx, &IteratorClass);
    1733            2507 :     if (!iteratorProto)
    1734               0 :         return false;
    1735                 : 
    1736            5014 :     AutoIdVector blank(cx);
    1737            2507 :     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
    1738            2507 :     if (!ni)
    1739               0 :         return false;
    1740            2507 :     ni->init(NULL, 0 /* flags */, 0, 0);
    1741                 : 
    1742            2507 :     iteratorProto->setNativeIterator(ni);
    1743                 : 
    1744                 :     JSFunction *ctor = global->createConstructor(cx, Iterator, &IteratorClass,
    1745            2507 :                                                  CLASS_ATOM(cx, Iterator), 2);
    1746            2507 :     if (!ctor)
    1747               0 :         return false;
    1748                 : 
    1749            2507 :     if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
    1750               0 :         return false;
    1751                 : 
    1752            2507 :     if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
    1753               0 :         return false;
    1754                 : 
    1755            2507 :     return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
    1756                 : }
    1757                 : 
    1758                 : bool
    1759            2507 : GlobalObject::initGeneratorClass(JSContext *cx)
    1760                 : {
    1761                 : #if JS_HAS_GENERATORS
    1762            2507 :     JSObject *proto = createBlankPrototype(cx, &GeneratorClass);
    1763            2507 :     if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
    1764               0 :         return false;
    1765            2507 :     setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
    1766                 : #endif
    1767            2507 :     return true;
    1768                 : }
    1769                 : 
    1770                 : static JSObject *
    1771            2507 : InitStopIterationClass(JSContext *cx, GlobalObject *global)
    1772                 : {
    1773            2507 :     JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass);
    1774            2507 :     if (!proto || !proto->freeze(cx))
    1775               0 :         return NULL;
    1776                 : 
    1777                 :     /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
    1778            2507 :     if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
    1779               0 :         return NULL;
    1780                 : 
    1781            2507 :     MarkStandardClassInitializedNoProto(global, &StopIterationClass);
    1782                 : 
    1783            2507 :     return proto;
    1784                 : }
    1785                 : 
    1786                 : JSObject *
    1787            4678 : js_InitIteratorClasses(JSContext *cx, JSObject *obj)
    1788                 : {
    1789            4678 :     JS_ASSERT(obj->isNative());
    1790                 : 
    1791            4678 :     GlobalObject *global = &obj->asGlobal();
    1792                 : 
    1793                 :     /*
    1794                 :      * Bail if Iterator has already been initialized.  We test for Iterator
    1795                 :      * rather than for StopIteration because if js_InitIteratorClasses recurs,
    1796                 :      * as happens when the StopIteration object is frozen, initializing the
    1797                 :      * Iterator class a second time will assert.
    1798                 :      */
    1799                 :     JSObject *iter;
    1800            4678 :     if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
    1801               0 :         return NULL;
    1802            4678 :     if (iter)
    1803            2171 :         return iter;
    1804                 : 
    1805            2507 :     if (!InitIteratorClass(cx, global) || !global->initGeneratorClass(cx))
    1806               0 :         return NULL;
    1807            2507 :     return InitStopIterationClass(cx, global);
    1808                 : }

Generated by: LCOV version 1.7