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
This commit is contained in:
brendan%mozilla.org 2006-10-05 00:19:49 +00:00
parent 25a3c9dfe8
commit 68b5da6d05
10 changed files with 432 additions and 627 deletions

View File

@ -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
}

View File

@ -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;

View File

@ -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.
*

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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.