Clean up for-in ops and naming nit (443039, r=igor).
authorBrendan Eich <brendan@mozilla.org>
Tue, 01 Jul 2008 18:59:18 -0700
changeset 15613 bd3e62a095a79984800f4003404741e0eb8088e2
parent 15612 e4fea8e9d036f3f8c68143ee52d8eaf5be09770d
child 15614 a8266dce5794627af4727eb4cf274bdda3c62718
push idunknown
push userunknown
push dateunknown
reviewersigor
bugs443039
milestone1.9.1a1pre
Clean up for-in ops and naming nit (443039, r=igor).
js/src/jscntxt.h
js/src/jsemit.cpp
js/src/jsgc.cpp
js/src/jsinterp.cpp
js/src/jsiter.h
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsopcode.cpp
js/src/jsopcode.tbl
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jsprvtd.h
js/src/jsxdrapi.h
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -160,19 +160,24 @@ typedef enum JSRuntimeState {
 } JSRuntimeState;
 
 typedef struct JSPropertyTreeEntry {
     JSDHashEntryHdr     hdr;
     JSScopeProperty     *child;
 } JSPropertyTreeEntry;
 
 /*
- * Forward declaration for opaque JSRuntime.nativeIteratorStates.
+ * Private type used to enumerate properties of a native JS object.
  */
-typedef struct JSNativeIteratorState JSNativeIteratorState;
+struct JSNativeEnumerator {
+    jsint               next_index;     /* index into jsid array */
+    JSIdArray           *ida;           /* all property ids in enumeration */
+    JSNativeEnumerator  *next;          /* double-linked list support */
+    JSNativeEnumerator  **prevp;
+};
 
 typedef struct JSSetSlotRequest JSSetSlotRequest;
 
 struct JSSetSlotRequest {
     JSObject            *obj;           /* object containing slot to set */
     JSObject            *pobj;          /* new proto or parent reference */
     uint16              slot;           /* which to set, proto or parent */
     uint16              errnum;         /* JSMSG_NO_ERROR or error result */
@@ -375,19 +380,19 @@ struct JSRuntime {
      * the object graph usually associated with a JSContext's global object,
      * including the set of standard class objects.  See jsxml.c for details.
      */
     JSObject            *anynameObject;
     JSObject            *functionNamespaceObject;
 
     /*
      * A helper list for the GC, so it can mark native iterator states. See
-     * js_TraceNativeIteratorStates for details.
+     * js_TraceNativeEnumerators for details.
      */
-    JSNativeIteratorState *nativeIteratorStates;
+    JSNativeEnumerator  *nativeEnumerators;
 
 #ifndef JS_THREADSAFE
     /*
      * For thread-unsafe embeddings, the GSN cache lives in the runtime and
      * not each context, since we expect it to be filled once when decompiling
      * a longer script, then hit repeatedly as js_GetSrcNote is called during
      * the decompiler activation that filled it.
      */
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -4298,24 +4298,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
             if (!js_EmitTree(cx, cg, pn2->pn_right))
                 return JS_FALSE;
 
             /*
              * Emit a bytecode to convert top of stack value to the iterator
              * object depending on the loop variant (for-in, for-each-in, or
              * destructuring for-in).
              */
-#if JS_HAS_DESTRUCTURING
-            JS_ASSERT(pn->pn_op == JSOP_FORIN ||
-                      pn->pn_op == JSOP_FOREACHKEYVAL ||
-                      pn->pn_op == JSOP_FOREACH);
-#else
-            JS_ASSERT(pn->pn_op == JSOP_FORIN || pn->pn_op == JSOP_FOREACH);
-#endif
-            if (js_Emit1(cx, cg, PN_OP(pn)) < 0)
+            JS_ASSERT(pn->pn_op == JSOP_ITER);
+            if (js_Emit2(cx, cg, PN_OP(pn), (uint8) pn->pn_iflags) < 0)
                 return JS_FALSE;
 
             top = CG_OFFSET(cg);
             SET_STATEMENT_TOP(&stmtInfo, top);
 
             /*
              * Compile a JSOP_FOR* bytecode based on the left hand side.
              *
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2860,17 +2860,17 @@ js_TraceRuntime(JSTracer *trc, JSBool al
 {
     JSRuntime *rt = trc->context->runtime;
     JSContext *iter, *acx;
 
     JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_traversal, trc);
     if (rt->gcLocksHash)
         JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc);
     js_TraceAtomState(trc, allAtoms);
-    js_TraceNativeIteratorStates(trc);
+    js_TraceNativeEnumerators(trc);
     js_TraceRuntimeNumberState(trc);
 
     iter = NULL;
     while ((acx = js_ContextIterator(rt, JS_TRUE, &iter)) != NULL)
         js_TraceContext(trc, acx);
 
     if (rt->gcExtraRootsTraceOp)
         rt->gcExtraRootsTraceOp(trc, rt->gcExtraRootsData);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2550,17 +2550,16 @@ js_Interpret(JSContext *cx)
     jsbytecode *endpc, *pc2;
     JSOp op, op2;
     jsatomid index;
     JSAtom *atom;
     uintN argc, attrs, flags;
     uint32 slot;
     jsval *vp, lval, rval, ltmp, rtmp;
     jsid id;
-    JSObject *iterobj;
     JSProperty *prop;
     JSScopeProperty *sprop;
     JSString *str, *str2;
     jsint i, j;
     jsdouble d, d2;
     JSClass *clasp;
     JSFunction *fun;
     JSType type;
@@ -3142,42 +3141,23 @@ js_Interpret(JSContext *cx)
             if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
                 goto error;
             regs.sp--;
             STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL));
             if (prop)
                 OBJ_DROP_PROPERTY(cx, obj2, prop);
           END_CASE(JSOP_IN)
 
-          BEGIN_CASE(JSOP_FOREACH)
-            flags = JSITER_ENUMERATE | JSITER_FOREACH;
-            goto value_to_iter;
-
-#if JS_HAS_DESTRUCTURING
-          BEGIN_CASE(JSOP_FOREACHKEYVAL)
-            flags = JSITER_ENUMERATE | JSITER_FOREACH | JSITER_KEYVALUE;
-            goto value_to_iter;
-#endif
-
-          BEGIN_CASE(JSOP_FORIN)
-            /*
-             * Set JSITER_ENUMERATE to indicate that for-in loop should use
-             * the enumeration protocol's iterator for compatibility if an
-             * explicit iterator is not given via the optional __iterator__
-             * method.
-             */
-            flags = JSITER_ENUMERATE;
-
-          value_to_iter:
+          BEGIN_CASE(JSOP_ITER)
+            flags = regs.pc[1];
             JS_ASSERT(regs.sp > fp->spbase);
             if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
                 goto error;
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
-            JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length);
-          END_CASE(JSOP_FORIN)
+          END_CASE(JSOP_ITER)
 
           BEGIN_CASE(JSOP_FORPROP)
             /*
              * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop
              * is not paid for the more common cases.
              */
             LOAD_ATOM(0);
             id = ATOM_TO_JSID(atom);
@@ -3211,19 +3191,17 @@ js_Interpret(JSContext *cx)
             i = -1;
 
           do_forinloop:
             /*
              * Reach under the top of stack to find our property iterator, a
              * JSObject that contains the iteration state.
              */
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[i]));
-            iterobj = JSVAL_TO_OBJECT(regs.sp[i]);
-
-            if (!js_CallIteratorNext(cx, iterobj, &rval))
+            if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[i]), &rval))
                 goto error;
             if (rval == JSVAL_HOLE) {
                 rval = JSVAL_FALSE;
                 goto end_forinloop;
             }
 
             switch (op) {
               case JSOP_FORARG:
@@ -6801,17 +6779,16 @@ js_Interpret(JSContext *cx)
 
 # if !JS_HAS_GENERATORS
           L_JSOP_GENERATOR:
           L_JSOP_YIELD:
           L_JSOP_ARRAYPUSH:
 # endif
 
 # if !JS_HAS_DESTRUCTURING
-          L_JSOP_FOREACHKEYVAL:
           L_JSOP_ENUMCONSTELEM:
 # endif
 
 # if !JS_HAS_XML_SUPPORT
           L_JSOP_CALLXMLNAME:
           L_JSOP_STARTXMLEXPR:
           L_JSOP_STARTXML:
           L_JSOP_DELDESC:
@@ -6836,16 +6813,19 @@ js_Interpret(JSContext *cx)
           L_JSOP_TOATTRNAME:
           L_JSOP_QNAME:
           L_JSOP_QNAMECONST:
           L_JSOP_QNAMEPART:
           L_JSOP_ANYNAME:
           L_JSOP_DEFXMLNS:
 # endif
 
+          L_JSOP_UNUSED186:
+          L_JSOP_UNUSED213:
+
 #else /* !JS_THREADED_INTERP */
           default:
 #endif
           {
             char numBuf[12];
             JS_snprintf(numBuf, sizeof numBuf, "%d", op);
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_BAD_BYTECODE, numBuf);
--- a/js/src/jsiter.h
+++ b/js/src/jsiter.h
@@ -43,16 +43,21 @@
 /*
  * JavaScript iterators.
  */
 #include "jsprvtd.h"
 #include "jspubtd.h"
 
 JS_BEGIN_EXTERN_C
 
+/*
+ * NB: these flag bits are encoded into the bytecode stream in the immediate
+ * operand of JSOP_ITER, so don't change them without advancing jsxdrapi.h's
+ * JSXDR_BYTECODE_VERSION.
+ */
 #define JSITER_ENUMERATE  0x1   /* for-in compatible hidden default iterator */
 #define JSITER_FOREACH    0x2   /* return [key, value] pair rather than key */
 #define JSITER_KEYVALUE   0x4   /* destructuring for-in wants [key, value] */
 
 /*
  * Convert the value stored in *vp to its iteration object. The flags should
  * contain JSITER_ENUMERATE if js_ValueToIterator is called when enumerating
  * for-in semantics are required, and when the caller can guarantee that the
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4147,24 +4147,16 @@ js_SetIdArrayLength(JSContext *cx, JSIdA
            JS_realloc(cx, ida, sizeof(JSIdArray) + (length-1) * sizeof(jsval));
     if (!rida)
         JS_DestroyIdArray(cx, ida);
     else
         rida->length = length;
     return rida;
 }
 
-/* Private type used to iterate over all properties of a native JS object */
-struct JSNativeIteratorState {
-    jsint                   next_index; /* index into jsid array */
-    JSIdArray               *ida;       /* all property ids in enumeration */
-    JSNativeIteratorState   *next;      /* double-linked list support */
-    JSNativeIteratorState   **prevp;
-};
-
 /*
  * This function is used to enumerate the properties of native JSObjects
  * and those host objects that do not define a JSNewEnumerateOp-style iterator
  * function.
  */
 JSBool
 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
              jsval *statep, jsid *idp)
@@ -4172,17 +4164,17 @@ js_Enumerate(JSContext *cx, JSObject *ob
     JSRuntime *rt;
     JSObject *proto;
     JSClass *clasp;
     JSEnumerateOp enumerate;
     JSScopeProperty *sprop, *lastProp;
     jsint i, length;
     JSScope *scope;
     JSIdArray *ida;
-    JSNativeIteratorState *state;
+    JSNativeEnumerator *state;
 
     rt = cx->runtime;
     clasp = OBJ_GET_CLASS(cx, obj);
     enumerate = clasp->enumerate;
     if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
         JS_ASSERT(enumerate != JS_EnumerateStub);
         return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
     }
@@ -4237,53 +4229,52 @@ js_Enumerate(JSContext *cx, JSObject *ob
                      SCOPE_HAS_PROPERTY(scope, sprop))) {
                     JS_ASSERT(i > 0);
                     ida->vector[--i] = sprop->id;
                 }
             }
         }
         JS_UNLOCK_OBJ(cx, obj);
 
-        state = (JSNativeIteratorState *)
-            JS_malloc(cx, sizeof(JSNativeIteratorState));
+        state = (JSNativeEnumerator *)JS_malloc(cx, sizeof(JSNativeEnumerator));
         if (!state) {
             JS_DestroyIdArray(cx, ida);
             return JS_FALSE;
         }
         state->ida = ida;
         state->next_index = 0;
 
         JS_LOCK_RUNTIME(rt);
-        state->next = rt->nativeIteratorStates;
+        state->next = rt->nativeEnumerators;
         if (state->next)
             state->next->prevp = &state->next;
-        state->prevp = &rt->nativeIteratorStates;
+        state->prevp = &rt->nativeEnumerators;
         *state->prevp = state;
         JS_UNLOCK_RUNTIME(rt);
 
         *statep = PRIVATE_TO_JSVAL(state);
         if (idp)
             *idp = INT_TO_JSVAL(length);
         break;
 
       case JSENUMERATE_NEXT:
-        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
+        state = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep);
         ida = state->ida;
         length = ida->length;
         if (state->next_index != length) {
             *idp = ida->vector[state->next_index++];
             break;
         }
         /* FALL THROUGH */
 
       case JSENUMERATE_DESTROY:
-        state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
+        state = (JSNativeEnumerator *) JSVAL_TO_PRIVATE(*statep);
 
         JS_LOCK_RUNTIME(rt);
-        JS_ASSERT(rt->nativeIteratorStates);
+        JS_ASSERT(rt->nativeEnumerators);
         JS_ASSERT(*state->prevp == state);
         if (state->next) {
             JS_ASSERT(state->next->prevp == &state->next);
             state->next->prevp = state->prevp;
         }
         *state->prevp = state->next;
         JS_UNLOCK_RUNTIME(rt);
 
@@ -4291,22 +4282,22 @@ js_Enumerate(JSContext *cx, JSObject *ob
         JS_free(cx, state);
         *statep = JSVAL_NULL;
         break;
     }
     return JS_TRUE;
 }
 
 void
-js_TraceNativeIteratorStates(JSTracer *trc)
+js_TraceNativeEnumerators(JSTracer *trc)
 {
-    JSNativeIteratorState *state;
+    JSNativeEnumerator *state;
     jsid *cursor, *end, id;
 
-    state = trc->context->runtime->nativeIteratorStates;
+    state = trc->context->runtime->nativeEnumerators;
     if (!state)
         return;
 
     do {
         JS_ASSERT(*state->prevp == state);
         cursor = state->ida->vector;
         end = cursor + state->ida->length;
         for (; cursor != end; ++cursor) {
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -616,17 +616,17 @@ js_NewIdArray(JSContext *cx, jsint lengt
 extern JSIdArray *
 js_SetIdArrayLength(JSContext *cx, JSIdArray *ida, jsint length);
 
 extern JSBool
 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
              jsval *statep, jsid *idp);
 
 extern void
-js_TraceNativeIteratorStates(JSTracer *trc);
+js_TraceNativeEnumerators(JSTracer *trc);
 
 extern JSBool
 js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
                jsval *vp, uintN *attrsp);
 
 extern JSBool
 js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -57,17 +57,17 @@
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsconfig.h"
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
-#include "jslock.h"
+#include "jsiter.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsregexp.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 
@@ -4523,18 +4523,19 @@ Decompile(SprintStack *ss, jsbytecode *p
                 break;
 
               case JSOP_TOXMLLIST:
                 op = JSOP_NOP;           /* turn off parens */
                 todo = Sprint(&ss->sprinter, "<>%s</>", POP_STR());
                 inXML = JS_FALSE;
                 break;
 
-              case JSOP_FOREACH:
-                foreach = JS_TRUE;
+              case JSOP_ITER:
+                foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) ==
+                          JSITER_FOREACH;
                 todo = -2;
                 break;
 
               case JSOP_TOXML:
               case JSOP_CALLXMLNAME:
               case JSOP_XMLNAME:
               case JSOP_FILTER:
                 /* These ops indicate the end of XML expressions. */
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -229,19 +229,20 @@ OPDEF(JSOP_INCVAR,    96, "incvar",     
 OPDEF(JSOP_DECARG,    97, "decarg",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_DEC)
 OPDEF(JSOP_DECVAR,    98, "decvar",     NULL,         3,  0,  1, 15,  JOF_QVAR |JOF_NAME|JOF_DEC)
 OPDEF(JSOP_ARGINC,    99, "arginc",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_INC|JOF_POST)
 OPDEF(JSOP_VARINC,    100,"varinc",     NULL,         3,  0,  1, 15,  JOF_QVAR |JOF_NAME|JOF_INC|JOF_POST)
 OPDEF(JSOP_ARGDEC,    101,"argdec",     NULL,         3,  0,  1, 15,  JOF_QARG |JOF_NAME|JOF_DEC|JOF_POST)
 OPDEF(JSOP_VARDEC,    102,"vardec",     NULL,         3,  0,  1, 15,  JOF_QVAR |JOF_NAME|JOF_DEC|JOF_POST)
 
 /*
- * Initialize for-in iterator. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL.
+ * Initialize for-in iterator using the JSITER_* flag bits in this op's uint8
+ * immediate operand. See also JSOP_ENDITER.
  */
-OPDEF(JSOP_FORIN,     103,"forin",      NULL,         1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_ITER,      103,"iter",       NULL,         2,  1,  1,  0,  JOF_2BYTE)
 
 /* ECMA-compliant for/in ops. */
 OPDEF(JSOP_FORNAME,   104,"forname",    NULL,         3,  0,  1, 19,  JOF_ATOM|JOF_NAME|JOF_FOR)
 OPDEF(JSOP_FORPROP,   105,"forprop",    NULL,         3,  1,  1, 18,  JOF_ATOM|JOF_PROP|JOF_FOR)
 OPDEF(JSOP_FORELEM,   106,"forelem",    NULL,         1,  1,  3, 18,  JOF_BYTE |JOF_ELEM|JOF_FOR)
 OPDEF(JSOP_POPN,      107,"popn",       NULL,         3, -1,  0,  0,  JOF_UINT16)
 
 /* ECMA-compliant assignment ops. */
@@ -405,17 +406,17 @@ OPDEF(JSOP_TOXMLLIST,     177,"toxmllist
 OPDEF(JSOP_XMLTAGEXPR,    178,"xmltagexpr", NULL,     1,  1,  1,  0,  JOF_BYTE)
 OPDEF(JSOP_XMLELTEXPR,    179,"xmleltexpr", NULL,     1,  1,  1,  0,  JOF_BYTE)
 OPDEF(JSOP_XMLOBJECT,     180,"xmlobject",  NULL,     3,  0,  1, 19,  JOF_OBJECT)
 OPDEF(JSOP_XMLCDATA,      181,"xmlcdata",   NULL,     3,  0,  1, 19,  JOF_ATOM)
 OPDEF(JSOP_XMLCOMMENT,    182,"xmlcomment", NULL,     3,  0,  1, 19,  JOF_ATOM)
 OPDEF(JSOP_XMLPI,         183,"xmlpi",      NULL,     3,  1,  1, 19,  JOF_ATOM)
 OPDEF(JSOP_CALLPROP,      184,"callprop",   NULL,     3,  1,  2, 18,  JOF_ATOM|JOF_PROP|JOF_CALLOP)
 OPDEF(JSOP_GETFUNNS,      185,"getfunns",   NULL,     1,  0,  1, 19,  JOF_BYTE)
-OPDEF(JSOP_FOREACH,       186,"foreach",    NULL,     1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_UNUSED186,     186,"unused186",  NULL,     1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_DELDESC,       187,"deldesc",    NULL,     1,  2,  1, 17,  JOF_BYTE |JOF_ELEM|JOF_DEL)
 
 /*
  * Opcode to hold 24-bit immediate integer operands.
  */
 OPDEF(JSOP_UINT24,        188,"uint24",     NULL,     4,  0,  1, 16,  JOF_UINT24)
 
 /*
@@ -472,17 +473,17 @@ OPDEF(JSOP_FORLOCAL,      207,"forlocal"
  * Iterator, generator, and array comprehension support.
  */
 OPDEF(JSOP_FORCONST,      208,"forconst",    NULL,    3,  0,  1, 19,  JOF_QVAR|JOF_NAME|JOF_FOR)
 OPDEF(JSOP_ENDITER,       209,"enditer",     NULL,    1,  1,  0,  0,  JOF_BYTE|JOF_TMPSLOT)
 OPDEF(JSOP_GENERATOR,     210,"generator",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_YIELD,         211,"yield",       NULL,    1,  1,  1,  1,  JOF_BYTE)
 OPDEF(JSOP_ARRAYPUSH,     212,"arraypush",   NULL,    3,  1,  0,  3,  JOF_LOCAL)
 
-OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL,   1,  1,  1,  0,  JOF_BYTE)
+OPDEF(JSOP_UNUSED213,     213,"unused213",   NULL,    1,  0,  0,  0,  JOF_BYTE)
 
 /*
  * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...).
  */
 OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL,   1,  3,  0,  3,  JOF_BYTE|JOF_SET)
 
 /*
  * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -61,16 +61,17 @@
 #include "jsapi.h"
 #include "jsarray.h"
 #include "jsatom.h"
 #include "jscntxt.h"
 #include "jsconfig.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsinterp.h"
+#include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
@@ -2669,30 +2670,31 @@ Statement(JSContext *cx, JSTokenStream *
 #endif
 
         /* A FOR node is binary, left is loop control and right is the body. */
         pn = NewParseNode(cx, ts, PN_BINARY, tc);
         if (!pn)
             return NULL;
         js_PushStatement(tc, &stmtInfo, STMT_FOR_LOOP, -1);
 
-        pn->pn_op = JSOP_FORIN;
+        pn->pn_op = JSOP_ITER;
+        pn->pn_iflags = 0;
         if (js_MatchToken(cx, ts, TOK_NAME)) {
             if (CURRENT_TOKEN(ts).t_atom == cx->runtime->atomState.eachAtom)
-                pn->pn_op = JSOP_FOREACH;
+                pn->pn_iflags = JSITER_FOREACH;
             else
                 js_UngetToken(ts);
         }
 
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
         ts->flags |= TSF_OPERAND;
         tt = js_PeekToken(cx, ts);
         ts->flags &= ~TSF_OPERAND;
         if (tt == TOK_SEMI) {
-            if (pn->pn_op == JSOP_FOREACH)
+            if (pn->pn_iflags & JSITER_FOREACH)
                 goto bad_for_each;
 
             /* No initializer -- set first kid of left sub-node to null. */
             pn1 = NULL;
         } else {
             /*
              * Set pn1 to a var list or an initializing expression.
              *
@@ -2738,38 +2740,41 @@ Statement(JSContext *cx, JSTokenStream *
 
         /*
          * We can be sure that it's a for/in loop if there's still an 'in'
          * keyword here, even if JavaScript recognizes 'in' as an operator,
          * as we've excluded 'in' from being parsed in RelExpr by setting
          * the TCF_IN_FOR_INIT flag in our JSTreeContext.
          */
         if (pn1 && js_MatchToken(cx, ts, TOK_IN)) {
+            pn->pn_iflags |= JSITER_ENUMERATE;
             stmtInfo.type = STMT_FOR_IN_LOOP;
 
             /* Check that the left side of the 'in' is valid. */
             JS_ASSERT(!TOKEN_TYPE_IS_DECL(tt) || pn1->pn_type == tt);
             if (TOKEN_TYPE_IS_DECL(tt)
                 ? (pn1->pn_count > 1 || pn1->pn_op == JSOP_DEFCONST
 #if JS_HAS_DESTRUCTURING
                    || (JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
-                       pn->pn_op == JSOP_FORIN &&
+                       pn->pn_op == JSOP_ITER &&
+                       !(pn->pn_iflags & JSITER_FOREACH) &&
                        (pn1->pn_head->pn_type == TOK_RC ||
                         (pn1->pn_head->pn_type == TOK_RB &&
                          pn1->pn_head->pn_count != 2) ||
                         (pn1->pn_head->pn_type == TOK_ASSIGN &&
                          (pn1->pn_head->pn_left->pn_type != TOK_RB ||
                           pn1->pn_head->pn_left->pn_count != 2))))
 #endif
                   )
                 : (pn1->pn_type != TOK_NAME &&
                    pn1->pn_type != TOK_DOT &&
 #if JS_HAS_DESTRUCTURING
                    ((JSVERSION_NUMBER(cx) == JSVERSION_1_7 &&
-                     pn->pn_op == JSOP_FORIN)
+                     pn->pn_op == JSOP_ITER &&
+                     !(pn->pn_iflags & JSITER_FOREACH))
                     ? (pn1->pn_type != TOK_RB || pn1->pn_count != 2)
                     : (pn1->pn_type != TOK_RB && pn1->pn_type != TOK_RC)) &&
 #endif
 #if JS_HAS_LVALUE_RETURN
                    pn1->pn_type != TOK_LP &&
 #endif
 #if JS_HAS_XML_SUPPORT
                    (pn1->pn_type != TOK_UNARYOP ||
@@ -2825,32 +2830,33 @@ Statement(JSContext *cx, JSTokenStream *
                 if (pn1 == pn2 && !CheckDestructuring(cx, NULL, pn2, NULL, tc))
                     return NULL;
 
                 if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
                     /*
                      * Destructuring for-in requires [key, value] enumeration
                      * in JS1.7.
                      */
-                    if (pn->pn_op != JSOP_FOREACH)
-                        pn->pn_op = JSOP_FOREACHKEYVAL;
+                    JS_ASSERT(pn->pn_op == JSOP_ITER);
+                    if (!(pn->pn_iflags & JSITER_FOREACH))
+                        pn->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
                 }
                 break;
 #endif
 
               default:;
             }
 
             /* Parse the object expression as the right operand of 'in'. */
             pn2 = NewBinary(cx, TOK_IN, JSOP_NOP, pn1, Expr(cx, ts, tc), tc);
             if (!pn2)
                 return NULL;
             pn->pn_left = pn2;
         } else {
-            if (pn->pn_op == JSOP_FOREACH)
+            if (pn->pn_iflags & JSITER_FOREACH)
                 goto bad_for_each;
             pn->pn_op = JSOP_NOP;
 
             /* Parse the loop condition or null into pn2. */
             MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT);
             ts->flags |= TSF_OPERAND;
             tt = js_PeekToken(cx, ts);
             ts->flags &= ~TSF_OPERAND;
@@ -4180,20 +4186,21 @@ ComprehensionTail(JSContext *cx, JSToken
          * FOR node is binary, left is loop control and right is body.  Use
          * index to count each block-local let-variable on the left-hand side
          * of the IN.
          */
         pn2 = NewParseNode(cx, ts, PN_BINARY, tc);
         if (!pn2)
             return NULL;
 
-        pn2->pn_op = JSOP_FORIN;
+        pn2->pn_op = JSOP_ITER;
+        pn2->pn_iflags = JSITER_ENUMERATE;
         if (js_MatchToken(cx, ts, TOK_NAME)) {
             if (CURRENT_TOKEN(ts).t_atom == rt->atomState.eachAtom)
-                pn2->pn_op = JSOP_FOREACH;
+                pn2->pn_iflags |= JSITER_FOREACH;
             else
                 js_UngetToken(ts);
         }
         MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
 
         tt = js_GetToken(cx, ts);
         switch (tt) {
 #if JS_HAS_DESTRUCTURING
@@ -4206,18 +4213,20 @@ ComprehensionTail(JSContext *cx, JSToken
             if (pn3->pn_type != TOK_RB || pn3->pn_count != 2) {
                 js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
                                             JSMSG_BAD_FOR_LEFTSIDE);
                 return NULL;
             }
 
             if (JSVERSION_NUMBER(cx) == JSVERSION_1_7) {
                 /* Destructuring requires [key, value] enumeration in JS1.7. */
-                if (pn2->pn_op != JSOP_FOREACH)
-                    pn2->pn_op = JSOP_FOREACHKEYVAL;
+                JS_ASSERT(pn2->pn_op == JSOP_ITER);
+                JS_ASSERT(pn2->pn_iflags & JSITER_ENUMERATE);
+                if (!(pn2->pn_iflags & JSITER_FOREACH))
+                    pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
             }
             break;
 #endif
 
           case TOK_NAME:
             atom = CURRENT_TOKEN(ts).t_atom;
             if (!data.binder(cx, &data, atom, tc))
                 return NULL;
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -297,16 +297,17 @@ struct JSParseNode {
             JSParseNode *kid1;          /* condition, discriminant, etc. */
             JSParseNode *kid2;          /* then-part, case list, etc. */
             JSParseNode *kid3;          /* else-part, default case, etc. */
         } ternary;
         struct {                        /* two kids if binary */
             JSParseNode *left;
             JSParseNode *right;
             jsval       val;            /* switch case value */
+            uintN       iflags;         /* JSITER_* flags for TOK_FOR node */
         } binary;
         struct {                        /* one kid if unary */
             JSParseNode *kid;
             jsint       num;            /* -1 or sharp variable number */
             JSBool      hidden;         /* hidden genexp-induced JSOP_YIELD */
         } unary;
         struct {                        /* name, labeled statement, etc. */
             JSAtom      *atom;          /* name or label atom, null if slot */
@@ -341,16 +342,17 @@ struct JSParseNode {
 #define pn_count        pn_u.list.count
 #define pn_extra        pn_u.list.extra
 #define pn_kid1         pn_u.ternary.kid1
 #define pn_kid2         pn_u.ternary.kid2
 #define pn_kid3         pn_u.ternary.kid3
 #define pn_left         pn_u.binary.left
 #define pn_right        pn_u.binary.right
 #define pn_val          pn_u.binary.val
+#define pn_iflags       pn_u.binary.iflags
 #define pn_kid          pn_u.unary.kid
 #define pn_num          pn_u.unary.num
 #define pn_hidden       pn_u.unary.hidden
 #define pn_atom         pn_u.name.atom
 #define pn_expr         pn_u.name.expr
 #define pn_slot         pn_u.name.slot
 #define pn_const        pn_u.name.isconst
 #define pn_dval         pn_u.dval
--- a/js/src/jsprvtd.h
+++ b/js/src/jsprvtd.h
@@ -87,16 +87,17 @@ typedef uint8  jsbytecode;
 typedef uint8  jssrcnote;
 typedef uint32 jsatomid;
 
 /* Struct typedefs. */
 typedef struct JSArgumentFormatMap  JSArgumentFormatMap;
 typedef struct JSCodeGenerator      JSCodeGenerator;
 typedef struct JSGCThing            JSGCThing;
 typedef struct JSGenerator          JSGenerator;
+typedef struct JSNativeEnumerator   JSNativeEnumerator;
 typedef struct JSParseContext       JSParseContext;
 typedef struct JSParsedObjectBox    JSParsedObjectBox;
 typedef struct JSParseNode          JSParseNode;
 typedef struct JSPropCacheEntry     JSPropCacheEntry;
 typedef struct JSSharpObjectMap     JSSharpObjectMap;
 typedef struct JSTempValueRooter    JSTempValueRooter;
 typedef struct JSThread             JSThread;
 typedef struct JSToken              JSToken;
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -197,17 +197,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 26)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 27)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool