Bug 460501 - Round-trip change due to "&&" constant-folding leaving extra parens. r=brendan.

This commit is contained in:
Jason Orendorff 2008-10-21 13:35:22 -05:00
parent 121e528b9f
commit e1877796ba
8 changed files with 77 additions and 98 deletions

View File

@ -5720,8 +5720,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
default:
/*
* If useless, just emit JSOP_TRUE; otherwise convert delete foo()
* to foo(), true (a comma expression, requiring SRC_PCDELTA, and
* also JSOP_GROUP for correctly parenthesized decompilation).
* to foo(), true (a comma expression, requiring SRC_PCDELTA).
*/
useful = JS_FALSE;
if (!CheckSideEffects(cx, cg, pn2, &useful))
@ -5743,8 +5742,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!js_SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp-off))
return JS_FALSE;
}
if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
return JS_FALSE;
}
break;
@ -6165,8 +6162,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
if (!js_EmitTree(cx, cg, pn->pn_kid))
return JS_FALSE;
cg->treeContext.flags |= oldflags & TCF_IN_FOR_INIT;
if (js_Emit1(cx, cg, JSOP_GROUP) < 0)
return JS_FALSE;
break;
}

View File

@ -2857,7 +2857,6 @@ js_Interpret(JSContext *cx)
/* No-ops for ease of decompilation. */
ADD_EMPTY_CASE(JSOP_NOP)
ADD_EMPTY_CASE(JSOP_GROUP)
ADD_EMPTY_CASE(JSOP_CONDSWITCH)
ADD_EMPTY_CASE(JSOP_TRY)
ADD_EMPTY_CASE(JSOP_FINALLY)
@ -5137,15 +5136,11 @@ js_Interpret(JSContext *cx)
if (!prop) {
/* Kludge to allow (typeof foo == "undefined") tests. */
endpc = script->code + script->length;
for (pc2 = regs.pc + JSOP_NAME_LENGTH; pc2 < endpc; pc2++) {
op2 = (JSOp)*pc2;
if (op2 == JSOP_TYPEOF) {
PUSH_OPND(JSVAL_VOID);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
if (op2 != JSOP_GROUP)
break;
op2 = (JSOp) regs.pc[JSOP_NAME_LENGTH];
if (op2 == JSOP_TYPEOF) {
PUSH_OPND(JSVAL_VOID);
len = JSOP_NAME_LENGTH;
DO_NEXT_OP(len);
}
goto atom_not_defined;
}
@ -6841,6 +6836,7 @@ js_Interpret(JSContext *cx)
L_JSOP_UNUSED77:
L_JSOP_UNUSED78:
L_JSOP_UNUSED79:
L_JSOP_UNUSED131:
L_JSOP_UNUSED201:
L_JSOP_UNUSED202:
L_JSOP_UNUSED203:

View File

@ -3327,9 +3327,6 @@ Detecting(JSContext *cx, jsbytecode *pc)
}
return JS_FALSE;
case JSOP_GROUP:
break;
default:
/*
* At this point, anything but an extended atom index prefix means

View File

@ -965,10 +965,10 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op)
}
static ptrdiff_t
PopOff(SprintStack *ss, JSOp op)
PopOffPrec(SprintStack *ss, uint8 prec)
{
uintN top;
const JSCodeSpec *cs, *topcs;
const JSCodeSpec *topcs;
ptrdiff_t off;
/* ss->top points to the next free slot; be paranoid about underflow. */
@ -980,8 +980,7 @@ PopOff(SprintStack *ss, JSOp op)
ss->top = --top;
off = GetOff(ss, top);
topcs = &js_CodeSpec[ss->opcodes[top]];
cs = &js_CodeSpec[op];
if (topcs->prec != 0 && topcs->prec < cs->prec) {
if (topcs->prec != 0 && topcs->prec < prec) {
ss->sprinter.offset = ss->offsets[top] = off - 2;
off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off));
} else {
@ -991,14 +990,26 @@ PopOff(SprintStack *ss, JSOp op)
}
static const char *
PopStr(SprintStack *ss, JSOp op)
PopStrPrec(SprintStack *ss, uint8 prec)
{
ptrdiff_t off;
off = PopOff(ss, op);
off = PopOffPrec(ss, prec);
return OFF2STR(&ss->sprinter, off);
}
static ptrdiff_t
PopOff(SprintStack *ss, JSOp op)
{
return PopOffPrec(ss, js_CodeSpec[op].prec);
}
static const char *
PopStr(SprintStack *ss, JSOp op)
{
return PopStrPrec(ss, js_CodeSpec[op].prec);
}
typedef struct TableEntry {
jsval key;
ptrdiff_t offset;
@ -1744,10 +1755,17 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
/*
* Local macros
*/
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
#define DECOMPILE_CODE(pc,nb) if (!Decompile(ss, pc, nb, JSOP_NOP)) return NULL
#define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len])
#define POP_STR() PopStr(ss, op)
#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL)
#define POP_STR_PREC(prec) PopStrPrec(ss, prec)
/*
* Pop a condition expression for if/for/while. JSOP_IFEQ's precedence forces
* extra parens around assignment, which avoids a strict-mode warning.
*/
#define POP_COND_STR() PopStr(ss, JSOP_IFEQ)
/*
* Callers know that ATOM_IS_STRING(atom), and we leave it to the optimizer to
@ -1808,6 +1826,23 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
GET_QUOTE_AND_FMT(qfmt, ufmt, rval); \
JS_END_MACRO
/*
* Per spec, new x(y).z means (new x(y))).z. For example new (x(y).z) must
* decompile with the constructor parenthesized, but new x.z should not. The
* normal rules give x(y).z and x.z identical precedence: both are produced by
* JSOP_GETPROP.
*
* Therefore, we need to know in case JSOP_NEW whether the constructor
* expression contains any unparenthesized function calls. So when building a
* MemberExpression or CallExpression, we set ss->opcodes[n] to JSOP_CALL if
* this is true. x(y).z gets JSOP_CALL, not JSOP_GETPROP.
*/
#define PROPAGATE_CALLNESS() \
JS_BEGIN_MACRO \
if (ss->opcodes[ss->top - 1] == JSOP_CALL) \
saveop = JSOP_CALL; \
JS_END_MACRO
cx = ss->sprinter.context;
JS_CHECK_RECURSION(cx, return NULL);
@ -1986,8 +2021,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
op = (JSOp) pc[oplen];
LOCAL_ASSERT(op != saveop);
}
rval = POP_STR();
lval = POP_STR();
rval = POP_STR_PREC(cs->prec + (!inXML && !!(cs->format & JOF_LEFTASSOC)));
lval = POP_STR_PREC(cs->prec + (!inXML && !(cs->format & JOF_LEFTASSOC)));
if (op != saveop) {
/* Print only the right operand of the assignment-op. */
todo = SprintCString(&ss->sprinter, rval);
@ -2035,7 +2070,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
jp->indent += 4;
DECOMPILE_CODE(pc, tail);
jp->indent -= 4;
js_printf(jp, "\t} while (%s);\n", POP_STR());
js_printf(jp, "\t} while (%s);\n", POP_COND_STR());
pc += tail;
len = js_CodeSpec[*pc].length;
todo = -2;
@ -2071,7 +2106,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
if (cond != tail) {
/* Decompile the loop condition. */
DECOMPILE_CODE(pc + cond, tail - cond);
js_printf(jp, " %s", POP_STR());
js_printf(jp, " %s", POP_COND_STR());
}
/* Need a semicolon whether or not there was a cond. */
@ -2153,44 +2188,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
}
break;
case JSOP_GROUP:
cs = &js_CodeSpec[lastop];
if ((cs->prec != 0 &&
cs->prec <= js_CodeSpec[NEXT_OP(pc)].prec) ||
pc[JSOP_GROUP_LENGTH] == JSOP_NULL ||
pc[JSOP_GROUP_LENGTH] == JSOP_NULLTHIS ||
pc[JSOP_GROUP_LENGTH] == JSOP_DUP ||
pc[JSOP_GROUP_LENGTH] == JSOP_IFEQ ||
pc[JSOP_GROUP_LENGTH] == JSOP_IFNE) {
/*
* Force parens if this JSOP_GROUP forced re-association
* against precedence, or if this is a call or constructor
* expression, or if it is destructured (JSOP_DUP), or if
* it is an if or loop condition test.
*
* This is necessary to handle the operator new grammar,
* by which new x(y).z means (new x(y))).z. For example
* new (x(y).z) must decompile with the constructor
* parenthesized, but normal precedence has JSOP_GETPROP
* (for the final .z) higher than JSOP_NEW. In general,
* if the call or constructor expression is parenthesized,
* we preserve parens.
*/
op = JSOP_NAME;
rval = POP_STR();
todo = SprintCString(&ss->sprinter, rval);
} else {
/*
* Don't explicitly parenthesize -- just fix the top
* opcode so that the auto-parens magic in PopOff can do
* its thing.
*/
LOCAL_ASSERT(ss->top != 0);
ss->opcodes[ss->top-1] = saveop = lastop;
todo = -2;
}
break;
case JSOP_PUSH:
#if JS_HAS_DESTRUCTURING
sn = js_GetSrcNote(jp->script, pc);
@ -2816,6 +2813,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
LOCAL_ASSERT(jp->fun);
fun = jp->fun;
if (fun->flags & JSFUN_EXPR_CLOSURE) {
/* Turn on parens around comma-expression here. */
op = JSOP_SETNAME;
rval = POP_STR();
js_printf(jp, (*rval == '{') ? "(%s)%s" : ss_format,
rval,
@ -2967,8 +2966,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
cond = GetJumpOffset(pc, pc);
tail = js_GetSrcNoteOffset(sn, 0);
DECOMPILE_CODE(pc + cond, tail - cond);
rval = POP_STR();
js_printf(jp, "\twhile (%s) {\n", rval);
js_printf(jp, "\twhile (%s) {\n", POP_COND_STR());
jp->indent += 4;
DECOMPILE_CODE(pc + oplen, cond - oplen);
jp->indent -= 4;
@ -3023,8 +3021,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
switch (sn ? SN_TYPE(sn) : SRC_NULL) {
case SRC_IF:
case SRC_IF_ELSE:
op = JSOP_NOP; /* turn off parens */
rval = POP_STR();
rval = POP_COND_STR();
if (ss->inArrayInit || ss->inGenExp) {
LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF);
ss->sprinter.offset -= PAREN_SLOP;
@ -3467,6 +3464,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
/*
* Special case: new (x(y)(z)) must be parenthesized like so.
* Same for new (x(y).z) -- contrast with new x(y).z.
* See PROPAGATE_CALLNESS.
*/
op = (JSOp) ss->opcodes[ss->top-1];
lval = PopStr(ss,
@ -3535,6 +3533,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
case JSOP_DELPROP:
GET_ATOM_QUOTE_AND_FMT("%s %s[%s]", "%s %s.%s", rval);
op = JSOP_GETPROP;
lval = POP_STR();
todo = Sprint(&ss->sprinter, fmt, js_delete_str, lval, rval);
break;
@ -3542,7 +3541,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
case JSOP_DELELEM:
op = JSOP_NOP; /* turn off parens */
xval = POP_STR();
op = saveop;
op = JSOP_GETPROP;
lval = POP_STR();
if (*xval == '\0')
goto do_delete_lval;
@ -3556,6 +3555,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#if JS_HAS_XML_SUPPORT
case JSOP_DELDESC:
xval = POP_STR();
op = JSOP_GETPROP;
lval = POP_STR();
todo = Sprint(&ss->sprinter, "%s %s..%s",
js_delete_str, lval, xval);
@ -3700,6 +3700,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
do_getprop:
GET_QUOTE_AND_FMT(index_format, dot_format, rval);
do_getprop_lval:
PROPAGATE_CALLNESS();
lval = POP_STR();
todo = Sprint(&ss->sprinter, fmt, lval, rval);
break;
@ -3773,6 +3774,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
op = JSOP_NOP; /* turn off parens */
xval = POP_STR();
op = saveop;
PROPAGATE_CALLNESS();
lval = POP_STR();
if (*xval == '\0') {
todo = Sprint(&ss->sprinter, "%s", lval);
@ -4243,14 +4245,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break;
}
case JSOP_STRICTEQ:
case JSOP_STRICTNE:
rval = POP_STR();
lval = POP_STR();
todo = Sprint(&ss->sprinter, "%s %c== %s",
lval, (op == JSOP_STRICTEQ) ? '=' : '!', rval);
break;
case JSOP_DEFFUN:
LOAD_FUNCTION(0);
todo = -2;
@ -4607,12 +4601,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
case JSOP_ENDFILTER:
rval = POP_STR();
PROPAGATE_CALLNESS();
lval = POP_STR();
todo = Sprint(&ss->sprinter, "%s.(%s)", lval, rval);
break;
case JSOP_DESCENDANTS:
rval = POP_STR();
PROPAGATE_CALLNESS();
lval = POP_STR();
todo = Sprint(&ss->sprinter, "%s..%s", lval, rval);
break;

View File

@ -78,9 +78,9 @@
* 12 <<, >>, >>> JSOP_LSH, JSOP_RSH, JSOP_URSH
* 13 +, -, etc. JSOP_ADD, JSOP_SUB, etc.
* 14 *, /, % JSOP_MUL, JSOP_DIV, JSOP_MOD
* 15 !, ~, etc. JSOP_NOT, JSOP_BITNOT, etc.
* 15 !, ~, delete, etc. JSOP_NOT, JSOP_BITNOT, JSOP_DEL*, etc.
* 16 3.14, 0, etc. JSOP_DOUBLE, JSOP_ZERO, etc.
* 17 delete, new JSOP_DEL*, JSOP_NEW
* 17 new JSOP_NEW
* 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc.
* 19 x, null, etc. JSOP_NAME, JSOP_NULL, etc.
*
@ -139,9 +139,9 @@ OPDEF(JSOP_NOT, 32, "not", "!", 1, 1, 1, 15, JOF_BYTE|J
OPDEF(JSOP_BITNOT, 33, "bitnot", "~", 1, 1, 1, 15, JOF_BYTE)
OPDEF(JSOP_NEG, 34, "neg", "- ", 1, 1, 1, 15, JOF_BYTE)
OPDEF(JSOP_NEW, 35, js_new_str, NULL, 3, -1, 1, 17, JOF_UINT16|JOF_INVOKE)
OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 17, JOF_ATOM|JOF_NAME|JOF_DEL)
OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 17, JOF_ATOM|JOF_PROP|JOF_DEL)
OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL)
OPDEF(JSOP_DELNAME, 36, "delname", NULL, 3, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEL)
OPDEF(JSOP_DELPROP, 37, "delprop", NULL, 3, 1, 1, 15, JOF_ATOM|JOF_PROP|JOF_DEL)
OPDEF(JSOP_DELELEM, 38, "delelem", NULL, 1, 2, 1, 15, JOF_BYTE |JOF_ELEM|JOF_DEL)
OPDEF(JSOP_TYPEOF, 39, js_typeof_str,NULL, 1, 1, 1, 15, JOF_BYTE|JOF_DETECTING)
OPDEF(JSOP_VOID, 40, js_void_str, NULL, 1, 1, 1, 15, JOF_BYTE)
@ -181,8 +181,8 @@ OPDEF(JSOP_TABLESWITCH, 70, "tableswitch", NULL, -1, 1, 0, 0, JOF_TABLES
OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUPSWITCH|JOF_DETECTING|JOF_PARENHEAD)
/* New, infallible/transitive identity ops. */
OPDEF(JSOP_STRICTEQ, 72, "stricteq", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING)
OPDEF(JSOP_STRICTNE, 73, "strictne", NULL, 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING)
OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
/* Resume instruction (emitted for the JIT for instructions that can't be restarted). */
OPDEF(JSOP_RESUME, 74, "resume", NULL, 1, 0, 0, 0, JOF_BYTE)
@ -318,7 +318,7 @@ OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 19, JOF_OBJECT
OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET)
/* Parenthesization opcode to help the decompiler. */
OPDEF(JSOP_GROUP, 131, "group", NULL, 1, 0, 0, 19, JOF_BYTE)
OPDEF(JSOP_UNUSED131, 131, "unused131", NULL, 1, 0, 0, 0, JOF_BYTE)
/*
* Host object extension: given 'o.item(i) = j', the left-hand side compiles
@ -419,7 +419,7 @@ OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
*/
OPDEF(JSOP_GETUPVAR, 186,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 17, JOF_BYTE |JOF_ELEM|JOF_DEL)
OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
/*
* Opcode to hold 24-bit immediate integer operands.

View File

@ -4516,7 +4516,7 @@ MemberExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
} else if (tt == TOK_RP) {
JSParseNode *group = pn3;
/* Recycle the useless TOK_RP/JSOP_GROUP node. */
/* Recycle the useless TOK_RP node. */
pn3 = group->pn_kid;
group->pn_kid = NULL;
RecycleTree(group, tc);

View File

@ -6174,12 +6174,6 @@ TraceRecorder::record_JSOP_SETLOCALPOP()
return true;
}
bool
TraceRecorder::record_JSOP_GROUP()
{
return true; // no-op
}
bool
TraceRecorder::record_JSOP_SETCALL()
{
@ -6952,6 +6946,7 @@ UNUSED(JSOP_UNUSED76)
UNUSED(JSOP_UNUSED77)
UNUSED(JSOP_UNUSED78)
UNUSED(JSOP_UNUSED79)
UNUSED(JSOP_UNUSED131)
UNUSED(JSOP_UNUSED201)
UNUSED(JSOP_UNUSED202)
UNUSED(JSOP_UNUSED203)

View File

@ -204,7 +204,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 - 32)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 33)
/*
* Library-private functions.