PageRenderTime 896ms CodeModel.GetById 54ms app.highlight 710ms RepoModel.GetById 44ms app.codeStats 2ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsiter.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1050 lines | 758 code | 131 blank | 161 comment | 169 complexity | 643f56208cf9fcbfd4c9348ef4603d00 MD5 | raw file
   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 "jsstddef.h"
  45#include <string.h>     /* for memcpy */
  46#include "jstypes.h"
  47#include "jsutil.h"
  48#include "jsarena.h"
  49#include "jsapi.h"
  50#include "jsarray.h"
  51#include "jsatom.h"
  52#include "jsbool.h"
  53#include "jscntxt.h"
  54#include "jsversion.h"
  55#include "jsexn.h"
  56#include "jsfun.h"
  57#include "jsgc.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 "jsscan.h"
  65#include "jsscope.h"
  66#include "jsscript.h"
  67
  68#if JS_HAS_XML_SUPPORT
  69#include "jsxml.h"
  70#endif
  71
  72#if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS
  73#error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
  74#endif
  75
  76#if JS_HAS_GENERATORS
  77
  78static JSBool
  79CloseGenerator(JSContext *cx, JSObject *genobj);
  80
  81#endif
  82
  83/*
  84 * Shared code to close iterator's state either through an explicit call or
  85 * when GC detects that the iterator is no longer reachable.
  86 */
  87void
  88js_CloseNativeIterator(JSContext *cx, JSObject *iterobj)
  89{
  90    jsval state;
  91    JSObject *iterable;
  92
  93    JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
  94
  95    /* Avoid double work if js_CloseNativeIterator was called on obj. */
  96    state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
  97    if (JSVAL_IS_NULL(state))
  98        return;
  99
 100    /* Protect against failure to fully initialize obj. */
 101    iterable = STOBJ_GET_PARENT(iterobj);
 102    if (iterable) {
 103#if JS_HAS_XML_SUPPORT
 104        uintN flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
 105        if ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, iterable)) {
 106            ((JSXMLObjectOps *) iterable->map->ops)->
 107                enumerateValues(cx, iterable, JSENUMERATE_DESTROY, &state,
 108                                NULL, NULL);
 109        } else
 110#endif
 111            OBJ_ENUMERATE(cx, iterable, JSENUMERATE_DESTROY, &state, NULL);
 112    }
 113    STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
 114}
 115
 116JSClass js_IteratorClass = {
 117    "Iterator",
 118    JSCLASS_HAS_RESERVED_SLOTS(2) | /* slots for state and flags */
 119    JSCLASS_HAS_CACHED_PROTO(JSProto_Iterator),
 120    JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
 121    JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
 122    JSCLASS_NO_OPTIONAL_MEMBERS
 123};
 124
 125static JSBool
 126InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags)
 127{
 128    jsval state;
 129    JSBool ok;
 130
 131    JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
 132
 133    /* Initialize iterobj in case of enumerate hook failure. */
 134    STOBJ_SET_PARENT(iterobj, obj);
 135    STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, JSVAL_NULL);
 136    STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_FLAGS, INT_TO_JSVAL(flags));
 137    if (!js_RegisterCloseableIterator(cx, iterobj))
 138        return JS_FALSE;
 139    if (!obj)
 140        return JS_TRUE;
 141
 142    ok =
 143#if JS_HAS_XML_SUPPORT
 144         ((flags & JSITER_FOREACH) && OBJECT_IS_XML(cx, obj))
 145         ? ((JSXMLObjectOps *) obj->map->ops)->
 146               enumerateValues(cx, obj, JSENUMERATE_INIT, &state, NULL, NULL)
 147         :
 148#endif
 149           OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL);
 150    if (!ok)
 151        return JS_FALSE;
 152
 153    STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
 154    if (flags & JSITER_ENUMERATE) {
 155        /*
 156         * The enumerating iterator needs the original object to suppress
 157         * enumeration of deleted or shadowed prototype properties. Since the
 158         * enumerator never escapes to scripts, we use the prototype slot to
 159         * store the original object.
 160         */
 161        JS_ASSERT(obj != iterobj);
 162        STOBJ_SET_PROTO(iterobj, obj);
 163    }
 164    return JS_TRUE;
 165}
 166
 167static JSBool
 168Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval)
 169{
 170    JSBool keyonly;
 171    uintN flags;
 172    JSObject *obj;
 173
 174    keyonly = js_ValueToBoolean(argv[1]);
 175    flags = keyonly ? 0 : JSITER_FOREACH;
 176
 177    if (cx->fp->flags & JSFRAME_CONSTRUCTING) {
 178        /* XXX work around old valueOf call hidden beneath js_ValueToObject */
 179        if (!JSVAL_IS_PRIMITIVE(argv[0])) {
 180            obj = JSVAL_TO_OBJECT(argv[0]);
 181        } else {
 182            obj = js_ValueToNonNullObject(cx, argv[0]);
 183            if (!obj)
 184                return JS_FALSE;
 185            argv[0] = OBJECT_TO_JSVAL(obj);
 186        }
 187        return InitNativeIterator(cx, iterobj, obj, flags);
 188    }
 189
 190    *rval = argv[0];
 191    return js_ValueToIterator(cx, flags, rval);
 192}
 193
 194static JSBool
 195NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval)
 196{
 197    jsval vec[2];
 198    JSTempValueRooter tvr;
 199    JSObject *aobj;
 200
 201    vec[0] = ID_TO_VALUE(key);
 202    vec[1] = val;
 203
 204    JS_PUSH_TEMP_ROOT(cx, 2, vec, &tvr);
 205    aobj = js_NewArrayObject(cx, 2, vec);
 206    *rval = OBJECT_TO_JSVAL(aobj);
 207    JS_POP_TEMP_ROOT(cx, &tvr);
 208
 209    return aobj != NULL;
 210}
 211
 212static JSBool
 213IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval)
 214{
 215    JSObject *iterable;
 216    jsval state;
 217    uintN flags;
 218    JSBool foreach, ok;
 219    jsid id;
 220
 221    JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass);
 222
 223    iterable = OBJ_GET_PARENT(cx, obj);
 224    JS_ASSERT(iterable);
 225    state = STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE);
 226    if (JSVAL_IS_NULL(state))
 227        goto stop;
 228
 229    flags = JSVAL_TO_INT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_FLAGS));
 230    JS_ASSERT(!(flags & JSITER_ENUMERATE));
 231    foreach = (flags & JSITER_FOREACH) != 0;
 232    ok =
 233#if JS_HAS_XML_SUPPORT
 234         (foreach && OBJECT_IS_XML(cx, iterable))
 235         ? ((JSXMLObjectOps *) iterable->map->ops)->
 236               enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state,
 237                               &id, rval)
 238         :
 239#endif
 240           OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id);
 241    if (!ok)
 242        return JS_FALSE;
 243
 244    STOBJ_SET_SLOT(obj, JSSLOT_ITER_STATE, state);
 245    if (JSVAL_IS_NULL(state))
 246        goto stop;
 247
 248    if (foreach) {
 249#if JS_HAS_XML_SUPPORT
 250        if (!OBJECT_IS_XML(cx, iterable) &&
 251            !OBJ_GET_PROPERTY(cx, iterable, id, rval)) {
 252            return JS_FALSE;
 253        }
 254#endif
 255        if (!NewKeyValuePair(cx, id, *rval, rval))
 256            return JS_FALSE;
 257    } else {
 258        *rval = ID_TO_VALUE(id);
 259    }
 260    return JS_TRUE;
 261
 262  stop:
 263    JS_ASSERT(STOBJ_GET_SLOT(obj, JSSLOT_ITER_STATE) == JSVAL_NULL);
 264    *rval = JSVAL_HOLE;
 265    return JS_TRUE;
 266}
 267
 268JSBool
 269js_ThrowStopIteration(JSContext *cx)
 270{
 271    jsval v;
 272
 273    JS_ASSERT(!JS_IsExceptionPending(cx));
 274    if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v))
 275        JS_SetPendingException(cx, v);
 276    return JS_FALSE;
 277}
 278
 279static JSBool
 280iterator_next(JSContext *cx, uintN argc, jsval *vp)
 281{
 282    JSObject *obj;
 283
 284    obj = JS_THIS_OBJECT(cx, vp);
 285    if (!JS_InstanceOf(cx, obj, &js_IteratorClass, vp + 2))
 286        return JS_FALSE;
 287
 288    if (!IteratorNextImpl(cx, obj, vp))
 289        return JS_FALSE;
 290
 291    if (*vp == JSVAL_HOLE) {
 292        *vp = JSVAL_NULL;
 293        js_ThrowStopIteration(cx);
 294        return JS_FALSE;
 295    }
 296    return JS_TRUE;
 297}
 298
 299static JSBool
 300iterator_self(JSContext *cx, uintN argc, jsval *vp)
 301{
 302    *vp = JS_THIS(cx, vp);
 303    return !JSVAL_IS_NULL(*vp);
 304}
 305
 306#define JSPROP_ROPERM   (JSPROP_READONLY | JSPROP_PERMANENT)
 307
 308static JSFunctionSpec iterator_methods[] = {
 309    JS_FN(js_iterator_str,  iterator_self,  0,JSPROP_ROPERM),
 310    JS_FN(js_next_str,      iterator_next,  0,JSPROP_ROPERM),
 311    JS_FS_END
 312};
 313
 314uintN
 315js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj)
 316{
 317    if (OBJ_GET_CLASS(cx, iterobj) != &js_IteratorClass)
 318        return 0;
 319    return JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
 320}
 321
 322/*
 323 * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists.
 324 * Otherwise construct the default iterator.
 325 */
 326JS_FRIEND_API(JSBool)
 327js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp)
 328{
 329    JSObject *obj;
 330    JSTempValueRooter tvr;
 331    JSAtom *atom;
 332    JSClass *clasp;
 333    JSExtendedClass *xclasp;
 334    JSBool ok;
 335    JSObject *iterobj;
 336    jsval arg;
 337
 338    JS_ASSERT(!(flags & ~(JSITER_ENUMERATE |
 339                          JSITER_FOREACH |
 340                          JSITER_KEYVALUE)));
 341
 342    /* JSITER_KEYVALUE must always come with JSITER_FOREACH */
 343    JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH));
 344
 345    /* XXX work around old valueOf call hidden beneath js_ValueToObject */
 346    if (!JSVAL_IS_PRIMITIVE(*vp)) {
 347        obj = JSVAL_TO_OBJECT(*vp);
 348    } else {
 349        /*
 350         * Enumerating over null and undefined gives an empty enumerator.
 351         * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of
 352         * the first production in 12.6.4 and step 4 of the second production,
 353         * but it's "web JS" compatible.
 354         */
 355        if ((flags & JSITER_ENUMERATE)) {
 356            if (!js_ValueToObject(cx, *vp, &obj))
 357                return JS_FALSE;
 358            if (!obj)
 359                goto default_iter;
 360        } else {
 361            obj = js_ValueToNonNullObject(cx, *vp);
 362            if (!obj)
 363                return JS_FALSE;
 364        }
 365    }
 366
 367    JS_ASSERT(obj);
 368    JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
 369
 370    clasp = OBJ_GET_CLASS(cx, obj);
 371    if ((clasp->flags & JSCLASS_IS_EXTENDED) &&
 372        (xclasp = (JSExtendedClass *) clasp)->iteratorObject) {
 373        iterobj = xclasp->iteratorObject(cx, obj, !(flags & JSITER_FOREACH));
 374        if (!iterobj)
 375            goto bad;
 376        *vp = OBJECT_TO_JSVAL(iterobj);
 377    } else {
 378        atom = cx->runtime->atomState.iteratorAtom;
 379#if JS_HAS_XML_SUPPORT
 380        if (OBJECT_IS_XML(cx, obj)) {
 381            if (!js_GetXMLFunction(cx, obj, ATOM_TO_JSID(atom), vp))
 382                goto bad;
 383        } else
 384#endif
 385        {
 386            if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), vp))
 387                goto bad;
 388        }
 389
 390        if (JSVAL_IS_VOID(*vp)) {
 391          default_iter:
 392            /*
 393             * Fail over to the default enumerating native iterator.
 394             *
 395             * Create iterobj with a NULL parent to ensure that we use the
 396             * correct scope chain to lookup the iterator's constructor. Since
 397             * we use the parent slot to keep track of the iterable, we must
 398             * fix it up after.
 399             */
 400            iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL, 0);
 401            if (!iterobj)
 402                goto bad;
 403
 404            /* Store in *vp to protect it from GC (callers must root vp). */
 405            *vp = OBJECT_TO_JSVAL(iterobj);
 406
 407            if (!InitNativeIterator(cx, iterobj, obj, flags))
 408                goto bad;
 409        } else {
 410            arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
 411            if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg,
 412                                   vp)) {
 413                goto bad;
 414            }
 415            if (JSVAL_IS_PRIMITIVE(*vp)) {
 416                const char *printable = js_AtomToPrintableString(cx, atom);
 417                if (printable) {
 418                    js_ReportValueError2(cx, JSMSG_BAD_ITERATOR_RETURN,
 419                                         JSDVG_SEARCH_STACK, *vp, NULL,
 420                                         printable);
 421                }
 422                goto bad;
 423            }
 424        }
 425    }
 426
 427    ok = JS_TRUE;
 428  out:
 429    if (obj)
 430        JS_POP_TEMP_ROOT(cx, &tvr);
 431    return ok;
 432  bad:
 433    ok = JS_FALSE;
 434    goto out;
 435}
 436
 437JS_FRIEND_API(JSBool) JS_FASTCALL
 438js_CloseIterator(JSContext *cx, jsval v)
 439{
 440    JSObject *obj;
 441    JSClass *clasp;
 442
 443    JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
 444    obj = JSVAL_TO_OBJECT(v);
 445    clasp = OBJ_GET_CLASS(cx, obj);
 446
 447    if (clasp == &js_IteratorClass) {
 448        js_CloseNativeIterator(cx, obj);
 449    }
 450#if JS_HAS_GENERATORS
 451    else if (clasp == &js_GeneratorClass) {
 452        if (!CloseGenerator(cx, obj))
 453            return JS_FALSE;
 454    }
 455#endif
 456    return JS_TRUE;
 457}
 458
 459static JSBool
 460CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval)
 461{
 462    JSObject *obj, *origobj;
 463    jsval state;
 464    JSBool foreach;
 465    jsid id;
 466    JSObject *obj2;
 467    JSBool cond;
 468    JSClass *clasp;
 469    JSExtendedClass *xclasp;
 470    JSProperty *prop;
 471    JSString *str;
 472
 473    JS_ASSERT(flags & JSITER_ENUMERATE);
 474    JS_ASSERT(STOBJ_GET_CLASS(iterobj) == &js_IteratorClass);
 475
 476    obj = STOBJ_GET_PARENT(iterobj);
 477    origobj = STOBJ_GET_PROTO(iterobj);
 478    state = STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE);
 479    if (JSVAL_IS_NULL(state))
 480        goto stop;
 481
 482    foreach = (flags & JSITER_FOREACH) != 0;
 483#if JS_HAS_XML_SUPPORT
 484    /*
 485     * Treat an XML object specially only when it starts the prototype chain.
 486     * Otherwise we need to do the usual deleted and shadowed property checks.
 487     */
 488    if (obj == origobj && OBJECT_IS_XML(cx, obj)) {
 489        if (foreach) {
 490            JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops;
 491
 492            if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state,
 493                                         &id, rval)) {
 494                return JS_FALSE;
 495            }
 496        } else {
 497            if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id))
 498                return JS_FALSE;
 499        }
 500        STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
 501        if (JSVAL_IS_NULL(state))
 502            goto stop;
 503    } else
 504#endif
 505    {
 506      restart:
 507        if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id))
 508            return JS_FALSE;
 509
 510        STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
 511        if (JSVAL_IS_NULL(state)) {
 512#if JS_HAS_XML_SUPPORT
 513            if (OBJECT_IS_XML(cx, obj)) {
 514                /*
 515                 * We just finished enumerating an XML obj that is present on
 516                 * the prototype chain of a non-XML origobj. Stop further
 517                 * prototype chain searches because XML objects don't
 518                 * enumerate prototypes.
 519                 */
 520                JS_ASSERT(origobj != obj);
 521                JS_ASSERT(!OBJECT_IS_XML(cx, origobj));
 522            } else
 523#endif
 524            {
 525                obj = OBJ_GET_PROTO(cx, obj);
 526                if (obj) {
 527                    STOBJ_SET_PARENT(iterobj, obj);
 528                    if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL))
 529                        return JS_FALSE;
 530                    STOBJ_SET_SLOT(iterobj, JSSLOT_ITER_STATE, state);
 531                    if (!JSVAL_IS_NULL(state))
 532                        goto restart;
 533                }
 534            }
 535            goto stop;
 536        }
 537
 538        /* Skip properties not in obj when looking from origobj. */
 539        if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop))
 540            return JS_FALSE;
 541        if (!prop)
 542            goto restart;
 543        OBJ_DROP_PROPERTY(cx, obj2, prop);
 544
 545        /*
 546         * If the id was found in a prototype object or an unrelated object
 547         * (specifically, not in an inner object for obj), skip it. This step
 548         * means that all OBJ_LOOKUP_PROPERTY implementations must return an
 549         * object further along on the prototype chain, or else possibly an
 550         * object returned by the JSExtendedClass.outerObject optional hook.
 551         */
 552        if (obj != obj2) {
 553            cond = JS_FALSE;
 554            clasp = OBJ_GET_CLASS(cx, obj2);
 555            if (clasp->flags & JSCLASS_IS_EXTENDED) {
 556                xclasp = (JSExtendedClass *) clasp;
 557                cond = xclasp->outerObject &&
 558                    xclasp->outerObject(cx, obj2) == obj;
 559            }
 560            if (!cond)
 561                goto restart;
 562        }
 563
 564        if (foreach) {
 565            /* Get property querying the original object. */
 566            if (!OBJ_GET_PROPERTY(cx, origobj, id, rval))
 567                return JS_FALSE;
 568        }
 569    }
 570
 571    if (foreach) {
 572        if (flags & JSITER_KEYVALUE) {
 573            if (!NewKeyValuePair(cx, id, *rval, rval))
 574                return JS_FALSE;
 575        }
 576    } else {
 577        /* Make rval a string for uniformity and compatibility. */
 578        str = js_ValueToString(cx, ID_TO_VALUE(id));
 579        if (!str)
 580            return JS_FALSE;
 581        *rval = STRING_TO_JSVAL(str);
 582    }
 583    return JS_TRUE;
 584
 585  stop:
 586    JS_ASSERT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_STATE) == JSVAL_NULL);
 587    *rval = JSVAL_HOLE;
 588    return JS_TRUE;
 589}
 590
 591JS_FRIEND_API(JSBool)
 592js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval)
 593{
 594    uintN flags;
 595
 596    /* Fast path for native iterators */
 597    if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) {
 598        flags = JSVAL_TO_INT(STOBJ_GET_SLOT(iterobj, JSSLOT_ITER_FLAGS));
 599        if (flags & JSITER_ENUMERATE)
 600            return CallEnumeratorNext(cx, iterobj, flags, rval);
 601
 602        /*
 603         * Call next directly as all the methods of the native iterator are
 604         * read-only and permanent.
 605         */
 606        if (!IteratorNextImpl(cx, iterobj, rval))
 607            return JS_FALSE;
 608    } else {
 609        jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom);
 610
 611        if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval))
 612            return JS_FALSE;
 613        if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) {
 614            /* Check for StopIteration. */
 615            if (!cx->throwing || !js_ValueIsStopIteration(cx->exception))
 616                return JS_FALSE;
 617
 618            /* Inline JS_ClearPendingException(cx). */
 619            cx->throwing = JS_FALSE;
 620            cx->exception = JSVAL_VOID;
 621            *rval = JSVAL_HOLE;
 622            return JS_TRUE;
 623        }
 624    }
 625
 626    return JS_TRUE;
 627}
 628
 629static JSBool
 630stopiter_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
 631{
 632    *bp = js_ValueIsStopIteration(v);
 633    return JS_TRUE;
 634}
 635
 636JSClass js_StopIterationClass = {
 637    js_StopIteration_str,
 638    JSCLASS_HAS_CACHED_PROTO(JSProto_StopIteration),
 639    JS_PropertyStub,  JS_PropertyStub,
 640    JS_PropertyStub,  JS_PropertyStub,
 641    JS_EnumerateStub, JS_ResolveStub,
 642    JS_ConvertStub,   JS_FinalizeStub,
 643    NULL,             NULL,
 644    NULL,             NULL,
 645    NULL,             stopiter_hasInstance,
 646    NULL,             NULL
 647};
 648
 649#if JS_HAS_GENERATORS
 650
 651static void
 652generator_finalize(JSContext *cx, JSObject *obj)
 653{
 654    JSGenerator *gen;
 655
 656    gen = (JSGenerator *) JS_GetPrivate(cx, obj);
 657    if (gen) {
 658        /*
 659         * gen can be open on shutdown when close hooks are ignored or when
 660         * the embedding cancels scheduled close hooks.
 661         */
 662        JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_CLOSED ||
 663                  gen->state == JSGEN_OPEN);
 664        JS_free(cx, gen);
 665    }
 666}
 667
 668static void
 669generator_trace(JSTracer *trc, JSObject *obj)
 670{
 671    JSGenerator *gen;
 672
 673    gen = (JSGenerator *) JS_GetPrivate(trc->context, obj);
 674    if (!gen)
 675        return;
 676
 677    /*
 678     * js_TraceStackFrame does not recursively trace the down-linked frame
 679     * chain, so we insist that gen->frame has no parent to trace when the
 680     * generator is not running.
 681     */
 682    JS_ASSERT_IF(gen->state != JSGEN_RUNNING && gen->state != JSGEN_CLOSING,
 683                 !gen->frame.down);
 684
 685    /*
 686     * FIXME be 390950. Generator's frame is a part of the JS stack when the
 687     * generator is running or closing. Thus tracing the frame in this case
 688     * here duplicates the work done in js_TraceContext.
 689     */
 690    js_TraceStackFrame(trc, &gen->frame);
 691}
 692
 693JSClass js_GeneratorClass = {
 694    js_Generator_str,
 695    JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS |
 696    JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator),
 697    JS_PropertyStub,  JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
 698    JS_EnumerateStub, JS_ResolveStub,  JS_ConvertStub,  generator_finalize,
 699    NULL,             NULL,            NULL,            NULL,
 700    NULL,             NULL,            JS_CLASS_TRACE(generator_trace), NULL
 701};
 702
 703/*
 704 * Called from the JSOP_GENERATOR case in the interpreter, with fp referring
 705 * to the frame by which the generator function was activated.  Create a new
 706 * JSGenerator object, which contains its own JSStackFrame that we populate
 707 * from *fp.  We know that upon return, the JSOP_GENERATOR opcode will return
 708 * from the activation in fp, so we can steal away fp->callobj and fp->argsobj
 709 * if they are non-null.
 710 */
 711JSObject *
 712js_NewGenerator(JSContext *cx, JSStackFrame *fp)
 713{
 714    JSObject *obj;
 715    uintN argc, nargs, nslots;
 716    JSGenerator *gen;
 717    jsval *slots;
 718
 719    /* After the following return, failing control flow must goto bad. */
 720    obj = js_NewObject(cx, &js_GeneratorClass, NULL, NULL, 0);
 721    if (!obj)
 722        return NULL;
 723
 724    /* Load and compute stack slot counts. */
 725    argc = fp->argc;
 726    nargs = JS_MAX(argc, fp->fun->nargs);
 727    nslots = 2 + nargs + fp->script->nslots;
 728
 729    /* Allocate obj's private data struct. */
 730    gen = (JSGenerator *)
 731          JS_malloc(cx, sizeof(JSGenerator) + (nslots - 1) * sizeof(jsval));
 732    if (!gen)
 733        goto bad;
 734
 735    gen->obj = obj;
 736
 737    /* Steal away objects reflecting fp and point them at gen->frame. */
 738    gen->frame.callobj = fp->callobj;
 739    if (fp->callobj) {
 740        JS_SetPrivate(cx, fp->callobj, &gen->frame);
 741        fp->callobj = NULL;
 742    }
 743    gen->frame.argsobj = fp->argsobj;
 744    if (fp->argsobj) {
 745        JS_SetPrivate(cx, fp->argsobj, &gen->frame);
 746        fp->argsobj = NULL;
 747    }
 748
 749    /* These two references can be shared with fp until it goes away. */
 750    gen->frame.varobj = fp->varobj;
 751    gen->frame.thisp = fp->thisp;
 752
 753    /* Copy call-invariant script and function references. */
 754    gen->frame.script = fp->script;
 755    gen->frame.callee = fp->callee;
 756    gen->frame.fun = fp->fun;
 757
 758    /* Use slots to carve space out of gen->slots. */
 759    slots = gen->slots;
 760    gen->arena.next = NULL;
 761    gen->arena.base = (jsuword) slots;
 762    gen->arena.limit = gen->arena.avail = (jsuword) (slots + nslots);
 763
 764    /* Copy rval, argv and vars. */
 765    gen->frame.rval = fp->rval;
 766    memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval));
 767    gen->frame.argc = nargs;
 768    gen->frame.argv = slots + 2;
 769    slots += 2 + nargs;
 770    memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval));
 771
 772    /* Initialize or copy virtual machine state. */
 773    gen->frame.down = NULL;
 774    gen->frame.annotation = NULL;
 775    gen->frame.scopeChain = fp->scopeChain;
 776
 777    gen->frame.imacpc = NULL;
 778    gen->frame.slots = slots;
 779    JS_ASSERT(StackBase(fp) == fp->regs->sp);
 780    gen->savedRegs.sp = slots + fp->script->nfixed;
 781    gen->savedRegs.pc = fp->regs->pc;
 782    gen->frame.regs = &gen->savedRegs;
 783
 784    /* Copy remaining state (XXX sharp* and xml* should be local vars). */
 785    gen->frame.sharpDepth = 0;
 786    gen->frame.sharpArray = NULL;
 787    gen->frame.flags = (fp->flags & ~JSFRAME_ROOTED_ARGV) | JSFRAME_GENERATOR;
 788    gen->frame.dormantNext = NULL;
 789    gen->frame.xmlNamespace = NULL;
 790    gen->frame.blockChain = NULL;
 791
 792    /* Note that gen is newborn. */
 793    gen->state = JSGEN_NEWBORN;
 794
 795    if (!JS_SetPrivate(cx, obj, gen)) {
 796        JS_free(cx, gen);
 797        goto bad;
 798    }
 799    return obj;
 800
 801  bad:
 802    cx->weakRoots.newborn[GCX_OBJECT] = NULL;
 803    return NULL;
 804}
 805
 806typedef enum JSGeneratorOp {
 807    JSGENOP_NEXT,
 808    JSGENOP_SEND,
 809    JSGENOP_THROW,
 810    JSGENOP_CLOSE
 811} JSGeneratorOp;
 812
 813/*
 814 * Start newborn or restart yielding generator and perform the requested
 815 * operation inside its frame.
 816 */
 817static JSBool
 818SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
 819                JSGenerator *gen, jsval arg)
 820{
 821    JSStackFrame *fp;
 822    JSArena *arena;
 823    JSBool ok;
 824
 825    if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
 826        js_ReportValueError(cx, JSMSG_NESTING_GENERATOR,
 827                            JSDVG_SEARCH_STACK, OBJECT_TO_JSVAL(obj),
 828                            JS_GetFunctionId(gen->frame.fun));
 829        return JS_FALSE;
 830    }
 831
 832    JS_ASSERT(gen->state ==  JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
 833    switch (op) {
 834      case JSGENOP_NEXT:
 835      case JSGENOP_SEND:
 836        if (gen->state == JSGEN_OPEN) {
 837            /*
 838             * Store the argument to send as the result of the yield
 839             * expression.
 840             */
 841            gen->savedRegs.sp[-1] = arg;
 842        }
 843        gen->state = JSGEN_RUNNING;
 844        break;
 845
 846      case JSGENOP_THROW:
 847        JS_SetPendingException(cx, arg);
 848        gen->state = JSGEN_RUNNING;
 849        break;
 850
 851      default:
 852        JS_ASSERT(op == JSGENOP_CLOSE);
 853        JS_SetPendingException(cx, JSVAL_ARETURN);
 854        gen->state = JSGEN_CLOSING;
 855        break;
 856    }
 857
 858    /* Extend the current stack pool with gen->arena. */
 859    arena = cx->stackPool.current;
 860    JS_ASSERT(!arena->next);
 861    JS_ASSERT(!gen->arena.next);
 862    JS_ASSERT(cx->stackPool.current != &gen->arena);
 863    cx->stackPool.current = arena->next = &gen->arena;
 864
 865    /* Push gen->frame around the interpreter activation. */
 866    fp = cx->fp;
 867    cx->fp = &gen->frame;
 868    gen->frame.down = fp;
 869    ok = js_Interpret(cx);
 870    cx->fp = fp;
 871    gen->frame.down = NULL;
 872
 873    /* Retract the stack pool and sanitize gen->arena. */
 874    JS_ASSERT(!gen->arena.next);
 875    JS_ASSERT(arena->next == &gen->arena);
 876    JS_ASSERT(cx->stackPool.current == &gen->arena);
 877    cx->stackPool.current = arena;
 878    arena->next = NULL;
 879
 880    if (gen->frame.flags & JSFRAME_YIELDING) {
 881        /* Yield cannot fail, throw or be called on closing. */
 882        JS_ASSERT(ok);
 883        JS_ASSERT(!cx->throwing);
 884        JS_ASSERT(gen->state == JSGEN_RUNNING);
 885        JS_ASSERT(op != JSGENOP_CLOSE);
 886        gen->frame.flags &= ~JSFRAME_YIELDING;
 887        gen->state = JSGEN_OPEN;
 888        return JS_TRUE;
 889    }
 890
 891    gen->frame.rval = JSVAL_VOID;
 892    gen->state = JSGEN_CLOSED;
 893    if (ok) {
 894        /* Returned, explicitly or by falling off the end. */
 895        if (op == JSGENOP_CLOSE)
 896            return JS_TRUE;
 897        return js_ThrowStopIteration(cx);
 898    }
 899
 900    /*
 901     * An error, silent termination by operation callback or an exception.
 902     * Propagate the condition to the caller.
 903     */
 904    return JS_FALSE;
 905}
 906
 907static JSBool
 908CloseGenerator(JSContext *cx, JSObject *obj)
 909{
 910    JSGenerator *gen;
 911
 912    JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
 913    gen = (JSGenerator *) JS_GetPrivate(cx, obj);
 914    if (!gen) {
 915        /* Generator prototype object. */
 916        return JS_TRUE;
 917    }
 918
 919    if (gen->state == JSGEN_CLOSED)
 920        return JS_TRUE;
 921
 922    return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, JSVAL_VOID);
 923}
 924
 925/*
 926 * Common subroutine of generator_(next|send|throw|close) methods.
 927 */
 928static JSBool
 929generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc)
 930{
 931    JSObject *obj;
 932    JSGenerator *gen;
 933    jsval arg;
 934
 935    obj = JS_THIS_OBJECT(cx, vp);
 936    if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
 937        return JS_FALSE;
 938
 939    gen = (JSGenerator *) JS_GetPrivate(cx, obj);
 940    if (gen == NULL) {
 941        /* This happens when obj is the generator prototype. See bug 352885. */
 942        goto closed_generator;
 943    }
 944
 945    if (gen->state == JSGEN_NEWBORN) {
 946        switch (op) {
 947          case JSGENOP_NEXT:
 948          case JSGENOP_THROW:
 949            break;
 950
 951          case JSGENOP_SEND:
 952            if (argc >= 1 && !JSVAL_IS_VOID(vp[2])) {
 953                js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
 954                                    JSDVG_SEARCH_STACK, vp[2], NULL);
 955                return JS_FALSE;
 956            }
 957            break;
 958
 959          default:
 960            JS_ASSERT(op == JSGENOP_CLOSE);
 961            gen->state = JSGEN_CLOSED;
 962            return JS_TRUE;
 963        }
 964    } else if (gen->state == JSGEN_CLOSED) {
 965      closed_generator:
 966        switch (op) {
 967          case JSGENOP_NEXT:
 968          case JSGENOP_SEND:
 969            return js_ThrowStopIteration(cx);
 970          case JSGENOP_THROW:
 971            JS_SetPendingException(cx, argc >= 1 ? vp[2] : JSVAL_VOID);
 972            return JS_FALSE;
 973          default:
 974            JS_ASSERT(op == JSGENOP_CLOSE);
 975            return JS_TRUE;
 976        }
 977    }
 978
 979    arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0)
 980          ? vp[2]
 981          : JSVAL_VOID;
 982    if (!SendToGenerator(cx, op, obj, gen, arg))
 983        return JS_FALSE;
 984    *vp = gen->frame.rval;
 985    return JS_TRUE;
 986}
 987
 988static JSBool
 989generator_send(JSContext *cx, uintN argc, jsval *vp)
 990{
 991    return generator_op(cx, JSGENOP_SEND, vp, argc);
 992}
 993
 994static JSBool
 995generator_next(JSContext *cx, uintN argc, jsval *vp)
 996{
 997    return generator_op(cx, JSGENOP_NEXT, vp, argc);
 998}
 999
1000static JSBool
1001generator_throw(JSContext *cx, uintN argc, jsval *vp)
1002{
1003    return generator_op(cx, JSGENOP_THROW, vp, argc);
1004}
1005
1006static JSBool
1007generator_close(JSContext *cx, uintN argc, jsval *vp)
1008{
1009    return generator_op(cx, JSGENOP_CLOSE, vp, argc);
1010}
1011
1012static JSFunctionSpec generator_methods[] = {
1013    JS_FN(js_iterator_str,  iterator_self,      0,JSPROP_ROPERM),
1014    JS_FN(js_next_str,      generator_next,     0,JSPROP_ROPERM),
1015    JS_FN(js_send_str,      generator_send,     1,JSPROP_ROPERM),
1016    JS_FN(js_throw_str,     generator_throw,    1,JSPROP_ROPERM),
1017    JS_FN(js_close_str,     generator_close,    0,JSPROP_ROPERM),
1018    JS_FS_END
1019};
1020
1021#endif /* JS_HAS_GENERATORS */
1022
1023JSObject *
1024js_InitIteratorClasses(JSContext *cx, JSObject *obj)
1025{
1026    JSObject *proto, *stop;
1027
1028    /* Idempotency required: we initialize several things, possibly lazily. */
1029    if (!js_GetClassObject(cx, obj, JSProto_StopIteration, &stop))
1030        return NULL;
1031    if (stop)
1032        return stop;
1033
1034    proto = JS_InitClass(cx, obj, NULL, &js_IteratorClass, Iterator, 2,
1035                         NULL, iterator_methods, NULL, NULL);
1036    if (!proto)
1037        return NULL;
1038    STOBJ_SET_SLOT(proto, JSSLOT_ITER_STATE, JSVAL_NULL);
1039
1040#if JS_HAS_GENERATORS
1041    /* Initialize the generator internals if configured. */
1042    if (!JS_InitClass(cx, obj, NULL, &js_GeneratorClass, NULL, 0,
1043                      NULL, generator_methods, NULL, NULL)) {
1044        return NULL;
1045    }
1046#endif
1047
1048    return JS_InitClass(cx, obj, NULL, &js_StopIterationClass, NULL, 0,
1049                        NULL, NULL, NULL, NULL);
1050}