Bug 443071 - Assertion failure with "for (;;[]=[])" (r=mrbkap).

This commit is contained in:
Brendan Eich 2008-11-07 14:01:11 -08:00
parent fa14a92fe7
commit 40acf84539
2 changed files with 85 additions and 46 deletions

View File

@ -3850,6 +3850,10 @@ EmitFunctionDefNop(JSContext *cx, JSCodeGenerator *cg, uintN index)
js_Emit1(cx, cg, JSOP_NOP) >= 0;
}
/* See the SRC_FOR source note offsetBias comments later in this file. */
JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
JS_STATIC_ASSERT(JSOP_POP_LENGTH == 1);
JSBool
js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
{
@ -4288,7 +4292,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
#endif
(type == TOK_VAR && !pn3->pn_expr)) &&
js_NewSrcNote2(cx, cg, SRC_DECL,
type == TOK_VAR
(type == TOK_VAR)
? SRC_DECL_VAR
: SRC_DECL_LET) < 0) {
return JS_FALSE;
@ -4441,11 +4445,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
}
cg->treeContext.flags &= ~TCF_IN_FOR_INIT;
}
/*
* NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
* Use tmp to hold the biased srcnote "top" offset, which differs
* from the top local variable by the length of the JSOP_GOTO{,X}
* emitted in between tmp and top if this loop has a condition.
*/
noteIndex = js_NewSrcNote(cx, cg, SRC_FOR);
if (noteIndex < 0 ||
js_Emit1(cx, cg, op) < 0) {
if (noteIndex < 0 || js_Emit1(cx, cg, op) < 0)
return JS_FALSE;
}
tmp = CG_OFFSET(cg);
if (pn2->pn_kid2) {
/* Goto the loop condition, which branches back to iterate. */
@ -4453,6 +4463,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (jmp < 0)
return JS_FALSE;
}
top = CG_OFFSET(cg);
SET_STATEMENT_TOP(&stmtInfo, top);
@ -4463,7 +4474,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
/* Set the second note offset so we can find the update part. */
JS_ASSERT(noteIndex != -1);
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1,
CG_OFFSET(cg) - top)) {
CG_OFFSET(cg) - tmp)) {
return JS_FALSE;
}
@ -4483,12 +4494,12 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return JS_FALSE;
}
#endif
if (op == JSOP_POP) {
if (!js_EmitTree(cx, cg, pn3))
return JS_FALSE;
if (js_Emit1(cx, cg, op) < 0)
return JS_FALSE;
}
if (op == JSOP_POP && !js_EmitTree(cx, cg, pn3))
return JS_FALSE;
/* Always emit the POP or NOP, to help the decompiler. */
if (js_Emit1(cx, cg, op) < 0)
return JS_FALSE;
/* Restore the absolute line number for source note readers. */
off = (ptrdiff_t) pn->pn_pos.end.lineno;
@ -4501,7 +4512,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
/* Set the first note offset so we can find the loop condition. */
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0,
CG_OFFSET(cg) - top)) {
CG_OFFSET(cg) - tmp)) {
return JS_FALSE;
}
@ -4516,7 +4527,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
/* The third note offset helps us find the loop-closing jump. */
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2,
CG_OFFSET(cg) - top)) {
CG_OFFSET(cg) - tmp)) {
return JS_FALSE;
}
@ -6356,7 +6367,10 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
return ok;
}
/* XXX get rid of offsetBias, it's used only by SRC_FOR and SRC_DECL */
/*
* We should try to get rid of offsetBias (always 0 or 1, where 1 is
* JSOP_{NOP,POP}_LENGTH), which is used only by SRC_FOR and SRC_DECL.
*/
JS_FRIEND_DATA(JSSrcNoteSpec) js_SrcNoteSpec[] = {
{"null", 0, 0, 0},
{"if", 0, 0, 0},

View File

@ -2090,8 +2090,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
rval = "";
do_forloop:
JS_ASSERT(SN_TYPE(sn) == SRC_FOR);
/* Skip the JSOP_NOP or JSOP_POP bytecode. */
pc++;
pc += JSOP_NOP_LENGTH;
/* Get the cond, next, and loop-closing tail offsets. */
cond = js_GetSrcNoteOffset(sn, 0);
@ -2102,13 +2104,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* If this loop has a condition, then pc points at a goto
* targeting the condition.
*/
pc2 = pc;
if (cond != tail) {
LOCAL_ASSERT(*pc == JSOP_GOTO || *pc == JSOP_GOTOX);
pc += (*pc == JSOP_GOTO)
? JSOP_GOTO_LENGTH
: JSOP_GOTOX_LENGTH;
pc2 += (*pc == JSOP_GOTO) ? JSOP_GOTO_LENGTH : JSOP_GOTOX_LENGTH;
}
LOCAL_ASSERT(tail == -GetJumpOffset(pc+tail, pc+tail));
LOCAL_ASSERT(tail + GetJumpOffset(pc+tail, pc+tail) == pc2 - pc);
/* Print the keyword and the possibly empty init-part. */
js_printf(jp, "\tfor (%s;", rval);
@ -2123,16 +2124,29 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
js_puts(jp, ";");
if (next != cond) {
/* Decompile the loop updater. */
DECOMPILE_CODE(pc + next,
cond - next - JSOP_POP_LENGTH);
js_printf(jp, " %s", POP_STR());
/*
* Decompile the loop updater. It may end in a JSOP_POP
* that we skip; or in a JSOP_POPN that we do not skip,
* followed by a JSOP_NOP (skipped as if it's a POP).
* We cope with the difference between these two cases
* by checking for stack imbalance and popping if there
* is an rval.
*/
uintN saveTop = ss->top;
DECOMPILE_CODE(pc + next, cond - next - JSOP_POP_LENGTH);
LOCAL_ASSERT(ss->top - saveTop <= 1U);
rval = (ss->top == saveTop)
? ss->sprinter.base + ss->sprinter.offset
: POP_STR();
js_printf(jp, " %s", rval);
}
/* Do the loop body. */
js_printf(jp, ") {\n");
jp->indent += 4;
DECOMPILE_CODE(pc, next);
next -= pc2 - pc;
DECOMPILE_CODE(pc2, next);
jp->indent -= 4;
js_printf(jp, "\t}\n");
@ -2293,17 +2307,28 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
return NULL;
/*
* Kill newtop before the end_groupassignment: label by
* retracting/popping early. Control will either jump to
* do_forloop: or do_letheadbody: or else break from our
* case JSOP_POPN: after the switch (*pc2) below.
* If this is an empty group assignment, we have no stack
* budget into which we can push our result string. Adjust
* ss->sprinter.offset so that our consumer can find the
* empty group assignment decompilation.
*/
if (newtop < oldtop) {
if (newtop == oldtop) {
ss->sprinter.offset = todo;
} else {
/*
* Kill newtop before the end_groupassignment: label by
* retracting/popping early. Control will either jump
* to do_forloop: or do_letheadbody: or else break from
* our case JSOP_POPN: after the switch (*pc2) below.
*/
LOCAL_ASSERT(newtop < oldtop);
ss->sprinter.offset = GetOff(ss, newtop);
ss->top = newtop;
}
end_groupassignment:
LOCAL_ASSERT(*pc == JSOP_POPN);
/*
* Thread directly to the next opcode if we can, to handle
* the special cases of a group assignment in the first or
@ -2318,15 +2343,15 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
rval = OFF2STR(&ss->sprinter, todo);
todo = -2;
pc2 = pc + oplen;
switch (*pc2) {
case JSOP_NOP:
/* First part of for(;;) or let block/expr head. */
if (*pc2 == JSOP_NOP) {
sn = js_GetSrcNote(jp->script, pc2);
if (sn) {
if (SN_TYPE(sn) == SRC_FOR) {
op = JSOP_NOP;
pc = pc2;
goto do_forloop;
}
if (SN_TYPE(sn) == SRC_DECL) {
if (ss->top == StackDepth(jp->script)) {
/*
@ -2334,33 +2359,34 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* in the head of a let whose body block
* is also empty.
*/
pc = pc2 + 1;
pc = pc2 + JSOP_NOP_LENGTH;
len = js_GetSrcNoteOffset(sn, 0);
LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCK);
js_printf(jp, "\tlet (%s) {\n", rval);
js_printf(jp, "\t}\n");
goto end_popn;
break;
}
todo = SprintCString(&ss->sprinter, rval);
if (todo < 0 || !PushOff(ss, todo, JSOP_NOP))
return NULL;
op = JSOP_POP;
pc = pc2 + 1;
pc = pc2 + JSOP_NOP_LENGTH;
goto do_letheadbody;
}
}
break;
case JSOP_GOTO:
case JSOP_GOTOX:
/* Third part of for(;;) loop head. */
cond = GetJumpOffset(pc2, pc2);
sn = js_GetSrcNote(jp->script, pc2 + cond - 1);
if (sn && SN_TYPE(sn) == SRC_FOR) {
} else {
/*
* An unnannotated NOP following a POPN must be the
* third part of for(;;) loop head. If the POPN's
* immediate operand is 0, then we may have no slot
* on the sprint-stack in which to push our result
* string. In this case the result can be recovered
* at ss->sprinter.base + ss->sprinter.offset.
*/
if (GET_UINT16(pc) == 0)
break;
todo = SprintCString(&ss->sprinter, rval);
saveop = JSOP_NOP;
}
break;
}
/*
@ -2369,7 +2395,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
*/
if (todo == -2)
js_printf(jp, "\t%s;\n", rval);
end_popn:
break;
}
#endif