LCOV - app.info - js/src/jsiter.cpp

LCOV - code coverage report
| Current view: | directory - js/src - jsiter.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 770 | 634 | 82.3 %
| Date: | 2012-04-07 | Functions: | 69 | 63 | 91.3 %

           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(FreeOp *fop, 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            7629 : NativeIterator::mark(JSTracer *trc)
         146                 : {
         147         8622721 :     for (HeapPtr<JSFlatString> *str = begin(); str < end(); str++)
         148         8615092 :         MarkString(trc, str, "prop");
         149            7629 :     if (obj)
         150            3720 :         MarkObject(trc, &obj, "obj");
         151            7629 : }
         152                 :
         153                 : static void
         154          397146 : iterator_finalize(FreeOp *fop, JSObject *obj)
         155                 : {
         156          397146 :     JS_ASSERT(obj->isIterator());
         157                 :
         158          397146 :     NativeIterator *ni = obj->getNativeIterator();
         159          397146 :     if (ni) {
         160          397137 :         obj->setPrivate(NULL);
         161          397137 :         fop->free_(ni);
         162                 :     }
         163          397146 : }
         164                 :
         165                 : static void
         166            7647 : iterator_trace(JSTracer *trc, JSObject *obj)
         167                 : {
         168            7647 :     NativeIterator *ni = obj->getNativeIterator();
         169                 :
         170            7647 :     if (ni)
         171            7629 :         ni->mark(trc);
         172            7647 : }
         173                 :
         174                 : struct IdHashPolicy {
         175                 :     typedef jsid Lookup;
         176        68805213 :     static HashNumber hash(jsid id) {
         177        68805213 :         return JSID_BITS(id);
         178                 :     }
         179          264460 :     static bool match(jsid id1, jsid id2) {
         180          264460 :         return id1 == id2;
         181                 :     }
         182                 : };
         183                 :
         184                 : typedef HashSet<jsid, IdHashPolicy> IdSet;
         185                 :
         186                 : static inline bool
         187              27 : NewKeyValuePair(JSContext *cx, jsid id, const Value &val, Value *rval)
         188                 : {
         189              27 :     Value vec[2] = { IdToValue(id), val };
         190              54 :     AutoArrayRooter tvr(cx, ArrayLength(vec), vec);
         191                 :
         192              27 :     JSObject *aobj = NewDenseCopiedArray(cx, 2, vec);
         193              27 :     if (!aobj)
         194               0 :         return false;
         195              27 :     rval->setObject(*aobj);
         196              27 :     return true;
         197                 : }
         198                 :
         199                 : static inline bool
         200        73223018 : Enumerate(JSContext *cx, JSObject *obj, JSObject *pobj, jsid id,
         201                 :           bool enumerable, unsigned flags, IdSet& ht, AutoIdVector *props)
         202                 : {
         203        73223018 :     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        73223018 :     if (JS_UNLIKELY(!pobj->getProto() && JSID_IS_ATOM(id, cx->runtime->atomState.protoAtom)))
         214          395577 :         return true;
         215                 :
         216        72827441 :     if (!(flags & JSITER_OWNONLY) || pobj->isProxy() || pobj->getOps()->enumerate) {
         217                 :         /* If we've already seen this, we definitely won't add it. */
         218       137610426 :         IdSet::AddPtr p = ht.lookupForAdd(id);
         219        68805213 :         if (JS_UNLIKELY(!!p))
         220          264460 :             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        68540753 :         if ((pobj->getProto() || pobj->isProxy() || pobj->getOps()->enumerate) && !ht.add(p, id))
         228               0 :             return false;
         229                 :     }
         230                 :
         231        72562981 :     if (enumerable || (flags & JSITER_HIDDEN))
         232        57614914 :         return props->append(id);
         233                 :
         234        14948067 :     return true;
         235                 : }
         236                 :
         237                 : static bool
         238         2069854 : EnumerateNativeProperties(JSContext *cx, JSObject *obj, JSObject *pobj, unsigned flags, IdSet &ht,
         239                 :                           AutoIdVector *props)
         240                 : {
         241         4139708 :     RootObject objRoot(cx, &obj);
         242         4139708 :     RootObject pobjRoot(cx, &pobj);
         243                 :
         244         2069854 :     size_t initialLength = props->length();
         245                 :
         246                 :     /* Collect all unique properties from this object's scope. */
         247         2069854 :     Shape::Range r = pobj->lastProperty()->all();
         248         4139708 :     Shape::Range::Root root(cx, &r);
         249        36429423 :     for (; !r.empty(); r.popFront()) {
         250        34359569 :         const Shape &shape = r.front();
         251                 :
         252        68719120 :         if (!JSID_IS_DEFAULT_XML_NAMESPACE(shape.propid()) &&
         253        34359551 :             !Enumerate(cx, obj, pobj, shape.propid(), shape.enumerable(), flags, ht, props))
         254                 :         {
         255               0 :             return false;
         256                 :         }
         257                 :     }
         258                 :
         259         2069854 :     ::Reverse(props->begin() + initialLength, props->end());
         260         2069854 :     return true;
         261                 : }
         262                 :
         263                 : static bool
         264            8424 : EnumerateDenseArrayProperties(JSContext *cx, JSObject *obj, JSObject *pobj, unsigned flags,
         265                 :                               IdSet &ht, AutoIdVector *props)
         266                 : {
         267           16848 :     if (!Enumerate(cx, obj, pobj, ATOM_TO_JSID(cx->runtime->atomState.lengthAtom), false,
         268            8424 :                    flags, ht, props)) {
         269               0 :         return false;
         270                 :     }
         271                 :
         272            8424 :     if (pobj->getArrayLength() > 0) {
         273            7524 :         size_t initlen = pobj->getDenseArrayInitializedLength();
         274            7524 :         const Value *vp = pobj->getDenseArrayElements();
         275          142667 :         for (size_t i = 0; i < initlen; ++i, ++vp) {
         276          135143 :             if (!vp->isMagic(JS_ARRAY_HOLE)) {
         277                 :                 /* Dense arrays never get so large that i would not fit into an integer id. */
         278          131283 :                 if (!Enumerate(cx, obj, pobj, INT_TO_JSID(i), true, flags, ht, props))
         279               0 :                     return false;
         280                 :             }
         281                 :         }
         282                 :     }
         283                 :
         284            8424 :     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         1696838 : Snapshot(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
         319                 : {
         320         3393676 :     IdSet ht(cx);
         321         1696838 :     if (!ht.init(32))
         322               0 :         return NULL;
         323                 :
         324         3393676 :     RootObject objRoot(cx, &obj);
         325         3393676 :     RootedVarObject pobj(cx);
         326         1696838 :     pobj = obj;
         327                 :
         328          806600 :     do {
         329         2100850 :         Class *clasp = pobj->getClass();
         330         6240666 :         if (pobj->isNative() &&
         331         2069980 :             !pobj->getOps()->enumerate &&
         332         2069836 :             !(clasp->flags & JSCLASS_NEW_ENUMERATE)) {
         333         2069836 :             if (!clasp->enumerate(cx, pobj))
         334               0 :                 return false;
         335         2069836 :             if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
         336               0 :                 return false;
         337           31014 :         } else if (pobj->isDenseArray()) {
         338            8424 :             if (!EnumerateDenseArrayProperties(cx, obj, pobj, flags, ht, props))
         339               0 :                 return false;
         340                 :         } else {
         341           22590 :             if (pobj->isProxy()) {
         342           44856 :                 AutoIdVector proxyProps(cx);
         343           22428 :                 if (flags & JSITER_OWNONLY) {
         344           22428 :                     if (flags & JSITER_HIDDEN) {
         345               9 :                         if (!Proxy::getOwnPropertyNames(cx, pobj, proxyProps))
         346               0 :                             return false;
         347                 :                     } else {
         348           22419 :                         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           45936 :                 for (size_t n = 0, len = proxyProps.length(); n < len; n++) {
         356           23508 :                     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           22428 :                 break;
         361                 :             }
         362                 :             Value state;
         363             162 :             JSIterateOp op = (flags & JSITER_HIDDEN) ? JSENUMERATE_INIT_ALL : JSENUMERATE_INIT;
         364             162 :             if (!pobj->enumerate(cx, op, &state, NULL))
         365               0 :                 return false;
         366             162 :             if (state.isMagic(JS_NATIVE_ENUMERATE)) {
         367              18 :                 if (!EnumerateNativeProperties(cx, obj, pobj, flags, ht, props))
         368               0 :                     return false;
         369                 :             } else {
         370        38700252 :                 while (true) {
         371                 :                     jsid id;
         372        38700396 :                     if (!pobj->enumerate(cx, JSENUMERATE_NEXT, &state, &id))
         373               0 :                         return false;
         374        38700396 :                     if (state.isNull())
         375             144 :                         break;
         376        38700252 :                     if (!Enumerate(cx, obj, pobj, id, true, flags, ht, props))
         377               0 :                         return false;
         378                 :                 }
         379                 :             }
         380                 :         }
         381                 :
         382         2078422 :         if ((flags & JSITER_OWNONLY) || pobj->isXML())
         383         1271822 :             break;
         384          806600 :     } 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         1696838 :     return true;
         416                 : }
         417                 :
         418                 : bool
         419           25246 : js::VectorToIdArray(JSContext *cx, AutoIdVector &props, JSIdArray **idap)
         420                 : {
         421                 :     JS_STATIC_ASSERT(sizeof(JSIdArray) > sizeof(jsid));
         422           25246 :     size_t len = props.length();
         423           25246 :     size_t idsz = len * sizeof(jsid);
         424           25246 :     size_t sz = (sizeof(JSIdArray) - sizeof(jsid)) + idsz;
         425           25246 :     JSIdArray *ida = static_cast<JSIdArray *>(cx->malloc_(sz));
         426           25246 :     if (!ida)
         427               0 :         return false;
         428                 :
         429           25246 :     ida->length = static_cast<int>(len);
         430           25246 :     jsid *v = props.begin();
         431           52291 :     for (int i = 0; i < ida->length; i++)
         432           27045 :         ida->vector[i].init(v[i]);
         433           25246 :     *idap = ida;
         434           25246 :     return true;
         435                 : }
         436                 :
         437                 : JS_FRIEND_API(bool)
         438         1301144 : js::GetPropertyNames(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector *props)
         439                 : {
         440         1301144 :     return Snapshot(cx, obj, flags & (JSITER_OWNONLY | JSITER_HIDDEN), props);
         441                 : }
         442                 :
         443                 : size_t sCustomIteratorCount = 0;
         444                 :
         445                 : static inline bool
         446          401892 : GetCustomIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
         447                 : {
         448          401892 :     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          401883 :     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          401640 :     JSAtom *atom = cx->runtime->atomState.iteratorAtom;
         462          401640 :     if (!js_GetMethod(cx, obj, ATOM_TO_JSID(atom), 0, vp))
         463               0 :         return false;
         464                 :
         465                 :     /* If there is no custom __iterator__ method, we are done here. */
         466          401640 :     if (!vp->isObject()) {
         467          395694 :         vp->setUndefined();
         468          395694 :         return true;
         469                 :     }
         470                 :
         471            5946 :     if (!cx->runningWithTrustedPrincipals())
         472            5946 :         ++sCustomIteratorCount;
         473                 :
         474                 :     /* Otherwise call it and return that object. */
         475            5946 :     Value arg = BooleanValue((flags & JSITER_FOREACH) == 0);
         476            5946 :     if (!Invoke(cx, ObjectValue(*obj), *vp, 1, &arg, vp))
         477            5739 :         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          396045 : NewIteratorObject(JSContext *cx, unsigned flags)
         514                 : {
         515          396045 :     if (flags & JSITER_ENUMERATE) {
         516          792000 :         RootedVarTypeObject type(cx);
         517          396000 :         type = cx->compartment->getEmptyType(cx);
         518          396000 :         if (!type)
         519               0 :             return NULL;
         520                 :
         521          792000 :         RootedVarShape emptyEnumeratorShape(cx);
         522                 :         emptyEnumeratorShape = EmptyShape::getInitialShape(cx, &IteratorClass, NULL, NULL,
         523          396000 :                                                            ITERATOR_FINALIZE_KIND);
         524          396000 :         if (!emptyEnumeratorShape)
         525               0 :             return NULL;
         526                 :
         527                 :         JSObject *obj = JSObject::create(cx, ITERATOR_FINALIZE_KIND,
         528          396000 :                                          emptyEnumeratorShape, type, NULL);
         529          396000 :         if (!obj)
         530               0 :             return NULL;
         531                 :
         532          396000 :         JS_ASSERT(obj->numFixedSlots() == JSObject::ITER_CLASS_NFIXED_SLOTS);
         533          396000 :         return obj;
         534                 :     }
         535                 :
         536              45 :     return NewBuiltinClassInstance(cx, &IteratorClass);
         537                 : }
         538                 :
         539                 : NativeIterator *
         540          397146 : NativeIterator::allocateIterator(JSContext *cx, uint32_t slength, const AutoIdVector &props)
         541                 : {
         542          397146 :     size_t plength = props.length();
         543                 :     NativeIterator *ni = (NativeIterator *)
         544                 :         cx->malloc_(sizeof(NativeIterator)
         545                 :                     + plength * sizeof(JSString *)
         546          397146 :                     + slength * sizeof(Shape *));
         547          397146 :     if (!ni)
         548               0 :         return NULL;
         549          794292 :     AutoValueVector strings(cx);
         550          397146 :     ni->props_array = ni->props_cursor = (HeapPtr<JSFlatString> *) (ni + 1);
         551          397146 :     ni->props_end = ni->props_array + plength;
         552          397146 :     if (plength) {
         553        53644564 :         for (size_t i = 0; i < plength; i++) {
         554        53250418 :             JSFlatString *str = IdToString(cx, props[i]);
         555        53250418 :             if (!str || !strings.append(StringValue(str)))
         556               9 :                 return NULL;
         557        53250409 :             ni->props_array[i].init(str);
         558                 :         }
         559                 :     }
         560          397137 :     return ni;
         561                 : }
         562                 :
         563                 : inline void
         564          397137 : NativeIterator::init(JSObject *obj, unsigned flags, uint32_t slength, uint32_t key)
         565                 : {
         566          397137 :     this->obj.init(obj);
         567          397137 :     this->flags = flags;
         568          397137 :     this->shapes_array = (const Shape **) this->props_end;
         569          397137 :     this->shapes_length = slength;
         570          397137 :     this->shapes_key = key;
         571          397137 : }
         572                 :
         573                 : static inline void
         574          462065 : RegisterEnumerator(JSContext *cx, JSObject *iterobj, NativeIterator *ni)
         575                 : {
         576                 :     /* Register non-escaping native enumerators (for-in) with the current context. */
         577          462065 :     if (ni->flags & JSITER_ENUMERATE) {
         578          462020 :         ni->next = cx->enumerators;
         579          462020 :         cx->enumerators = iterobj;
         580                 :
         581          462020 :         JS_ASSERT(!(ni->flags & JSITER_ACTIVE));
         582          462020 :         ni->flags |= JSITER_ACTIVE;
         583                 :     }
         584          462065 : }
         585                 :
         586                 : static inline bool
         587          247167 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys,
         588                 :                     uint32_t slength, uint32_t key, Value *vp)
         589                 : {
         590          247167 :     JS_ASSERT(!(flags & JSITER_FOREACH));
         591                 :
         592          247167 :     if (obj) {
         593          246843 :         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
         594               0 :             return false;
         595          246843 :         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
         596                 :     }
         597                 :
         598          247167 :     JSObject *iterobj = NewIteratorObject(cx, flags);
         599          247167 :     if (!iterobj)
         600               0 :         return false;
         601                 :
         602          247167 :     NativeIterator *ni = NativeIterator::allocateIterator(cx, slength, keys);
         603          247167 :     if (!ni)
         604               9 :         return false;
         605          247158 :     ni->init(obj, flags, slength, key);
         606                 :
         607          247158 :     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            1685 :         JSObject *pobj = obj;
         616            1685 :         size_t ind = 0;
         617            3330 :         do {
         618            3330 :             ni->shapes_array[ind++] = pobj->lastProperty();
         619            3330 :             pobj = pobj->getProto();
         620                 :         } while (pobj);
         621            1685 :         JS_ASSERT(ind == slength);
         622                 :     }
         623                 :
         624          247158 :     iterobj->setNativeIterator(ni);
         625          247158 :     vp->setObject(*iterobj);
         626                 :
         627          247158 :     RegisterEnumerator(cx, iterobj, ni);
         628          247158 :     return true;
         629                 : }
         630                 :
         631                 : namespace js {
         632                 :
         633                 : bool
         634              27 : VectorToKeyIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &props, Value *vp)
         635                 : {
         636              27 :     return VectorToKeyIterator(cx, obj, flags, props, 0, 0, vp);
         637                 : }
         638                 :
         639                 : bool
         640          148878 : VectorToValueIterator(JSContext *cx, JSObject *obj, unsigned flags, AutoIdVector &keys,
         641                 :                       Value *vp)
         642                 : {
         643          148878 :     JS_ASSERT(flags & JSITER_FOREACH);
         644                 :
         645          148878 :     if (obj) {
         646          148878 :         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
         647               0 :             return false;
         648          148878 :         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
         649                 :     }
         650                 :
         651          148878 :     JSObject *iterobj = NewIteratorObject(cx, flags);
         652          148878 :     if (!iterobj)
         653               0 :         return false;
         654                 :
         655          148878 :     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
         656          148878 :     if (!ni)
         657               0 :         return false;
         658          148878 :     ni->init(obj, flags, 0, 0);
         659                 :
         660          148878 :     iterobj->setNativeIterator(ni);
         661          148878 :     vp->setObject(*iterobj);
         662                 :
         663          148878 :     RegisterEnumerator(cx, iterobj, ni);
         664          148878 :     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           66029 : 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           66029 :     ni->obj = obj;
         682           66029 : }
         683                 :
         684                 : bool
         685          472401 : GetIterator(JSContext *cx, JSObject *obj, unsigned flags, Value *vp)
         686                 : {
         687          944802 :     Vector<const Shape *, 8> shapes(cx);
         688          472401 :     uint32_t key = 0;
         689                 :
         690          472401 :     bool keysOnly = (flags == JSITER_ENUMERATE);
         691                 :
         692          472401 :     if (obj) {
         693                 :         /* Enumerate Iterator.prototype directly. */
         694          472077 :         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           12031 :             if (!(obj->getClass()->flags & JSCLASS_FOR_OF_ITERATION) || flags == JSITER_FOR_OF) {
         704            4066 :                 JSObject *iterobj = op(cx, obj, !(flags & (JSITER_FOREACH | JSITER_FOR_OF)));
         705            4066 :                 if (!iterobj)
         706               0 :                     return false;
         707            4066 :                 vp->setObject(*iterobj);
         708            4066 :                 types::MarkIteratorUnknown(cx);
         709            4066 :                 return true;
         710                 :             }
         711                 :         }
         712                 :
         713          468011 :         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          313088 :             JSObject *last = cx->compartment->nativeIterCache.last;
         721          313088 :             JSObject *proto = obj->getProto();
         722          313088 :             if (last) {
         723           66934 :                 NativeIterator *lastni = last->getNativeIterator();
         724          376579 :                 if (!(lastni->flags & (JSITER_ACTIVE|JSITER_UNREUSABLE)) &&
         725           66159 :                     obj->isNative() &&
         726           66144 :                     obj->lastProperty() == lastni->shapes_array[0] &&
         727           59114 :                     proto && proto->isNative() &&
         728           59114 :                     proto->lastProperty() == lastni->shapes_array[1] &&
         729           59114 :                     !proto->getProto()) {
         730           59114 :                     vp->setObject(*last);
         731           59114 :                     UpdateNativeIterator(lastni, obj);
         732           59114 :                     RegisterEnumerator(cx, last, lastni);
         733           59114 :                     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          253974 :             JSObject *pobj = obj;
         744          258784 :             do {
         745         1523567 :                 if (!pobj->isNative() ||
         746          500825 :                     pobj->hasUncacheableProto() ||
         747          259397 :                     obj->getOps()->enumerate ||
         748          259307 :                     pobj->getClass()->enumerate != JS_EnumerateStub) {
         749          245254 :                     shapes.clear();
         750          245254 :                     goto miss;
         751                 :                 }
         752          258784 :                 const Shape *shape = pobj->lastProperty();
         753          258784 :                 key = (key + (key << 16)) ^ (uintptr_t(shape) >> 3);
         754          258784 :                 if (!shapes.append((Shape *) shape))
         755               0 :                     return false;
         756          258784 :                 pobj = pobj->getProto();
         757                 :             } while (pobj);
         758                 :
         759            8720 :             JSObject *iterobj = cx->compartment->nativeIterCache.get(key);
         760            8720 :             if (iterobj) {
         761            6965 :                 NativeIterator *ni = iterobj->getNativeIterator();
         762           20795 :                 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          401982 :         if (obj->isProxy()) {
         779              90 :             types::MarkIteratorUnknown(cx);
         780              90 :             return Proxy::iterate(cx, obj, flags, vp);
         781                 :         }
         782          401892 :         if (!GetCustomIterator(cx, obj, flags, vp))
         783            5991 :             return false;
         784          395901 :         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          792036 :     AutoIdVector keys(cx);
         793          396018 :     if (flags & JSITER_FOREACH) {
         794          148878 :         if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
         795               0 :             return false;
         796          148878 :         JS_ASSERT(shapes.empty());
         797          148878 :         if (!VectorToValueIterator(cx, obj, flags, keys, vp))
         798               0 :             return false;
         799                 :     } else {
         800          247140 :         if (JS_LIKELY(obj != NULL) && !Snapshot(cx, obj, flags, &keys))
         801               0 :             return false;
         802          247140 :         if (!VectorToKeyIterator(cx, obj, flags, keys, shapes.length(), key, vp))
         803               9 :             return false;
         804                 :     }
         805                 :
         806          396009 :     JSObject *iterobj = &vp->toObject();
         807                 :
         808                 :     /* Cache the iterator object if possible. */
         809          396009 :     if (shapes.length())
         810            1685 :         cx->compartment->nativeIterCache.set(key, iterobj);
         811                 :
         812          396009 :     if (shapes.length() == 2)
         813            1505 :         cx->compartment->nativeIterCache.last = iterobj;
         814          396009 :     return true;
         815                 : }
         816                 :
         817                 : }
         818                 :
         819                 : static JSObject *
         820            3138 : iterator_iterator(JSContext *cx, JSObject *obj, JSBool keysonly)
         821                 : {
         822            3138 :     return obj;
         823                 : }
         824                 :
         825                 : static JSBool
         826            5784 : Iterator(JSContext *cx, unsigned argc, Value *vp)
         827                 : {
         828            5784 :     Value *argv = JS_ARGV(cx, vp);
         829            5784 :     bool keyonly = argc >= 2 ? js_ValueToBoolean(argv[1]) : false;
         830            5784 :     unsigned flags = JSITER_OWNONLY | (keyonly ? 0 : (JSITER_FOREACH | JSITER_KEYVALUE));
         831            5784 :     *vp = argc >= 1 ? argv[0] : UndefinedValue();
         832            5784 :     return ValueToIterator(cx, flags, vp);
         833                 : }
         834                 :
         835                 : JSBool
         836            3186 : js_ThrowStopIteration(JSContext *cx)
         837                 : {
         838                 :     Value v;
         839                 :
         840            3186 :     JS_ASSERT(!JS_IsExceptionPending(cx));
         841            3186 :     if (js_FindClassObject(cx, NULL, JSProto_StopIteration, &v))
         842            3186 :         cx->setPendingException(v);
         843            3186 :     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          472392 : js::ValueToIterator(JSContext *cx, unsigned flags, Value *vp)
         885                 : {
         886                 :     /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
         887          472392 :     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          472392 :     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
         895                 :
         896                 :     JSObject *obj;
         897          472392 :     if (vp->isObject()) {
         898                 :         /* Common case. */
         899          465978 :         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            6414 :         if ((flags & JSITER_ENUMERATE)) {
         909             540 :             if (!js_ValueToObjectOrNull(cx, *vp, &obj))
         910               0 :                 return false;
         911                 :             /* fall through */
         912                 :         } else {
         913            5874 :             obj = js_ValueToNonNullObject(cx, *vp);
         914            5874 :             if (!obj)
         915              36 :                 return false;
         916                 :         }
         917                 :     }
         918                 :
         919          472356 :     return GetIterator(cx, obj, flags, vp);
         920                 : }
         921                 :
         922                 : bool
         923          166650 : js::CloseIterator(JSContext *cx, JSObject *obj)
         924                 : {
         925          166650 :     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
         926                 :
         927          166650 :     if (obj->isIterator()) {
         928                 :         /* Remove enumerators from the active list, which is a stack. */
         929          162341 :         NativeIterator *ni = obj->getNativeIterator();
         930                 :
         931          162341 :         if (ni->flags & JSITER_ENUMERATE) {
         932          162341 :             JS_ASSERT(cx->enumerators == obj);
         933          162341 :             cx->enumerators = ni->next;
         934                 :
         935          162341 :             JS_ASSERT(ni->flags & JSITER_ACTIVE);
         936          162341 :             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          162341 :             ni->props_cursor = ni->props_array;
         943                 :         }
         944                 :     }
         945                 : #if JS_HAS_GENERATORS
         946            4309 :     else if (obj->isGenerator()) {
         947            3201 :         return CloseGenerator(cx, obj);
         948                 :     }
         949                 : #endif
         950          163449 :     return JS_TRUE;
         951                 : }
         952                 :
         953                 : bool
         954             151 : js::UnwindIteratorForException(JSContext *cx, JSObject *obj)
         955                 : {
         956             151 :     Value v = cx->getPendingException();
         957             151 :     cx->clearPendingException();
         958             151 :     if (!CloseIterator(cx, obj))
         959              11 :         return false;
         960             140 :     cx->setPendingException(v);
         961             140 :     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           49726 : SuppressDeletedPropertyHelper(JSContext *cx, JSObject *obj, StringPredicate predicate)
         996                 : {
         997           49726 :     JSObject *iterobj = cx->enumerators;
         998           99785 :     while (iterobj) {
         999                 :       again:
        1000             333 :         NativeIterator *ni = iterobj->getNativeIterator();
        1001                 :         /* This only works for identified surpressed keys, not values. */
        1002             333 :         if (ni->isKeyIter() && ni->obj == obj && ni->props_cursor < ni->props_end) {
        1003                 :             /* Check whether id is still to come. */
        1004             306 :             HeapPtr<JSFlatString> *props_cursor = ni->current();
        1005             306 :             HeapPtr<JSFlatString> *props_end = ni->end();
        1006             819 :             for (HeapPtr<JSFlatString> *idp = props_cursor; idp < props_end; ++idp) {
        1007             756 :                 if (predicate(*idp)) {
        1008                 :                     /*
        1009                 :                      * Check whether another property along the prototype chain
        1010                 :                      * became visible as a result of this deletion.
        1011                 :                      */
        1012             243 :                     if (obj->getProto()) {
        1013             243 :                         JSObject *proto = obj->getProto();
        1014                 :                         JSObject *obj2;
        1015                 :                         JSProperty *prop;
        1016                 :                         jsid id;
        1017             243 :                         if (!ValueToId(cx, StringValue(*idp), &id))
        1018               0 :                             return false;
        1019             243 :                         id = js_CheckForStringIndex(id);
        1020             243 :                         if (!proto->lookupGeneric(cx, id, &obj2, &prop))
        1021               0 :                             return false;
        1022             243 :                         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             243 :                     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             243 :                     if (idp == props_cursor) {
        1047             126 :                         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             243 :                     ni->flags |= JSITER_UNREUSABLE;
        1062                 :
        1063             243 :                     if (predicate.matchesAtMostOne())
        1064             243 :                         break;
        1065                 :                 }
        1066                 :             }
        1067                 :         }
        1068             333 :         iterobj = ni->next;
        1069                 :     }
        1070           49726 :     return true;
        1071                 : }
        1072                 :
        1073                 : class SingleStringPredicate {
        1074                 :     JSFlatString *str;
        1075                 : public:
        1076           49674 :     SingleStringPredicate(JSFlatString *str) : str(str) {}
        1077                 :
        1078             756 :     bool operator()(JSFlatString *str) { return EqualStrings(str, this->str); }
        1079             243 :     bool matchesAtMostOne() { return true; }
        1080                 : };
        1081                 :
        1082                 : bool
        1083           49674 : js_SuppressDeletedProperty(JSContext *cx, JSObject *obj, jsid id)
        1084                 : {
        1085           49674 :     JSFlatString *str = IdToString(cx, id);
        1086           49674 :     if (!str)
        1087               0 :         return false;
        1088           49674 :     return SuppressDeletedPropertyHelper(cx, obj, SingleStringPredicate(str));
        1089                 : }
        1090                 :
        1091                 : bool
        1092           18384 : js_SuppressDeletedElement(JSContext *cx, JSObject *obj, uint32_t index)
        1093                 : {
        1094                 :     jsid id;
        1095           18384 :     if (!IndexToId(cx, index, &id))
        1096               0 :         return false;
        1097           18384 :     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        12143005 : js_IteratorMore(JSContext *cx, JSObject *iterobj, Value *rval)
        1189                 : {
        1190                 :     /* Fast path for native iterators */
        1191        12143005 :     NativeIterator *ni = NULL;
        1192        12143005 :     if (iterobj->isIterator()) {
        1193                 :         /* Key iterators are handled by fast-paths. */
        1194        12121794 :         ni = iterobj->getNativeIterator();
        1195        12121794 :         bool more = ni->props_cursor < ni->props_end;
        1196        12121794 :         if (ni->isKeyIter() || !more) {
        1197          148779 :             rval->setBoolean(more);
        1198          148779 :             return true;
        1199                 :         }
        1200                 :     }
        1201                 :
        1202                 :     /* We might still have a pending value. */
        1203        11994226 :     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        11994226 :     JS_CHECK_RECURSION(cx, return false);
        1210                 :
        1211                 :     /* Fetch and cache the next value from the iterator. */
        1212        11994224 :     if (ni) {
        1213        11973015 :         JS_ASSERT(!ni->isKeyIter());
        1214                 :         jsid id;
        1215        11973015 :         if (!ValueToId(cx, StringValue(*ni->current()), &id))
        1216               0 :             return false;
        1217        11973015 :         id = js_CheckForStringIndex(id);
        1218        11973015 :         ni->incCursor();
        1219        11973015 :         if (!ni->obj->getGeneric(cx, id, rval))
        1220              18 :             return false;
        1221        11972997 :         if ((ni->flags & JSITER_KEYVALUE) && !NewKeyValuePair(cx, id, *rval, rval))
        1222               0 :             return false;
        1223           21209 :     } 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           17267 :     } 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           17213 :         jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
        1245           17213 :         if (!js_GetMethod(cx, iterobj, id, 0, rval))
        1246               0 :             return false;
        1247           17213 :         if (!Invoke(cx, ObjectValue(*iterobj), *rval, 0, NULL, rval)) {
        1248                 :             /* Check for StopIteration. */
        1249            3299 :             if (!cx->isExceptionPending() || !IsStopIteration(cx->getPendingException()))
        1250              23 :                 return false;
        1251                 :
        1252            3276 :             cx->clearPendingException();
        1253            3276 :             cx->iterValue.setMagic(JS_NO_ITER_VALUE);
        1254            3276 :             rval->setBoolean(false);
        1255            3276 :             return true;
        1256                 :         }
        1257                 :     }
        1258                 :
        1259                 :     /* Cache the value returned by iterobj.next() so js_IteratorNext() can find it. */
        1260        11990160 :     JS_ASSERT(!rval->isMagic(JS_NO_ITER_VALUE));
        1261        11990160 :     cx->iterValue = *rval;
        1262        11990160 :     rval->setBoolean(true);
        1263        11990160 :     return true;
        1264                 : }
        1265                 :
        1266                 : JSBool
        1267        11990115 : js_IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
        1268                 : {
        1269                 :     /* Fast path for native iterators */
        1270        11990115 :     if (iterobj->isIterator()) {
        1271                 :         /*
        1272                 :          * Implement next directly as all the methods of the native iterator are
        1273                 :          * read-only and permanent.
        1274                 :          */
        1275        11972997 :         NativeIterator *ni = iterobj->getNativeIterator();
        1276        11972997 :         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        11990115 :     JS_ASSERT(!cx->iterValue.isMagic(JS_NO_ITER_VALUE));
        1300        11990115 :     *rval = cx->iterValue;
        1301        11990115 :     cx->iterValue.setMagic(JS_NO_ITER_VALUE);
        1302                 :
        1303        11990115 :     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            9323 : generator_finalize(FreeOp *fop, JSObject *obj)
        1335                 : {
        1336            9323 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
        1337            9323 :     if (!gen)
        1338            1101 :         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            8222 :               gen->state == JSGEN_OPEN);
        1347            8222 :     fop->free_(gen);
        1348                 : }
        1349                 :
        1350                 : static void
        1351            2815 : MarkGenerator(JSTracer *trc, JSGenerator *gen)
        1352                 : {
        1353            2815 :     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            2815 :     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            2815 :     MarkValueRange(trc, (HeapValue *)fp->formalArgsEnd() - gen->floatingStack,
        1369            5630 :                    gen->floatingStack, "Generator Floating Args");
        1370            2815 :     fp->mark(trc);
        1371            2815 :     MarkValueRange(trc, gen->regs.sp - fp->slots(),
        1372            5630 :                    (HeapValue *)fp->slots(), "Generator Floating Stack");
        1373            2815 : }
        1374                 :
        1375                 : static void
        1376           19103 : GeneratorWriteBarrierPre(JSContext *cx, JSGenerator *gen)
        1377                 : {
        1378           19103 :     JSCompartment *comp = cx->compartment;
        1379           19103 :     if (comp->needsBarrier())
        1380              18 :         MarkGenerator(comp->barrierTracer(), gen);
        1381           19103 : }
        1382                 :
        1383                 : static void
        1384          231759 : generator_trace(JSTracer *trc, JSObject *obj)
        1385                 : {
        1386          231759 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
        1387          231759 :     if (!gen)
        1388            3909 :         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          227850 :     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING)
        1395          225053 :         return;
        1396                 :
        1397            2797 :     JS_ASSERT(gen->liveFrame() == gen->floatingFrame());
        1398            2797 :     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            8222 : js_NewGenerator(JSContext *cx)
        1436                 : {
        1437            8222 :     FrameRegs &stackRegs = cx->regs();
        1438            8222 :     StackFrame *stackfp = stackRegs.fp();
        1439            8222 :     JS_ASSERT(stackfp->base() == cx->regs().sp);
        1440            8222 :     JS_ASSERT(stackfp->actualArgs() <= stackfp->formalArgs());
        1441                 :
        1442            8222 :     GlobalObject *global = &stackfp->scopeChain().global();
        1443            8222 :     JSObject *proto = global->getOrCreateGeneratorPrototype(cx);
        1444            8222 :     if (!proto)
        1445               0 :         return NULL;
        1446            8222 :     JSObject *obj = NewObjectWithGivenProto(cx, &GeneratorClass, proto, global);
        1447            8222 :     if (!obj)
        1448               0 :         return NULL;
        1449                 :
        1450                 :     /* Load and compute stack slot counts. */
        1451            8222 :     Value *stackvp = stackfp->actualArgs() - 2;
        1452            8222 :     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            8222 :                     stackfp->numSlots()) * sizeof(HeapValue);
        1460                 :
        1461            8222 :     JS_ASSERT(nbytes % sizeof(Value) == 0);
        1462                 :     JS_STATIC_ASSERT(sizeof(StackFrame) % sizeof(HeapValue) == 0);
        1463                 :
        1464            8222 :     JSGenerator *gen = (JSGenerator *) cx->malloc_(nbytes);
        1465            8222 :     if (!gen)
        1466               0 :         return NULL;
        1467            8222 :     SetValueRangeToUndefined((Value *)gen, nbytes / sizeof(Value));
        1468                 :
        1469                 :     /* Cut up floatingStack space. */
        1470            8222 :     HeapValue *genvp = gen->floatingStack;
        1471            8222 :     StackFrame *genfp = reinterpret_cast<StackFrame *>(genvp + vplen);
        1472                 :
        1473                 :     /* Initialize JSGenerator. */
        1474            8222 :     gen->obj.init(obj);
        1475            8222 :     gen->state = JSGEN_NEWBORN;
        1476            8222 :     gen->enumerators = NULL;
        1477            8222 :     gen->floating = genfp;
        1478                 :
        1479                 :     /* Copy from the stack to the generator's floating frame. */
        1480            8222 :     gen->regs.rebaseFromTo(stackRegs, *genfp);
        1481                 :     genfp->stealFrameAndSlots<HeapValue, Value, StackFrame::DoPostBarrier>(
        1482            8222 :                               genfp, genvp, stackfp, stackvp, stackRegs.sp);
        1483            8222 :     genfp->initFloatingGenerator();
        1484                 :
        1485            8222 :     obj->setPrivate(gen);
        1486            8222 :     return obj;
        1487                 : }
        1488                 :
        1489                 : JSGenerator *
        1490           19543 : js_FloatingFrameToGenerator(StackFrame *fp)
        1491                 : {
        1492           19543 :     JS_ASSERT(fp->isGeneratorFrame() && fp->isFloatingGenerator());
        1493           19543 :     char *floatingStackp = (char *)(fp->actualArgs() - 2);
        1494           19543 :     char *p = floatingStackp - offsetof(JSGenerator, floatingStack);
        1495           19543 :     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           19103 : SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
        1511                 :                 JSGenerator *gen, const Value &arg)
        1512                 : {
        1513           19103 :     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           19103 :     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           19103 :     GeneratorWriteBarrierPre(cx, gen);
        1536                 :
        1537           19103 :     JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
        1538           19103 :     switch (op) {
        1539                 :       case JSGENOP_NEXT:
        1540                 :       case JSGENOP_SEND:
        1541           17629 :         if (gen->state == JSGEN_OPEN) {
        1542                 :             /*
        1543                 :              * Store the argument to send as the result of the yield
        1544                 :              * expression.
        1545                 :              */
        1546           13837 :             gen->regs.sp[-1] = arg;
        1547                 :         }
        1548           17629 :         gen->state = JSGEN_RUNNING;
        1549           17629 :         break;
        1550                 :
        1551                 :       case JSGENOP_THROW:
        1552               0 :         cx->setPendingException(arg);
        1553               0 :         gen->state = JSGEN_RUNNING;
        1554               0 :         break;
        1555                 :
        1556                 :       default:
        1557            1474 :         JS_ASSERT(op == JSGENOP_CLOSE);
        1558            1474 :         cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING));
        1559            1474 :         gen->state = JSGEN_CLOSING;
        1560            1474 :         break;
        1561                 :     }
        1562                 :
        1563           19103 :     StackFrame *genfp = gen->floatingFrame();
        1564                 :
        1565                 :     JSBool ok;
        1566                 :     {
        1567           38206 :         GeneratorFrameGuard gfg;
        1568           19103 :         if (!cx->stack.pushGeneratorFrame(cx, gen, &gfg)) {
        1569               0 :             gen->state = JSGEN_CLOSED;
        1570               0 :             return JS_FALSE;
        1571                 :         }
        1572                 :
        1573           19103 :         StackFrame *fp = gfg.fp();
        1574           19103 :         gen->regs = cx->regs();
        1575           19103 :         JS_ASSERT(gen->liveFrame() == fp);
        1576                 :
        1577           19103 :         cx->enterGenerator(gen);   /* OOM check above. */
        1578           19103 :         JSObject *enumerators = cx->enumerators;
        1579           19103 :         cx->enumerators = gen->enumerators;
        1580                 :
        1581           19103 :         ok = RunScript(cx, fp->script(), fp);
        1582                 :
        1583           19103 :         gen->enumerators = cx->enumerators;
        1584           19103 :         cx->enumerators = enumerators;
        1585           38206 :         cx->leaveGenerator(gen);
        1586                 :     }
        1587                 :
        1588           19103 :     if (gen->floatingFrame()->isYielding()) {
        1589                 :         /* Yield cannot fail, throw or be called on closing. */
        1590           15426 :         JS_ASSERT(ok);
        1591           15426 :         JS_ASSERT(!cx->isExceptionPending());
        1592           15426 :         JS_ASSERT(gen->state == JSGEN_RUNNING);
        1593           15426 :         JS_ASSERT(op != JSGENOP_CLOSE);
        1594           15426 :         genfp->clearYielding();
        1595           15426 :         gen->state = JSGEN_OPEN;
        1596           15426 :         return JS_TRUE;
        1597                 :     }
        1598                 :
        1599            3677 :     genfp->clearReturnValue();
        1600            3677 :     gen->state = JSGEN_CLOSED;
        1601            3677 :     if (ok) {
        1602                 :         /* Returned, explicitly or by falling off the end. */
        1603            3629 :         if (op == JSGENOP_CLOSE)
        1604            1461 :             return JS_TRUE;
        1605            2168 :         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              48 :     return JS_FALSE;
        1613                 : }
        1614                 :
        1615                 : static JSBool
        1616            3201 : CloseGenerator(JSContext *cx, JSObject *obj)
        1617                 : {
        1618            3201 :     JS_ASSERT(obj->isGenerator());
        1619                 :
        1620            3201 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
        1621            3201 :     if (!gen) {
        1622                 :         /* Generator prototype object. */
        1623               0 :         return JS_TRUE;
        1624                 :     }
        1625                 :
        1626            3201 :     if (gen->state == JSGEN_CLOSED)
        1627            3118 :         return JS_TRUE;
        1628                 :
        1629              83 :     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           20073 : generator_op(JSContext *cx, Native native, JSGeneratorOp op, Value *vp, unsigned argc)
        1637                 : {
        1638           20073 :     CallArgs args = CallArgsFromVp(argc, vp);
        1639                 :
        1640                 :     bool ok;
        1641           20073 :     JSObject *obj = NonGenericMethodGuard(cx, args, native, &GeneratorClass, &ok);
        1642           20073 :     if (!obj)
        1643              63 :         return ok;
        1644                 :
        1645           20010 :     JSGenerator *gen = (JSGenerator *) obj->getPrivate();
        1646           20010 :     if (!gen) {
        1647                 :         /* This happens when obj is the generator prototype. See bug 352885. */
        1648               0 :         goto closed_generator;
        1649                 :     }
        1650                 :
        1651           20010 :     if (gen->state == JSGEN_NEWBORN) {
        1652            3792 :         switch (op) {
        1653                 :           case JSGENOP_NEXT:
        1654                 :           case JSGENOP_THROW:
        1655            3792 :             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           16218 :     } else if (gen->state == JSGEN_CLOSED) {
        1672                 :       closed_generator:
        1673             990 :         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               0 :             JS_ASSERT(op == JSGENOP_CLOSE);
        1682               0 :             args.rval().setUndefined();
        1683               0 :             return true;
        1684                 :         }
        1685                 :     }
        1686                 :
        1687           19020 :     bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && args.length() != 0);
        1688           19020 :     if (!SendToGenerator(cx, op, obj, gen, undef ? args[0] : UndefinedValue()))
        1689            2205 :         return false;
        1690                 :
        1691           16815 :     args.rval() = gen->floatingFrame()->returnValue();
        1692           16815 :     return true;
        1693                 : }
        1694                 :
        1695                 : static JSBool
        1696               0 : generator_send(JSContext *cx, unsigned argc, Value *vp)
        1697                 : {
        1698               0 :     return generator_op(cx, generator_send, JSGENOP_SEND, vp, argc);
        1699                 : }
        1700                 :
        1701                 : static JSBool
        1702           18682 : generator_next(JSContext *cx, unsigned argc, Value *vp)
        1703                 : {
        1704           18682 :     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            1391 : generator_close(JSContext *cx, unsigned argc, Value *vp)
        1715                 : {
        1716            1391 :     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            1101 : InitIteratorClass(JSContext *cx, GlobalObject *global)
        1731                 : {
        1732            1101 :     JSObject *iteratorProto = global->createBlankPrototype(cx, &IteratorClass);
        1733            1101 :     if (!iteratorProto)
        1734               0 :         return false;
        1735                 :
        1736            2202 :     AutoIdVector blank(cx);
        1737            1101 :     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, blank);
        1738            1101 :     if (!ni)
        1739               0 :         return false;
        1740            1101 :     ni->init(NULL, 0 /* flags */, 0, 0);
        1741                 :
        1742            1101 :     iteratorProto->setNativeIterator(ni);
        1743                 :
        1744            1101 :     JSFunction *ctor = global->createConstructor(cx, Iterator, CLASS_ATOM(cx, Iterator), 2);
        1745            1101 :     if (!ctor)
        1746               0 :         return false;
        1747                 :
        1748            1101 :     if (!LinkConstructorAndPrototype(cx, ctor, iteratorProto))
        1749               0 :         return false;
        1750                 :
        1751            1101 :     if (!DefinePropertiesAndBrand(cx, iteratorProto, NULL, iterator_methods))
        1752               0 :         return false;
        1753                 :
        1754            1101 :     return DefineConstructorAndPrototype(cx, global, JSProto_Iterator, ctor, iteratorProto);
        1755                 : }
        1756                 :
        1757                 : bool
        1758            1101 : GlobalObject::initGeneratorClass(JSContext *cx)
        1759                 : {
        1760                 : #if JS_HAS_GENERATORS
        1761            1101 :     JSObject *proto = createBlankPrototype(cx, &GeneratorClass);
        1762            1101 :     if (!proto || !DefinePropertiesAndBrand(cx, proto, NULL, generator_methods))
        1763               0 :         return false;
        1764            1101 :     setReservedSlot(GENERATOR_PROTO, ObjectValue(*proto));
        1765                 : #endif
        1766            1101 :     return true;
        1767                 : }
        1768                 :
        1769                 : static JSObject *
        1770            1101 : InitStopIterationClass(JSContext *cx, GlobalObject *global)
        1771                 : {
        1772            1101 :     JSObject *proto = global->createBlankPrototype(cx, &StopIterationClass);
        1773            1101 :     if (!proto || !proto->freeze(cx))
        1774               0 :         return NULL;
        1775                 :
        1776                 :     /* This should use a non-JSProtoKey'd slot, but this is easier for now. */
        1777            1101 :     if (!DefineConstructorAndPrototype(cx, global, JSProto_StopIteration, proto, proto))
        1778               0 :         return NULL;
        1779                 :
        1780            1101 :     MarkStandardClassInitializedNoProto(global, &StopIterationClass);
        1781                 :
        1782            1101 :     return proto;
        1783                 : }
        1784                 :
        1785                 : JSObject *
        1786            2130 : js_InitIteratorClasses(JSContext *cx, JSObject *obj)
        1787                 : {
        1788            2130 :     JS_ASSERT(obj->isNative());
        1789                 :
        1790            2130 :     GlobalObject *global = &obj->asGlobal();
        1791                 :
        1792                 :     /*
        1793                 :      * Bail if Iterator has already been initialized.  We test for Iterator
        1794                 :      * rather than for StopIteration because if js_InitIteratorClasses recurs,
        1795                 :      * as happens when the StopIteration object is frozen, initializing the
        1796                 :      * Iterator class a second time will assert.
        1797                 :      */
        1798                 :     JSObject *iter;
        1799            2130 :     if (!js_GetClassObject(cx, global, JSProto_Iterator, &iter))
        1800               0 :         return NULL;
        1801            2130 :     if (iter)
        1802            1029 :         return iter;
        1803                 :
        1804            1101 :     if (!InitIteratorClass(cx, global) || !global->initGeneratorClass(cx))
        1805               0 :         return NULL;
        1806            1101 :     return InitStopIterationClass(cx, global);
        1807                 : }

Generated by: LCOV version 1.7