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:
parent
25a3c9dfe8
commit
68b5da6d05
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user