From 68b5da6d052bf00e95484b529c2e5664fc823b10 Mon Sep 17 00:00:00 2001 From: "brendan%mozilla.org" Date: Thu, 5 Oct 2006 00:19:49 +0000 Subject: [PATCH] Igor's cleanup patch, with some decompiler assist from me (354982, r=us). git-svn-id: svn://10.0.0.236/trunk@213135 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/src/jsapi.c | 2 +- mozilla/js/src/jscntxt.h | 19 +- mozilla/js/src/jsemit.c | 22 +- mozilla/js/src/jsgc.c | 4 +- mozilla/js/src/jsinterp.c | 367 +++++------------------ mozilla/js/src/jsiter.c | 561 +++++++++++++++++++++--------------- mozilla/js/src/jsiter.h | 37 +-- mozilla/js/src/jsopcode.c | 23 +- mozilla/js/src/jsopcode.tbl | 22 +- mozilla/js/src/jsxdrapi.h | 2 +- 10 files changed, 432 insertions(+), 627 deletions(-) diff --git a/mozilla/js/src/jsapi.c b/mozilla/js/src/jsapi.c index aa3e582890b..88e80359f00 100644 --- a/mozilla/js/src/jsapi.c +++ b/mozilla/js/src/jsapi.c @@ -4461,7 +4461,7 @@ JS_SetCallReturnValue2(JSContext *cx, jsval v) { #if JS_HAS_LVALUE_RETURN cx->rval2 = v; - cx->rval2set = JS_RVAL2_VALUE; + cx->rval2set = JS_TRUE; #endif } diff --git a/mozilla/js/src/jscntxt.h b/mozilla/js/src/jscntxt.h index bf84828a4e0..b866c90d5b9 100644 --- a/mozilla/js/src/jscntxt.h +++ b/mozilla/js/src/jscntxt.h @@ -396,20 +396,6 @@ struct JSRuntime { #define JS_KEEP_ATOMS(rt) JS_ATOMIC_INCREMENT(&(rt)->gcKeepAtoms); #define JS_UNKEEP_ATOMS(rt) JS_ATOMIC_DECREMENT(&(rt)->gcKeepAtoms); -#if JS_HAS_LVALUE_RETURN -/* - * Values for the cx->rval2set flag byte. This flag tells whether cx->rval2 - * is unset (CLEAR), set to a jsval (VALUE) naming a property in the object - * referenced by cx->fp->rval, or set to a jsid (ITERKEY) result of a native - * iterator's it.next() call (where the return value of it.next() is the next - * value in the iteration). - * - * The ITERKEY case is just an optimization for native iterators, as general - * iterators can return an array of length 2 to return a [key, value] pair. - */ -enum { JS_RVAL2_CLEAR, JS_RVAL2_VALUE, JS_RVAL2_ITERKEY }; -#endif - #ifdef JS_ARGUMENT_FORMATTER_DEFINED /* * Linked list mapping format strings for JS_{Convert,Push}Arguments{,VA} to @@ -652,7 +638,7 @@ struct JSContext { * jsval by calling JS_SetCallReturnValue2(cx, idval). */ jsval rval2; - uint8 rval2set; + JSPackedBool rval2set; #endif #if JS_HAS_XML_SUPPORT @@ -704,9 +690,6 @@ struct JSContext { /* Stack of thread-stack-allocated temporary GC roots. */ JSTempValueRooter *tempValueRooters; - /* Iterator cache to speed up native default for-in loop case. */ - JSObject *cachedIterObj; - #ifdef GC_MARK_DEBUG /* Top of the GC mark stack. */ void *gcCurrentMarkNode; diff --git a/mozilla/js/src/jsemit.c b/mozilla/js/src/jsemit.c index 2316bfbdcbd..ba9f9e9c177 100644 --- a/mozilla/js/src/jsemit.c +++ b/mozilla/js/src/jsemit.c @@ -4228,21 +4228,23 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* Compile the object expression to the right of 'in'. */ if (!js_EmitTree(cx, cg, pn2->pn_right)) return JS_FALSE; - if (js_Emit1(cx, cg, JSOP_TOOBJECT) < 0) + + /* + * 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). + */ + JS_ASSERT(pn->pn_op == JSOP_FORIN || +#if JS_HAS_DESTRUCTURING + pn->pn_op == JSOP_FOREACHKEYVAL || +#endif + pn->pn_op == JSOP_FOREACH); + if (js_Emit1(cx, cg, pn->pn_op) < 0) return JS_FALSE; top = CG_OFFSET(cg); SET_STATEMENT_TOP(&stmtInfo, top); - /* - * Emit a prefix bytecode to set flags distinguishing kinds of - * for-in loops (for-in, for-each-in, destructuring for-in) for - * the immediately subsequent JSOP_FOR* bytecode. - */ - JS_ASSERT(pn->pn_op != JSOP_NOP); - if (js_Emit1(cx, cg, pn->pn_op) < 0) - return JS_FALSE; - /* * Compile a JSOP_FOR* bytecode based on the left hand side. * diff --git a/mozilla/js/src/jsgc.c b/mozilla/js/src/jsgc.c index e478852395c..1d5a0393dfa 100644 --- a/mozilla/js/src/jsgc.c +++ b/mozilla/js/src/jsgc.c @@ -2794,7 +2794,7 @@ restart: if (acx->throwing && JSVAL_IS_GCTHING(acx->exception)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->exception), "exception"); #if JS_HAS_LVALUE_RETURN - if (acx->rval2set == JS_RVAL2_VALUE && JSVAL_IS_GCTHING(acx->rval2)) + if (acx->rval2set && JSVAL_IS_GCTHING(acx->rval2)) GC_MARK(cx, JSVAL_TO_GCTHING(acx->rval2), "rval2"); #endif @@ -2823,8 +2823,6 @@ restart: if (acx->sharpObjectMap.depth > 0) js_GCMarkSharpMap(cx, &acx->sharpObjectMap); - - acx->cachedIterObj = NULL; } #ifdef DUMP_CALL_TABLE diff --git a/mozilla/js/src/jsinterp.c b/mozilla/js/src/jsinterp.c index 79206aa60b1..efabf220f51 100644 --- a/mozilla/js/src/jsinterp.c +++ b/mozilla/js/src/jsinterp.c @@ -1364,7 +1364,7 @@ have_fun: #if JS_HAS_LVALUE_RETURN /* Set by JS_SetCallReturnValue2, used to return reference types. */ - cx->rval2set = JS_RVAL2_CLEAR; + cx->rval2set = JS_FALSE; #endif /* If native, use caller varobj and scopeChain for eval. */ @@ -2041,7 +2041,7 @@ js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) uintN argc, attrs, flags, slot; jsval *vp, lval, rval, ltmp, rtmp; jsid id; - JSObject *withobj, *origobj, *iterobj; + JSObject *withobj, *iterobj; JSProperty *prop; JSScopeProperty *sprop; JSString *str, *str2; @@ -2560,21 +2560,6 @@ interrupt: } END_CASE(JSOP_ANDX) - BEGIN_CASE(JSOP_TOOBJECT) - rval = FETCH_OPND(-1); - if (!JSVAL_IS_PRIMITIVE(rval)) { - obj = JSVAL_TO_OBJECT(rval); - } else { - SAVE_SP_AND_PC(fp); - ok = js_ValueToObject(cx, rval, &obj); - if (!ok) - goto out; - } - - /* Don't use STORE_OPND -- we want our input's generating pc. */ - sp[-1] = OBJECT_TO_JSVAL(obj); - END_CASE(JSOP_TOOBJECT) - /* * If the index value at sp[n] is not an int that fits in a jsval, it could * be an object (an XML QName, AttributeName, or AnyName), but only if we are @@ -2619,20 +2604,35 @@ interrupt: OBJ_DROP_PROPERTY(cx, obj2, prop); END_CASE(JSOP_IN) - BEGIN_CASE(JSOP_FORIN) - flags = 0; - END_CASE(JSOP_FORIN) - BEGIN_CASE(JSOP_FOREACH) - flags = JSITER_FOREACH; - END_CASE(JSOP_FOREACH) + flags = JSITER_ENUMERATE | JSITER_FOREACH; + goto value_to_iter; #if JS_HAS_DESTRUCTURING BEGIN_CASE(JSOP_FOREACHKEYVAL) - flags = JSITER_FOREACH | JSITER_KEYVALUE; - END_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: + JS_ASSERT(sp > fp->spbase); + SAVE_SP_AND_PC(fp); + ok = js_ValueToIterator(cx, flags, &sp[-1]); + if (!ok) + goto out; + JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); + JS_ASSERT(JSOP_FORIN_LENGTH == js_CodeSpec[op].length); + END_CASE(JSOP_FORIN) + BEGIN_CASE(JSOP_FORPROP) /* * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop @@ -2687,212 +2687,21 @@ interrupt: do_forinloop: /* - * ECMA-compatible for/in evals the object just once, before loop. - * Bad old bytecodes (since removed) did it on every iteration. + * Reach under the top of stack to find our property iterator, a + * JSObject that contains the iteration state. */ - obj = JSVAL_TO_OBJECT(sp[i]); + JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[i])); + iterobj = JSVAL_TO_OBJECT(sp[i]); - /* If the thing to the right of 'in' has no properties, break. */ - if (!obj) { + SAVE_SP_AND_PC(fp); + ok = js_CallIteratorNext(cx, iterobj, &rval); + if (!ok) + goto out; + if (rval == JSVAL_HOLE) { rval = JSVAL_FALSE; goto end_forinloop; } - /* - * Save the thing to the right of 'in' as origobj. Later on, we - * use this variable to suppress enumeration of shadowed prototype - * properties. - */ - origobj = obj; - - /* - * Reach under the top of stack to find our property iterator, a - * JSObject that contains the iteration state. (An object is used - * rather than a native struct so that the iteration state is - * cleaned up via GC if the for-in loop terminates abruptly.) - */ - vp = &sp[i - 1]; - rval = *vp; - - /* - * Save sp and pc in fp now, before any OBJ_* call-outs that might - * nest an interpreter or GC activation on this context. - */ - SAVE_SP_AND_PC(fp); - - /* Is this the first iteration ? */ - if (JSVAL_IS_NULL(rval)) { - /* Yes, use the new iteration protocol. */ - fp->pc = (jsbytecode *) sp[i-depth]; - iterobj = js_ValueToIterator(cx, OBJECT_TO_JSVAL(obj), flags); - fp->pc = pc; - if (!iterobj) { - ok = JS_FALSE; - goto out; - } - *vp = OBJECT_TO_JSVAL(iterobj); - flags |= js_GetNativeIteratorFlags(cx, iterobj); - - /* - * Store the current object below the iterator for generality: - * with the iteration protocol, we cannot assume that a native - * iterator was found or created by js_ValueToIterator, so we - * can't use its parent slot to track the current object being - * iterated along origobj's prototype chain. We need another - * stack slot, which JSOP_STARTITER allocated for us. - */ - vp[-1] = OBJECT_TO_JSVAL(obj); - } else { - /* This is not the first iteration. Recover iterator state. */ - JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval)); - iterobj = JSVAL_TO_OBJECT(rval); - flags |= js_GetNativeIteratorFlags(cx, iterobj); - obj = JSVAL_TO_OBJECT(vp[-1]); - } - - enum_next_property: - { - jsid fid; - - /* - * If enumerating, get the next jsid to be enumerated and store it - * in fid. If iterating, just get rval. - */ -#ifdef DEBUG - fid = JSVAL_NULL; -#endif - ok = js_CallIteratorNext(cx, iterobj, flags, - (flags & JSITER_ENUMERATE) ? &fid : NULL, - &rval); - if (!ok) { - /* Nothing more to iterate in obj, or some other exception? */ - if (!cx->throwing || - !VALUE_IS_STOP_ITERATION(cx, cx->exception)) { - /* Some other exception or error, bail out. */ - goto out; - } - - /* Inline JS_ClearPendingException(cx). */ - cx->throwing = JS_FALSE; - cx->exception = JSVAL_VOID; - - /* - * Enumerate the properties on obj's prototype chain, unless - * the JSITER_ENUMERATE is clear -- in this case, do not merge - * ECMA precedent and the Pythonic iteration protocol. Loop - * over only whatever values are returned by the iterator for - * the directly referenced object. - */ - if (!(flags & JSITER_ENUMERATE) || - !(obj = OBJ_GET_PROTO(cx, obj))) { - /* End of property list -- terminate loop. */ - ok = JS_TRUE; - flags = 0; - rval = JSVAL_FALSE; - goto end_forinloop; - } - - /* - * Clear JSITER_FOREACH now that we are up the prototype chain - * from the original object. We can't expect to get the same - * value from a prototype as we would if we started the get at - * the original object, so we must do our own getting, further - * below when testing 'if (flags & JSITER_FOREACH)'. - */ - ok = js_NewNativeIterator(cx, obj, flags & ~JSITER_FOREACH, vp); - if (!ok) - goto out; - iterobj = JSVAL_TO_OBJECT(*vp); - - /* Reset ok and store the current iterable in vp[-1]. */ - ok = JS_TRUE; - vp[-1] = OBJECT_TO_JSVAL(obj); - goto enum_next_property; - } - - /* - * Without the JSITER_ENUMERATE flag, do not expect to lookup fid - * and find anything. The iteration protocol does not require any - * such thing, which would make a name collision on 'next' hazard. - * But if the JSITER_ENUMERATE flag is set, we must do the usual - * deleted-property and shadowed-proto-property tests. - */ - if (flags & JSITER_ENUMERATE) { - /* Skip properties not in obj when looking from origobj. */ - ok = OBJ_LOOKUP_PROPERTY(cx, origobj, fid, &obj2, &prop); - if (!ok) - goto out; - if (prop) - OBJ_DROP_PROPERTY(cx, obj2, prop); - - /* - * If the id was deleted, or found in a prototype object or an - * unrelated object (specifically, not in an inner object for - * obj), skip it. This step means that all OBJ_LOOKUP_PROPERTY - * implementations must return an object further along on the - * prototype chain, or else possibly an object returned by the - * JSExtendedClass.outerObject optional hook. - */ - if (!prop) - goto enum_next_property; - if (obj != obj2) { - cond = JS_FALSE; - clasp = OBJ_GET_CLASS(cx, obj2); - if (clasp->flags & JSCLASS_IS_EXTENDED) { - JSExtendedClass *xclasp; - - xclasp = (JSExtendedClass *) clasp; - cond = xclasp->outerObject && - xclasp->outerObject(cx, obj2) == obj; - } - if (!cond) - goto enum_next_property; - } - } - - if (flags & JSITER_FOREACH) { - /* Clear the local foreach flag set by our prefix bytecode. */ - flags = 0; - - /* - * If enumerating up the prototype chain, we suppressed the - * JSITER_FOREACH flag when we created the iterator, because - * the iterator can't get the value for fid without starting - * from origobj. So we must OBJ_GET_PROPERTY here. - */ - if (origobj != obj) { - ok = OBJ_GET_PROPERTY(cx, origobj, fid, &rval); - if (!ok) - goto out; - } - } else if (!(flags & JSITER_ENUMERATE)) { - /* Iterators return arbitrary values, not string ids. */ - JS_ASSERT(fid == JSVAL_NULL); - } else if (JSID_IS_ATOM(fid)) { - /* Make rval a string for uniformity and compatibility. */ - rval = ATOM_KEY(JSID_TO_ATOM(fid)); - } -#if JS_HAS_XML_SUPPORT - else if (JSID_IS_OBJECT(fid)) { - str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(fid)); - if (!str) { - ok = JS_FALSE; - goto out; - } - - rval = STRING_TO_JSVAL(str); - } -#endif - else { - str = js_NumberToString(cx, (jsdouble) JSID_TO_INT(fid)); - if (!str) { - ok = JS_FALSE; - goto out; - } - - rval = STRING_TO_JSVAL(str); - } - switch (op) { case JSOP_FORARG: slot = GET_ARGNO(pc); @@ -2920,10 +2729,12 @@ interrupt: break; default: + JS_ASSERT(op == JSOP_FORPROP || op == JSOP_FORNAME); + /* Convert lval to a non-null object containing id. */ VALUE_TO_OBJECT(cx, lval, obj); - if (i + 1 < 0) - STORE_OPND(i + 1, OBJECT_TO_JSVAL(obj)); + if (op == JSOP_FORPROP) + STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); /* Set the variable obj[id] to refer to rval. */ fp->flags |= JSFRAME_ASSIGNING; @@ -2942,7 +2753,6 @@ interrupt: PUSH_OPND(rval); len = js_CodeSpec[op].length; DO_NEXT_OP(len); - } BEGIN_CASE(JSOP_DUP) JS_ASSERT(sp > fp->spbase); @@ -4118,51 +3928,27 @@ interrupt: goto out; JS_RUNTIME_METER(rt, nonInlineCalls); #if JS_HAS_LVALUE_RETURN - if (cx->rval2set != JS_RVAL2_CLEAR) { - if (cx->rval2set == JS_RVAL2_VALUE) { - /* - * Use the stack depth we didn't claim in our budget, but - * that we know is there on account of [fun, this] already - * having been pushed, at a minimum (if no args). Those - * two slots have been popped and [rval] has been pushed, - * which leaves one more slot for rval2 before we might - * overflow. - * - * NB: rval2 must be the property identifier, and rval the - * object from which to get the property. The pair form an - * ECMA "reference type", which can be used on the right- - * or left-hand side of assignment ops. Note: only native - * methods can return reference types. See JSOP_SETCALL - * just below for the left-hand-side case. - */ - PUSH_OPND(cx->rval2); - ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); - } -#if JS_HAS_GENERATORS - else { - /* - * A native iterator has returned an [id, value] pair with - * id in cx->rval2 and value on top of stack. Push value, - * store id as a value under it, and create a new array. - */ - JS_ASSERT(cx->rval2set == JS_RVAL2_ITERKEY); - lval = ID_TO_VALUE((jsid)cx->rval2); - rval = sp[-1]; - PUSH_OPND(rval); - sp[-2] = lval; - SAVE_SP_AND_PC(fp); - obj = js_NewArrayObject(cx, 2, sp - 2); - if (!obj) { - ok = JS_FALSE; - goto out; - } - rval = OBJECT_TO_JSVAL(obj); - } -#endif /* JS_HAS_GENERATORS */ + if (cx->rval2set) { + /* + * Use the stack depth we didn't claim in our budget, but that + * we know is there on account of [fun, this] already having + * been pushed, at a minimum (if no args). Those two slots + * have been popped and [rval] has been pushed, which leaves + * one more slot for rval2 before we might overflow. + * + * NB: rval2 must be the property identifier, and rval the + * object from which to get the property. The pair form an + * ECMA "reference type", which can be used on the right- or + * left-hand side of assignment ops. Note well: only native + * methods can return reference types. See JSOP_SETCALL just + * below for the left-hand-side case. + */ + PUSH_OPND(cx->rval2); + ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); sp--; STORE_OPND(-1, rval); - cx->rval2set = JS_RVAL2_CLEAR; + cx->rval2set = JS_FALSE; } #endif /* JS_HAS_LVALUE_RETURN */ obj = NULL; @@ -4178,14 +3964,14 @@ interrupt: LOAD_INTERRUPT_HANDLER(rt); if (!ok) goto out; - if (cx->rval2set != JS_RVAL2_VALUE) { + if (!cx->rval2set) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS); ok = JS_FALSE; goto out; } PUSH_OPND(cx->rval2); - cx->rval2set = JS_RVAL2_CLEAR; + cx->rval2set = JS_FALSE; obj = NULL; END_CASE(JSOP_SETCALL) #endif @@ -6101,36 +5887,21 @@ interrupt: #undef FAST_LOCAL_INCREMENT_OP - BEGIN_CASE(JSOP_STARTITER) - /* - * Start of a for-in or for-each-in loop: push two nulls. Push - * null instead of undefined so that code at do_forinloop: can - * tell that this opcode pushed the iterator slot, rather than a - * backward compatible JSOP_PUSH that was emitted prior to the - * introduction of the new iteration protocol. - */ - sp[0] = sp[1] = JSVAL_NULL; - sp += 2; - END_CASE(JSOP_STARTITER) + EMPTY_CASE(JSOP_STARTITER) BEGIN_CASE(JSOP_ENDITER) + JS_ASSERT(!JSVAL_IS_PRIMITIVE(sp[-1])); + iterobj = JSVAL_TO_OBJECT(sp[-1]); + /* - * For backward bytecode compatibility, the object currently being - * iterated is at sp[-3], and the iterator is at sp[-2]. + * js_CloseNativeIterator checks whether the iterator is not + * native, and also detects the case of a native iterator that + * has already escaped, even though a for-in loop caused it to + * be created. See jsiter.c. */ - rval = sp[-2]; - if (!JSVAL_IS_NULL(rval)) { - /* - * js_CloseNativeIterator checks whether the iterator is not - * native, and also detects the case of a native iterator that - * has already escaped, even though a for-in loop caused it to - * be created. See jsiter.c. - */ - SAVE_SP_AND_PC(fp); - js_CloseNativeIterator(cx, JSVAL_TO_OBJECT(rval)); - sp[-2] = JSVAL_NULL; - } - sp -= 3; + SAVE_SP_AND_PC(fp); + js_CloseNativeIterator(cx, iterobj); + *--sp = JSVAL_NULL; END_CASE(JSOP_ENDITER) #if JS_HAS_GENERATORS diff --git a/mozilla/js/src/jsiter.c b/mozilla/js/src/jsiter.c index cc90d67143d..8865137f487 100644 --- a/mozilla/js/src/jsiter.c +++ b/mozilla/js/src/jsiter.c @@ -58,6 +58,7 @@ #include "jsinterp.h" #include "jsiter.h" #include "jslock.h" +#include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" #include "jsscope.h" @@ -136,6 +137,8 @@ InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) iterobj->slots[JSSLOT_ITER_FLAGS] = INT_TO_JSVAL(flags); if (!js_RegisterCloseableIterator(cx, iterobj)) return JS_FALSE; + if (!obj) + return JS_TRUE; ok = #if JS_HAS_XML_SUPPORT @@ -149,49 +152,45 @@ InitNativeIterator(JSContext *cx, JSObject *iterobj, JSObject *obj, uintN flags) return JS_FALSE; iterobj->slots[JSSLOT_ITER_STATE] = state; + if (flags & JSITER_ENUMERATE) { + /* + * The enumerating iterator needs the original object to suppress + * enumeration of deleted or shadowed prototype properties. Since the + * enumerator never escapes to scripts, we use the prototype slot to + * store the original object. + */ + iterobj->slots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(obj); + } return JS_TRUE; } static JSBool Iterator(JSContext *cx, JSObject *iterobj, uintN argc, jsval *argv, jsval *rval) { - JSObject *obj; JSBool keyonly; - jsid id; - JSObject *obj2; - jsval fval; + uintN flags; + JSObject *obj; - /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(argv[0])) { - obj = JSVAL_TO_OBJECT(argv[0]); - } else { - obj = js_ValueToNonNullObject(cx, argv[0]); - if (!obj) - return JS_FALSE; - argv[0] = OBJECT_TO_JSVAL(obj); - } + keyonly = JS_FALSE; + if (!js_ValueToBoolean(cx, argv[1], &keyonly)) + return JS_FALSE; + flags = keyonly ? 0 : JSITER_FOREACH; if (cx->fp->flags & JSFRAME_CONSTRUCTING) { - keyonly = JS_FALSE; - return js_ValueToBoolean(cx, argv[1], &keyonly) && - InitNativeIterator(cx, iterobj, obj, - keyonly ? 0 : JSITER_FOREACH); + /* XXX work around old valueOf call hidden beneath js_ValueToObject */ + if (!JSVAL_IS_PRIMITIVE(argv[0])) { + obj = JSVAL_TO_OBJECT(argv[0]); + } else { + obj = js_ValueToNonNullObject(cx, argv[0]); + if (!obj) + return JS_FALSE; + argv[0] = OBJECT_TO_JSVAL(obj); + } + return InitNativeIterator(cx, iterobj, obj, flags); } - id = ATOM_TO_JSID(cx->runtime->atomState.iteratorAtom); - if (!JS_GetMethodById(cx, obj, id, &obj2, &fval)) - return JS_FALSE; - - if (JSVAL_IS_VOID(fval)) { - /* Fail over to the default enumerating native iterator. */ - keyonly = JS_FALSE; - return js_ValueToBoolean(cx, argv[1], &keyonly) && - js_NewNativeIterator(cx, obj, - keyonly ? 0 : JSITER_FOREACH, - rval); - } - argv[0] = OBJECT_TO_JSVAL(obj2); - return js_InternalCall(cx, obj2, fval, argc - 1, argv + 1, rval); + *rval = argv[0]; + return js_ValueToIterator(cx, flags, rval); } static JSBool @@ -210,90 +209,31 @@ NewKeyValuePair(JSContext *cx, jsid key, jsval val, jsval *rval) } static JSBool -SetupKeyValueReturn(JSContext *cx, jsid id, jsval val, jsval *rval) -{ -#if JS_HAS_LVALUE_RETURN - cx->rval2 = (jsval) id; - cx->rval2set = JS_RVAL2_ITERKEY; - *rval = val; - return JS_TRUE; -#else - return NewKeyValuePair(cx, id, val, rval); -#endif -} - -static JSBool -CheckKeyValueReturn(JSContext *cx, uintN flags, jsid *idp, jsval *rval) -{ - jsval val, idval; - JSBool arraylike; - jsuint length; - JSObject *obj; - - val = *rval; - -#if JS_HAS_LVALUE_RETURN - if (cx->rval2set == JS_RVAL2_ITERKEY) { - cx->rval2set = JS_RVAL2_CLEAR; - if (idp) - *idp = (jsid) cx->rval2; - if (!idp || (flags & JSITER_KEYVALUE)) - return NewKeyValuePair(cx, (jsid) cx->rval2, val, rval); - return JS_TRUE; - } -#endif - - if (!idp) - return JS_TRUE; - - arraylike = JS_FALSE; - length = 0; /* quell GCC overwarnings */ - obj = NULL; - if (!JSVAL_IS_PRIMITIVE(val)) { - obj = JSVAL_TO_OBJECT(val); - if (!js_IsArrayLike(cx, obj, &arraylike, &length)) - return JS_FALSE; - } - - if (arraylike && length == 2) { - if (!OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(0), &idval)) - return JS_FALSE; - if (!JS_ValueToId(cx, idval, idp)) - return JS_FALSE; - if (flags & JSITER_KEYVALUE) - return JS_TRUE; - return OBJ_GET_PROPERTY(cx, obj, INT_TO_JSID(1), rval); - } - - return JS_ValueToId(cx, val, idp); -} - -static JSBool -iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, - jsval *rval) +IteratorNextImpl(JSContext *cx, JSObject *obj, jsval *rval) { JSObject *iterable; - jsval state, val; + jsval state; uintN flags; JSBool foreach, ok; jsid id; - if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) - return JS_FALSE; + JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_IteratorClass); iterable = OBJ_GET_PARENT(cx, obj); + JS_ASSERT(iterable); state = OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE); if (JSVAL_IS_NULL(state)) goto stop; flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_FLAGS)); + JS_ASSERT(!(flags & JSITER_ENUMERATE)); foreach = (flags & JSITER_FOREACH) != 0; ok = #if JS_HAS_XML_SUPPORT (foreach && OBJECT_IS_XML(cx, iterable)) ? ((JSXMLObjectOps *) iterable->map->ops)-> enumerateValues(cx, iterable, JSENUMERATE_NEXT, &state, - &id, &val) + &id, rval) : #endif OBJ_ENUMERATE(cx, iterable, JSENUMERATE_NEXT, &state, &id); @@ -307,22 +247,52 @@ iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, if (foreach) { #if JS_HAS_XML_SUPPORT if (!OBJECT_IS_XML(cx, iterable) && - !OBJ_GET_PROPERTY(cx, iterable, id, &val)) { + !OBJ_GET_PROPERTY(cx, iterable, id, rval)) { return JS_FALSE; } #endif - if (!SetupKeyValueReturn(cx, id, val, rval)) + if (!NewKeyValuePair(cx, id, *rval, rval)) return JS_FALSE; } else { *rval = ID_TO_VALUE(id); } return JS_TRUE; -stop: - js_ThrowStopIteration(cx, obj); + stop: + JS_ASSERT(OBJ_GET_SLOT(cx, obj, JSSLOT_ITER_STATE) == JSVAL_NULL); + *rval = JSVAL_HOLE; + return JS_TRUE; +} + +static JSBool +js_ThrowStopIteration(JSContext *cx, JSObject *obj) +{ + jsval v; + + JS_ASSERT(!JS_IsExceptionPending(cx)); + if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) + JS_SetPendingException(cx, v); return JS_FALSE; } +static JSBool +iterator_next(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, + jsval *rval) +{ + if (!JS_InstanceOf(cx, obj, &js_IteratorClass, argv)) + return JS_FALSE; + + if (!IteratorNextImpl(cx, obj, rval)) + return JS_FALSE; + + if (*rval == JSVAL_HOLE) { + *rval = JSVAL_NULL; + js_ThrowStopIteration(cx, obj); + return JS_FALSE; + } + return JS_TRUE; +} + static JSBool iterator_self(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) @@ -337,26 +307,6 @@ static JSFunctionSpec iterator_methods[] = { {0,0,0,0,0} }; -JSBool -js_NewNativeIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp) -{ - JSObject *iterobj; - - /* - * Create iterobj with a NULL parent to ensure that we use the correct - * scope chain to lookup the iterator's constructor. Since we use the - * parent slot to keep track of the iterable, we must fix it up later. - */ - iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); - if (!iterobj) - return JS_FALSE; - - /* Store iterobj in *vp to protect it from GC (callers must root vp). */ - *vp = OBJECT_TO_JSVAL(iterobj); - - return InitNativeIterator(cx, iterobj, obj, flags); -} - uintN js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj) { @@ -385,148 +335,296 @@ js_CloseNativeIterator(JSContext *cx, JSObject *iterobj) if (!(flags & JSITER_ENUMERATE)) return; - /* - * Clear the cached iterator object member of cx. Normally the GC clears - * all contexts' cachedIterObj members, but JSOP_ENDITER calls us eagerly - * to close iterobj. - */ - if (iterobj == cx->cachedIterObj) - cx->cachedIterObj = NULL; - js_CloseIteratorState(cx, iterobj); } /* - * Inline expansion of Iterator, with extra logic to constrain the result of - * ToObject(v).__iterator__. + * Call ToObject(v).__iterator__(keyonly) if ToObject(v).__iterator__ exists. + * Otherwise construct the defualt iterator. */ -JSObject * -js_ValueToIterator(JSContext *cx, jsval v, uintN flags) +JSBool +js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp) { - JSObject *obj, *iterobj; + JSObject *obj; JSTempValueRooter tvr; - jsval arg, fval, rval; + const JSAtom *atom; + JSBool ok; + JSObject *iterobj; + jsval arg; JSString *str; - const JSAtom *atom = cx->runtime->atomState.iteratorAtom; + + JS_ASSERT(!(flags & ~(JSITER_ENUMERATE | + JSITER_FOREACH | + JSITER_KEYVALUE))); + + /* JSITER_KEYVALUE must always come with JSITER_FOREACH */ + JS_ASSERT(!(flags & JSITER_KEYVALUE) || (flags & JSITER_FOREACH)); /* XXX work around old valueOf call hidden beneath js_ValueToObject */ - if (!JSVAL_IS_PRIMITIVE(v)) { - obj = JSVAL_TO_OBJECT(v); + if (!JSVAL_IS_PRIMITIVE(*vp)) { + obj = JSVAL_TO_OBJECT(*vp); } else { - obj = js_ValueToNonNullObject(cx, v); - if (!obj) - return NULL; + /* + * Enumerating over null and undefined gives an empty enumerator. + * This is contrary to ECMA-262 9.9 ToObject, invoked from step 3 of + * the first production in 12.6.4 and step 4 of the second production, + * but it's "web JS" compatible. + */ + if ((flags & JSITER_ENUMERATE)) { + if (!js_ValueToObject(cx, *vp, &obj)) + return JS_FALSE; + if (!obj) + goto default_iter; + } else { + obj = js_ValueToNonNullObject(cx, *vp); + if (!obj) + return JS_FALSE; + } } - JS_PUSH_SINGLE_TEMP_ROOT(cx, obj, &tvr); - if (!JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), &obj, &fval)) + JS_ASSERT(obj); + JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr); + + atom = cx->runtime->atomState.iteratorAtom; + if (!JS_GetMethodById(cx, obj, ATOM_TO_JSID(atom), &obj, vp)) goto bad; - if (JSVAL_IS_VOID(fval)) { - /* Fail over to the default enumerating native iterator. */ - if (!js_NewNativeIterator(cx, obj, - (flags & JSITER_FOREACH) | JSITER_ENUMERATE, - &rval)) { + + if (JSVAL_IS_VOID(*vp)) { + default_iter: + /* + * Fail over to the default enumerating native iterator. + * + * Create iterobj with a NULL parent to ensure that we use the correct + * scope chain to lookup the iterator's constructor. Since we use the + * parent slot to keep track of the iterable, we must fix it up after. + */ + iterobj = js_NewObject(cx, &js_IteratorClass, NULL, NULL); + if (!iterobj) + goto bad; + + /* Store iterobj in *vp to protect it from GC (callers must root vp). */ + *vp = OBJECT_TO_JSVAL(iterobj); + + if (!InitNativeIterator(cx, iterobj, obj, flags)) goto bad; - } } else { + tvr.u.object = obj; arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0); - if (!js_InternalInvoke(cx, obj, fval, JSINVOKE_ITERATOR, 1, &arg, - &rval)) { + if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg, vp)) + goto bad; + if (JSVAL_IS_PRIMITIVE(*vp)) { + str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, *vp, NULL); + if (str) { + JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_ITERATOR_RETURN, + JSSTRING_CHARS(str), + JSSTRING_CHARS(ATOM_TO_STRING(atom))); + } goto bad; } - if (JSVAL_IS_PRIMITIVE(rval)) - goto bad_iterator; } - iterobj = JSVAL_TO_OBJECT(rval); - -out: - JS_POP_TEMP_ROOT(cx, &tvr); - return iterobj; - -bad: - iterobj = NULL; + ok = JS_TRUE; + out: + if (obj) + JS_POP_TEMP_ROOT(cx, &tvr); + return ok; + bad: + ok = JS_FALSE; goto out; +} -bad_iterator: - str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL); - if (str) { - JS_ReportErrorNumberUC(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_ITERATOR_RETURN, - JSSTRING_CHARS(str), - JSSTRING_CHARS(ATOM_TO_STRING(atom))); +static JSBool +CallEnumeratorNext(JSContext *cx, JSObject *iterobj, uintN flags, jsval *rval) +{ + JSObject *obj, *origobj; + jsval state; + JSBool foreach; + jsid id; + JSObject *obj2; + JSBool cond; + JSClass *clasp; + JSExtendedClass *xclasp; + JSProperty *prop; + JSString *str; + + JS_ASSERT(flags & JSITER_ENUMERATE); + JS_ASSERT(JSVAL_TO_PRIVATE(iterobj->slots[JSSLOT_CLASS]) == + &js_IteratorClass); + + obj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PARENT]); + origobj = JSVAL_TO_OBJECT(iterobj->slots[JSSLOT_PROTO]); + state = iterobj->slots[JSSLOT_ITER_STATE]; + if (JSVAL_IS_NULL(state)) + goto stop; + + foreach = (flags & JSITER_FOREACH) != 0; +#if JS_HAS_XML_SUPPORT + /* + * Treat an XML object specially only when it starts the prototype chain. + * Otherwise we need to do the usual deleted and shadowed property checks. + */ + if (obj == origobj && OBJECT_IS_XML(cx, obj)) { + if (foreach) { + JSXMLObjectOps *xmlops = (JSXMLObjectOps *) obj->map->ops; + + if (!xmlops->enumerateValues(cx, obj, JSENUMERATE_NEXT, &state, + &id, rval)) { + return JS_FALSE; + } + } else { + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) + return JS_FALSE; + *rval = ID_TO_VALUE(id); + } + iterobj->slots[JSSLOT_ITER_STATE] = state; + if (JSVAL_IS_NULL(state)) + goto stop; + if (foreach) + goto return_key_value; + return JS_TRUE; } - goto bad; +#endif + + restart: + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_NEXT, &state, &id)) + return JS_TRUE; + + iterobj->slots[JSSLOT_ITER_STATE] = state; + if (JSVAL_IS_NULL(state)) { +#if JS_HAS_XML_SUPPORT + if (OBJECT_IS_XML(cx, obj)) { + /* + * We just finished enumerating an XML obj that is present on the + * prototype chain of a non-XML origobj. Stop further prototype + * chain searches because XML objects don't enumerate prototypes. + */ + JS_ASSERT(origobj != obj); + JS_ASSERT(!OBJECT_IS_XML(cx, origobj)); + } else +#endif + { + obj = OBJ_GET_PROTO(cx, obj); + if (obj) { + iterobj->slots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(obj); + if (!OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &state, NULL)) + return JS_FALSE; + iterobj->slots[JSSLOT_ITER_STATE] = state; + if (!JSVAL_IS_NULL(state)) + goto restart; + } + } + goto stop; + } + + /* Skip properties not in obj when looking from origobj. */ + if (!OBJ_LOOKUP_PROPERTY(cx, origobj, id, &obj2, &prop)) + return JS_FALSE; + if (prop) + OBJ_DROP_PROPERTY(cx, obj2, prop); + + /* + * If the id was deleted, or found in a prototype object or an unrelated + * object (specifically, not in an inner object for obj), skip it. This + * step means that all OBJ_LOOKUP_PROPERTY implementations must return an + * object further along on the prototype chain, or else possibly an object + * returned by the JSExtendedClass.outerObject optional hook. + */ + if (!prop) + goto restart; + if (obj != obj2) { + cond = JS_FALSE; + clasp = OBJ_GET_CLASS(cx, obj2); + if (clasp->flags & JSCLASS_IS_EXTENDED) { + xclasp = (JSExtendedClass *) clasp; + cond = xclasp->outerObject && + xclasp->outerObject(cx, obj2) == obj; + } + if (!cond) + goto restart; + } + + if (foreach) { + /* Get property querying the original object. */ + if (!OBJ_GET_PROPERTY(cx, origobj, id, rval)) + return JS_FALSE; + goto return_key_value; + } + + /* Make rval a string for uniformity and compatibility. */ + if (JSID_IS_ATOM(id)) { + *rval = ATOM_KEY(JSID_TO_ATOM(id)); + } +#if JS_HAS_XML_SUPPORT + else if (JSID_IS_OBJECT(id)) { + str = js_ValueToString(cx, OBJECT_JSID_TO_JSVAL(id)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + } +#endif + else { + str = js_NumberToString(cx, (jsdouble)JSID_TO_INT(id)); + if (!str) + return JS_FALSE; + *rval = STRING_TO_JSVAL(str); + } + return JS_TRUE; + + return_key_value: + JS_ASSERT(foreach); + if (flags & JSITER_KEYVALUE) { + if (!NewKeyValuePair(cx, id, *rval, rval)) + return JS_FALSE; + } + return JS_TRUE; + + stop: + JS_ASSERT(iterobj->slots[JSSLOT_ITER_STATE] == JSVAL_NULL); + *rval = JSVAL_HOLE; + return JS_TRUE; } JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags, - jsid *idp, jsval *rval) +js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval) { - JSBool unlock; - JSObject *obj; - JSScope *scope; - JSScopeProperty *sprop; - jsval fval; - JSFunction *fun; - const jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); + uintN flags; - /* Fastest path for repeated call from for-in loop bytecode. */ - if (flags & JSITER_ENUMERATE) { - JS_ASSERT(OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass); - if (!iterator_next(cx, iterobj, 0, NULL, rval) || - !CheckKeyValueReturn(cx, flags, idp, rval)) { - return JS_FALSE; - } - return JS_TRUE; - } + /* Fast path for native iterators */ + if (OBJ_GET_CLASS(cx, iterobj) == &js_IteratorClass) { + flags = JSVAL_TO_INT(OBJ_GET_SLOT(cx, iterobj, JSSLOT_ITER_FLAGS)); + if (flags & JSITER_ENUMERATE) + return CallEnumeratorNext(cx, iterobj, flags, rval); - /* Fast path for native iterator with unoverridden .next() method. */ - unlock = JS_TRUE; - obj = iterobj; - JS_LOCK_OBJ(cx, obj); - scope = OBJ_SCOPE(obj); - sprop = NULL; - - while (LOCKED_OBJ_GET_CLASS(obj) == &js_IteratorClass) { - obj = scope->object; - sprop = SCOPE_GET_PROPERTY(scope, id); - if (sprop) - break; - obj = LOCKED_OBJ_GET_PROTO(obj); - if (!obj) - break; - JS_UNLOCK_SCOPE(cx, scope); - scope = OBJ_SCOPE(obj); - JS_LOCK_SCOPE(cx, scope); - } - - if (sprop && SPROP_HAS_VALID_SLOT(sprop, scope)) { /* - * Unlock scope as soon as we fetch fval, and clear the unlock flag in - * case we do not return early after setting cx->cachedIterObj. + * Call next directly as all the methods of the native iterator are + * read-only and permanent. */ - fval = LOCKED_OBJ_GET_SLOT(obj, sprop->slot); - JS_UNLOCK_SCOPE(cx, scope); - unlock = JS_FALSE; - if (VALUE_IS_FUNCTION(cx, fval)) { - fun = (JSFunction *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(fval)); - if (!FUN_INTERPRETED(fun) && fun->u.n.native == iterator_next) { - if (!iterator_next(cx, iterobj, 0, NULL, rval) || - !CheckKeyValueReturn(cx, flags, idp, rval)) { - return JS_FALSE; - } - return JS_TRUE; + if (!IteratorNextImpl(cx, iterobj, rval)) + return JS_FALSE; + } else { + jsid id = ATOM_TO_JSID(cx->runtime->atomState.nextAtom); + + if (!JS_GetMethodById(cx, iterobj, id, &iterobj, rval)) + return JS_FALSE; + if (!js_InternalCall(cx, iterobj, *rval, 0, NULL, rval)) { + /* Check for StopIteration. */ + if (!cx->throwing || + JSVAL_IS_PRIMITIVE(cx->exception) || + OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(cx->exception)) + != &js_StopIterationClass) { + return JS_FALSE; } + + /* Inline JS_ClearPendingException(cx). */ + cx->throwing = JS_FALSE; + cx->exception = JSVAL_VOID; + *rval = JSVAL_HOLE; + return JS_TRUE; } } - if (unlock) - JS_UNLOCK_SCOPE(cx, scope); - - return JS_GetMethodById(cx, iterobj, id, &iterobj, &fval) && - js_InternalCall(cx, iterobj, fval, 0, NULL, rval) && - CheckKeyValueReturn(cx, flags, idp, rval); + return JS_TRUE; } static JSBool @@ -550,17 +648,6 @@ JSClass js_StopIterationClass = { NULL, NULL }; -JSBool -js_ThrowStopIteration(JSContext *cx, JSObject *obj) -{ - jsval v; - - JS_ASSERT(!JS_IsExceptionPending(cx)); - if (js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_StopIteration), &v)) - JS_SetPendingException(cx, v); - return JS_FALSE; -} - #if JS_HAS_GENERATORS static void diff --git a/mozilla/js/src/jsiter.h b/mozilla/js/src/jsiter.h index 9c41409e870..1a99b6b06f3 100644 --- a/mozilla/js/src/jsiter.h +++ b/mozilla/js/src/jsiter.h @@ -49,44 +49,27 @@ #define JSITER_FOREACH 0x2 /* return [key, value] pair rather than key */ #define JSITER_KEYVALUE 0x4 /* destructuring for-in wants [key, value] */ -extern JSBool -js_NewNativeIterator(JSContext *cx, JSObject *obj, uintN flags, jsval *vp); - -extern uintN -js_GetNativeIteratorFlags(JSContext *cx, JSObject *iterobj); - extern void js_CloseNativeIterator(JSContext *cx, JSObject *iterobj); extern void js_CloseIteratorState(JSContext *cx, JSObject *iterobj); -extern JSObject * -js_ValueToIterator(JSContext *cx, jsval v, uintN flags); - /* - * Given iterobj, call iterobj.next(). - * - * If idp is non-null, we are enumerating an iterable other than iterobj using - * a for-in or for-each-in loop, so we must return the id of the property being - * enumerated for shadowing and deleted-property checks. In this case, *rval - * will be either the key or the value from a [key, value] pair returned by the - * iterator, depending on whether for-in or for-each-in is being used. - * - * But if idp is null, then we are iterating iterobj itself -- the iterator is - * the right operand of 'in' in the for-in or for-each-in loop -- and we should - * pass its return value back unchanged. + * 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 + * iterator will never be exposed to scripts. */ extern JSBool -js_CallIteratorNext(JSContext *cx, JSObject *iterobj, uintN flags, - jsid *idp, jsval *rval); - -#define VALUE_IS_STOP_ITERATION(cx,v) \ - (!JSVAL_IS_PRIMITIVE(v) && \ - OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_StopIterationClass) +js_ValueToIterator(JSContext *cx, uintN flags, jsval *vp); +/* + * Given iterobj, call iterobj.next(). If the iterator stopped, set *rval to + * JSVAL_HOLE. Otherwise set it to the result of the next call. + */ extern JSBool -js_ThrowStopIteration(JSContext *cx, JSObject *obj); +js_CallIteratorNext(JSContext *cx, JSObject *iterobj, jsval *rval); #if JS_HAS_GENERATORS diff --git a/mozilla/js/src/jsopcode.c b/mozilla/js/src/jsopcode.c index b4f6f128eaf..3107a9e7198 100644 --- a/mozilla/js/src/jsopcode.c +++ b/mozilla/js/src/jsopcode.c @@ -1534,7 +1534,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) static const char exception_cookie[] = "/*EXCEPTION*/"; static const char retsub_pc_cookie[] = "/*RETSUB_PC*/"; - static const char iter_cookie[] = "/*ITER*/"; static const char forelem_cookie[] = "/*FORELEM*/"; static const char with_cookie[] = "/*WITH*/"; static const char dot_format[] = "%s.%s"; @@ -1925,16 +1924,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_STARTITER: - if (ss->inArrayInit) { - ss->offsets[ss->top++] = ss->sprinter.offset; - ss->offsets[ss->top++] = ss->sprinter.offset; - ss->opcodes[ss->top-1] = ss->opcodes[ss->top-2] = op; - break; - } - todo = Sprint(&ss->sprinter, iter_cookie); - if (todo < 0 || !PushOff(ss, todo, op)) - return NULL; - /* FALL THROUGH */ + break; case JSOP_PUSH: #if JS_HAS_DESTRUCTURING @@ -2256,15 +2246,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (sn && SN_TYPE(sn) == SRC_HIDDEN) break; (void) PopOff(ss, op); - if (ss->inArrayInit) { - ss->top -= 2; - break; - } - (void) PopOff(ss, op); - if (op == JSOP_ENDITER) { - rval = POP_STR(); - LOCAL_ASSERT(!strcmp(rval, iter_cookie)); - } + if (op == JSOP_POP2) + (void) PopOff(ss, op); break; case JSOP_ENTERWITH: diff --git a/mozilla/js/src/jsopcode.tbl b/mozilla/js/src/jsopcode.tbl index 29c90718dc9..7dae831211a 100644 --- a/mozilla/js/src/jsopcode.tbl +++ b/mozilla/js/src/jsopcode.tbl @@ -231,11 +231,15 @@ OPDEF(JSOP_VARINC, 100,"varinc", NULL, 3, 0, 1, 15, JOF_QVAR | 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. + */ +OPDEF(JSOP_FORIN, 103,"forin", NULL, 1, 1, 1, 0, JOF_BYTE) + /* ECMA-compliant for/in ops. */ -OPDEF(JSOP_TOOBJECT, 103,"toobject", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_FORNAME, 104,"forname", NULL, 3, 0, 1, 19, JOF_CONST|JOF_NAME|JOF_FOR) OPDEF(JSOP_FORPROP, 105,"forprop", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP|JOF_FOR) -OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 2, 4, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) +OPDEF(JSOP_FORELEM, 106,"forelem", NULL, 1, 1, 3, 18, JOF_BYTE |JOF_ELEM|JOF_FOR) OPDEF(JSOP_POP2, 107,"pop2", NULL, 1, 2, 0, 0, JOF_BYTE) /* ECMA-compliant assignment ops. */ @@ -399,7 +403,7 @@ OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_CONST) OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_CONST) OPDEF(JSOP_GETMETHOD, 184,"getmethod", NULL, 3, 1, 1, 18, JOF_CONST|JOF_PROP) OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) -OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_FOREACH, 186,"foreach", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL) /* @@ -454,13 +458,13 @@ OPDEF(JSOP_FORLOCAL, 207,"forlocal", NULL, 3, 0, 1, 19, JOF_LOCAL| /* * Iterator, generator, and array comprehension support. */ -OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 2, 0, JOF_BYTE) -OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 3, 0, 0, JOF_BYTE) +OPDEF(JSOP_STARTITER, 208,"startiter", NULL, 1, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_ENDITER, 209,"enditer", NULL, 1, 1, 0, 0, JOF_BYTE) 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, 0, 0, 0, JOF_BYTE) +OPDEF(JSOP_FOREACHKEYVAL, 213,"foreachkeyval",NULL, 1, 1, 1, 0, JOF_BYTE) /* * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). @@ -472,9 +476,3 @@ OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|J * which must be moved down when the block pops. */ OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, 0, 0, 1, JOF_UINT16) - -/* - * This opcode prefixes one of JSOP_FOR{NAME,ARG,VAR,LOCAL,PROP,ELEM} in a - * standard for-in loop. See also JSOP_FOREACH and JSOP_FOREACHKEYVAL. - */ -OPDEF(JSOP_FORIN, 216,"forin", NULL, 1, 0, 0, 0, JOF_BYTE) diff --git a/mozilla/js/src/jsxdrapi.h b/mozilla/js/src/jsxdrapi.h index 7863f69d75b..c74db24aa43 100644 --- a/mozilla/js/src/jsxdrapi.h +++ b/mozilla/js/src/jsxdrapi.h @@ -200,7 +200,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id); * 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 - 4) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 5) /* * Library-private functions.