Bug 441479 - for-in loops should use one backward branch (with downward goto on entry; r=mrbkap).
This commit is contained in:
@@ -82,6 +82,7 @@
|
||||
#undef OPDEF
|
||||
|
||||
static const char js_incop_strs[][3] = {"++", "--"};
|
||||
static const char js_for_each_str[] = "for each";
|
||||
|
||||
const JSCodeSpec js_CodeSpec[] = {
|
||||
#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
|
||||
@@ -1717,7 +1718,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
{
|
||||
JSContext *cx;
|
||||
JSPrinter *jp, *jp2;
|
||||
jsbytecode *startpc, *endpc, *pc2, *done, *forelem_tail, *forelem_done;
|
||||
jsbytecode *startpc, *endpc, *pc2, *done;
|
||||
ptrdiff_t tail, todo, len, oplen, cond, next;
|
||||
JSOp op, lastop, saveop;
|
||||
const JSCodeSpec *cs;
|
||||
@@ -1748,6 +1749,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
static const char preindex_format[] = "%s%s[%s]";
|
||||
static const char postindex_format[] = "%s[%s]%s";
|
||||
static const char ss_format[] = "%s%s";
|
||||
static const char sss_format[] = "%s%s%s";
|
||||
|
||||
/* Argument and variables decompilation uses the following to share code. */
|
||||
JS_STATIC_ASSERT(ARGNO_LEN == SLOTNO_LEN);
|
||||
@@ -1849,7 +1851,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
jp = ss->printer;
|
||||
startpc = pc;
|
||||
endpc = (nb < 0) ? jp->script->code + jp->script->length : pc + nb;
|
||||
forelem_tail = forelem_done = NULL;
|
||||
tail = -1;
|
||||
todo = -2; /* NB: different from Sprint() error return. */
|
||||
saveop = JSOP_NOP;
|
||||
@@ -2872,34 +2873,36 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
rval = POP_STR();
|
||||
|
||||
/*
|
||||
* Skip down over iterables left stacked by JSOP_FOR* until
|
||||
* we hit a block-local or the new Array initialiser (empty
|
||||
* destructuring patterns yield zero-count blocks).
|
||||
* Skip the for loop head stacked by JSOP_FORLOCAL until we hit
|
||||
* a block local slot (note empty destructuring patterns result
|
||||
* in unit-count blocks).
|
||||
*/
|
||||
pos = ss->top;
|
||||
while ((op = (JSOp) ss->opcodes[--pos]) != JSOP_ENTERBLOCK &&
|
||||
op != JSOP_NEWINIT) {
|
||||
if (pos == 0)
|
||||
while (pos != 0) {
|
||||
op = (JSOp) ss->opcodes[--pos];
|
||||
if (op != JSOP_FORLOCAL)
|
||||
break;
|
||||
}
|
||||
JS_ASSERT(op == JSOP_ENTERBLOCK);
|
||||
|
||||
/*
|
||||
* Make forpos index the space before the left-most |for| in
|
||||
* the single string of accumulated |for| heads and optional
|
||||
* Here, forpos must index the space before the left-most |for|
|
||||
* in the single string of accumulated |for| heads and optional
|
||||
* final |if (condition)|.
|
||||
*/
|
||||
forpos = pos + (op == JSOP_ENTERBLOCK || op == JSOP_NEWINIT);
|
||||
forpos = pos + 1;
|
||||
LOCAL_ASSERT(forpos < ss->top);
|
||||
|
||||
/*
|
||||
* Now skip down over the block's local slots, if any. There
|
||||
* may be no locals for an empty destructuring pattern.
|
||||
* Now move pos downward over the block's local slots. Even an
|
||||
* empty destructuring pattern has one (dummy) local.
|
||||
*/
|
||||
while (ss->opcodes[pos] == JSOP_ENTERBLOCK) {
|
||||
if (pos == 0)
|
||||
break;
|
||||
--pos;
|
||||
}
|
||||
JS_ASSERT_IF(saveop == JSOP_ARRAYPUSH, pos == GET_UINT16(pc));
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (saveop == JSOP_YIELD) {
|
||||
@@ -2936,8 +2939,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
lval = OFF2STR(&ss->sprinter, start);
|
||||
RETRACT(&ss->sprinter, lval);
|
||||
|
||||
todo = Sprint(&ss->sprinter, "%s%s%.*s",
|
||||
lval, rval, rval - xval, xval);
|
||||
todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval);
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
ss->offsets[pos] = todo;
|
||||
@@ -2963,6 +2965,65 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
case JSOP_GOTOX:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
|
||||
case SRC_FOR_IN:
|
||||
/*
|
||||
* The loop back-edge carries +1 stack balance, for the
|
||||
* flag processed by JSOP_IFNE. We do not decompile the
|
||||
* JSOP_IFNE, and instead pass the left-hand side of 'in'
|
||||
* along the loop edge in this pushed stack slot.
|
||||
*/
|
||||
cond = GetJumpOffset(pc, pc);
|
||||
next = js_GetSrcNoteOffset(sn, 0);
|
||||
tail = js_GetSrcNoteOffset(sn, 1);
|
||||
DECOMPILE_CODE(pc + cond, tail - cond);
|
||||
DECOMPILE_CODE(pc + oplen, next - oplen);
|
||||
if (ss->inArrayInit || ss->inGenExp) {
|
||||
lval = POP_STR();
|
||||
rval = POP_STR();
|
||||
if (ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) {
|
||||
ss->sprinter.offset -= PAREN_SLOP;
|
||||
if (Sprint(&ss->sprinter, " %s (%s in %s)",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
lval, rval) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do not AddParentSlop here, as we will push the
|
||||
* top-most offset again, which will add paren slop
|
||||
* for us. We must push to balance the stack budget
|
||||
* when nesting for heads in a comprehension.
|
||||
*/
|
||||
todo = ss->offsets[ss->top - 1];
|
||||
} else {
|
||||
LOCAL_ASSERT(ss->opcodes[ss->top - 1] == JSOP_ENTERBLOCK);
|
||||
todo = Sprint(&ss->sprinter, " %s (%s in %s)",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
lval, rval);
|
||||
}
|
||||
if (todo < 0 || !PushOff(ss, todo, JSOP_FORLOCAL))
|
||||
return NULL;
|
||||
DECOMPILE_CODE(pc + next, cond - next);
|
||||
} else {
|
||||
/*
|
||||
* As above, rval or an extension of it must remain
|
||||
* stacked during loop body decompilation.
|
||||
*/
|
||||
lval = POP_STR();
|
||||
rval = GetStr(ss, ss->top - 1);
|
||||
js_printf(jp, "\t%s (%s in %s) {\n",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
lval, rval);
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc + next, cond - next);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
}
|
||||
pc += tail;
|
||||
LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
|
||||
len = js_CodeSpec[*pc].length;
|
||||
break;
|
||||
|
||||
case SRC_WHILE:
|
||||
cond = GetJumpOffset(pc, pc);
|
||||
tail = js_GetSrcNoteOffset(sn, 0);
|
||||
@@ -3153,31 +3214,34 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
goto do_logical_connective;
|
||||
|
||||
case JSOP_FORARG:
|
||||
atom = GetArgOrVarAtom(jp, GET_ARGNO(pc));
|
||||
LOCAL_ASSERT(atom);
|
||||
goto do_fornameinloop;
|
||||
sn = NULL;
|
||||
i = GET_ARGNO(pc);
|
||||
goto do_forvarslot;
|
||||
|
||||
case JSOP_FORCONST:
|
||||
case JSOP_FORLOCAL:
|
||||
if (IsVarSlot(jp, pc, &i)) {
|
||||
atom = GetArgOrVarAtom(jp, i);
|
||||
LOCAL_ASSERT(atom);
|
||||
goto do_fornameinloop;
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (!IsVarSlot(jp, pc, &i)) {
|
||||
JS_ASSERT(op == JSOP_FORLOCAL);
|
||||
todo = Sprint(&ss->sprinter, ss_format, VarPrefix(sn), GetStr(ss, i));
|
||||
break;
|
||||
}
|
||||
JS_ASSERT(op == JSOP_FORLOCAL);
|
||||
lval = GetStr(ss, i);
|
||||
atom = NULL;
|
||||
goto do_forlvalinloop;
|
||||
|
||||
do_forvarslot:
|
||||
atom = GetArgOrVarAtom(jp, i);
|
||||
LOCAL_ASSERT(atom);
|
||||
todo = SprintCString(&ss->sprinter, VarPrefix(sn));
|
||||
if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case JSOP_FORNAME:
|
||||
LOAD_ATOM(0);
|
||||
|
||||
do_fornameinloop:
|
||||
lval = "";
|
||||
do_forlvalinloop:
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
xval = NULL;
|
||||
goto do_forinloop;
|
||||
todo = SprintCString(&ss->sprinter, VarPrefix(sn));
|
||||
if (todo < 0 || !QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case JSOP_FORPROP:
|
||||
xval = NULL;
|
||||
@@ -3190,121 +3254,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
atom = NULL;
|
||||
}
|
||||
lval = POP_STR();
|
||||
sn = NULL;
|
||||
|
||||
do_forinloop:
|
||||
pc += oplen;
|
||||
LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
|
||||
oplen = js_CodeSpec[*pc].length;
|
||||
len = GetJumpOffset(pc, pc);
|
||||
sn2 = js_GetSrcNote(jp->script, pc);
|
||||
tail = js_GetSrcNoteOffset(sn2, 0);
|
||||
|
||||
do_forinhead:
|
||||
if (!atom && xval) {
|
||||
/*
|
||||
* If xval is not a dummy empty string, we have to strdup
|
||||
* it to save it from being clobbered by the first Sprint
|
||||
* below. Standard dumb decompiler operating procedure!
|
||||
*/
|
||||
if (*xval == '\0') {
|
||||
xval = NULL;
|
||||
} else {
|
||||
xval = JS_strdup(cx, xval);
|
||||
if (!xval)
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#if JS_HAS_XML_SUPPORT
|
||||
if (foreach) {
|
||||
foreach = JS_FALSE;
|
||||
todo = Sprint(&ss->sprinter, "for %s (%s%s",
|
||||
js_each_str, VarPrefix(sn), lval);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
todo = Sprint(&ss->sprinter, "for (%s%s",
|
||||
VarPrefix(sn), lval);
|
||||
}
|
||||
if (atom) {
|
||||
if (*lval && SprintPut(&ss->sprinter, ".", 1) < 0)
|
||||
return NULL;
|
||||
xval = QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0);
|
||||
if (!xval)
|
||||
return NULL;
|
||||
} else if (xval) {
|
||||
LOCAL_ASSERT(*xval != '\0');
|
||||
ok = (Sprint(&ss->sprinter,
|
||||
(JOF_OPMODE(lastop) == JOF_XMLNAME)
|
||||
? ".%s"
|
||||
: "[%s]",
|
||||
xval)
|
||||
>= 0);
|
||||
JS_free(cx, (char *)xval);
|
||||
if (!ok)
|
||||
return NULL;
|
||||
}
|
||||
todo = Sprint(&ss->sprinter, ss_format, lval, *lval ? "." : "");
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
|
||||
lval = OFF2STR(&ss->sprinter, todo);
|
||||
rval = GetStr(ss, ss->top-1);
|
||||
RETRACT(&ss->sprinter, rval);
|
||||
if (ss->inArrayInit || ss->inGenExp) {
|
||||
if (ss->top > 1 &&
|
||||
(js_CodeSpec[ss->opcodes[ss->top-2]].format &
|
||||
JOF_FOR)) {
|
||||
ss->sprinter.offset -= PAREN_SLOP;
|
||||
}
|
||||
todo = Sprint(&ss->sprinter, " %s in %s)", lval, rval);
|
||||
if (todo < 0)
|
||||
return NULL;
|
||||
ss->offsets[ss->top-1] = todo;
|
||||
ss->opcodes[ss->top-1] = op;
|
||||
AddParenSlop(ss);
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
} else {
|
||||
js_printf(jp, "\t%s in %s) {\n", lval, rval);
|
||||
jp->indent += 4;
|
||||
DECOMPILE_CODE(pc + oplen, tail - oplen);
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
}
|
||||
todo = -2;
|
||||
if (!QuoteString(&ss->sprinter, ATOM_TO_STRING(atom), 0))
|
||||
return NULL;
|
||||
break;
|
||||
|
||||
case JSOP_FORELEM:
|
||||
pc++;
|
||||
LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX);
|
||||
len = js_CodeSpec[*pc].length;
|
||||
|
||||
/*
|
||||
* Arrange for the JSOP_ENUMELEM case to set tail for use by
|
||||
* do_forinhead: code that uses on it to find the loop-closing
|
||||
* jump (whatever its format, normal or extended), in order to
|
||||
* bound the recursively decompiled loop body.
|
||||
*/
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
LOCAL_ASSERT(!forelem_tail);
|
||||
forelem_tail = pc + js_GetSrcNoteOffset(sn, 0);
|
||||
|
||||
/*
|
||||
* This gets a little wacky. Only the length of the for loop
|
||||
* body PLUS the element-indexing expression is known here, so
|
||||
* we pass the after-loop pc to the JSOP_ENUMELEM case, which
|
||||
* is immediately below, to decompile that helper bytecode via
|
||||
* the 'forelem_done' local.
|
||||
*
|
||||
* Since a for..in loop can't nest in the head of another for
|
||||
* loop, we can use forelem_{tail,done} singletons to remember
|
||||
* state from JSOP_FORELEM to JSOP_ENUMELEM, thence (via goto)
|
||||
* to label do_forinhead.
|
||||
*/
|
||||
LOCAL_ASSERT(!forelem_done);
|
||||
forelem_done = pc + GetJumpOffset(pc, pc);
|
||||
|
||||
/* Our net stack balance after forelem;ifeq is +1. */
|
||||
LOCAL_ASSERT(pc[1] == JSOP_IFNE || pc[1] == JSOP_IFNEX);
|
||||
todo = SprintCString(&ss->sprinter, forelem_cookie);
|
||||
break;
|
||||
|
||||
@@ -3325,13 +3283,16 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
op = saveop;
|
||||
rval = POP_STR();
|
||||
LOCAL_ASSERT(strcmp(rval, forelem_cookie) == 0);
|
||||
LOCAL_ASSERT(forelem_tail > pc);
|
||||
tail = forelem_tail - pc;
|
||||
forelem_tail = NULL;
|
||||
LOCAL_ASSERT(forelem_done > pc);
|
||||
len = forelem_done - pc;
|
||||
forelem_done = NULL;
|
||||
goto do_forinhead;
|
||||
if (*xval == '\0') {
|
||||
todo = SprintCString(&ss->sprinter, lval);
|
||||
} else {
|
||||
todo = Sprint(&ss->sprinter,
|
||||
(JOF_OPMODE(lastop) == JOF_XMLNAME)
|
||||
? dot_format
|
||||
: index_format,
|
||||
lval, xval);
|
||||
}
|
||||
break;
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
case JSOP_GETTER:
|
||||
@@ -3363,27 +3324,16 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
rval = POP_STR();
|
||||
|
||||
if (strcmp(rval, forelem_cookie) == 0) {
|
||||
LOCAL_ASSERT(forelem_tail > pc);
|
||||
tail = forelem_tail - pc;
|
||||
forelem_tail = NULL;
|
||||
LOCAL_ASSERT(forelem_done > pc);
|
||||
len = forelem_done - pc;
|
||||
forelem_done = NULL;
|
||||
xval = NULL;
|
||||
atom = NULL;
|
||||
todo = Sprint(&ss->sprinter, ss_format,
|
||||
VarPrefix(sn), lval);
|
||||
|
||||
/*
|
||||
* Null sn if this is a 'for (var [k, v] = i in o)'
|
||||
* loop, because 'var [k, v = i;' has already been
|
||||
* hoisted.
|
||||
*/
|
||||
if (js_GetSrcNoteOffset(sn, 0) == SRC_DECL_VAR)
|
||||
sn = NULL;
|
||||
goto do_forinhead;
|
||||
// Skip POP so the SRC_FOR_IN code can pop for itself.
|
||||
if (*pc == JSOP_POP)
|
||||
len = JSOP_POP_LENGTH;
|
||||
} else {
|
||||
todo = Sprint(&ss->sprinter, "%s%s = %s",
|
||||
VarPrefix(sn), lval, rval);
|
||||
}
|
||||
|
||||
todo = Sprint(&ss->sprinter, "%s%s = %s",
|
||||
VarPrefix(sn), lval, rval);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
@@ -3855,7 +3805,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
if (!rval)
|
||||
return NULL;
|
||||
RETRACT(&ss->sprinter, rval);
|
||||
todo = Sprint(&ss->sprinter, "%s%s%s",
|
||||
todo = Sprint(&ss->sprinter, sss_format,
|
||||
VarPrefix(sn), lval, rval);
|
||||
break;
|
||||
|
||||
@@ -3925,8 +3875,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
outer = jp->script;
|
||||
LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
|
||||
jp->script = inner;
|
||||
if (!Decompile(&ss2, inner->code, inner->length,
|
||||
JSOP_NOP)) {
|
||||
if (!Decompile(&ss2, inner->code, inner->length, JSOP_NOP)) {
|
||||
JS_ARENA_RELEASE(&cx->tempPool, mark);
|
||||
return NULL;
|
||||
}
|
||||
@@ -4390,7 +4339,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
goto do_initprop;
|
||||
}
|
||||
maybeComma = isFirst ? "" : ", ";
|
||||
todo = Sprint(&ss->sprinter, "%s%s%s",
|
||||
todo = Sprint(&ss->sprinter, sss_format,
|
||||
lval,
|
||||
maybeComma,
|
||||
rval);
|
||||
|
||||
Reference in New Issue
Block a user