diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index c4be221026a..7fdfaaa166c 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -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; } diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 5d9bbbe7d09..793151f2956 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -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: diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index ff589506ae8..92ac2404325 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -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 diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 83e3a99cf6a..c9149c5b57c 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -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; diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index c08be763930..0eb36b0f7ce 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -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. diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp index 574c3e97d31..34be3bc1c6d 100644 --- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -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); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 0181549c3ef..04b30b1c7bf 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -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) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index a949c2bf00a..3adb8ac43c9 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -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.