diff --git a/mozilla/js/ref/jsemit.c b/mozilla/js/ref/jsemit.c index 47b44e1ad85..b6a71766197 100644 --- a/mozilla/js/ref/jsemit.c +++ b/mozilla/js/ref/jsemit.c @@ -668,6 +668,11 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JSParseNode **table; jsbytecode *pc; JSBool hasDefault = JS_FALSE; + JSBool isEcmaSwitch = cx->version == JSVERSION_DEFAULT || + cx->version >= JSVERSION_1_4; + ptrdiff_t defaultOpPc = -1; + + switchop = isEcmaSwitch ? JSOP_CONDSWITCH : JSOP_TABLESWITCH; /* Emit code for the discriminant first. */ if (!js_EmitTree(cx, cg, pn->pn_kid1)) @@ -677,7 +682,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) top = CG_OFFSET(cg); js_PushStatement(&cg->treeContext, &stmtInfo, STMT_SWITCH, top); - switchop = JSOP_TABLESWITCH; pn2 = pn->pn_kid2; ncases = pn2->pn_count; @@ -696,6 +700,8 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) continue; } PR_ASSERT(pn3->pn_type == TOK_CASE); + if (switchop == JSOP_CONDSWITCH) + continue; if (!cg2.base) { if (!js_InitCodeGenerator(cx, &cg2, cg->filename, pn3->pn_pos.begin.lineno, @@ -716,18 +722,19 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; /* - * The GC must not run during this case expression evaluation. - * It won't if it runs only from the branch callback (because - * the only branches possible in a case expression come from ?: - * expressions, and those generate downward branches, while the + * The GC must not run during this case expression + * evaluation. It won't if it runs only from the + * branch callback (because the only branches possible + * in a case expression come from ?: expressions, + * and those generate downward branches, while the * branch callback runs only for upward branches). + * XXX - but function calls are possible! */ - ok = js_Execute(cx, cx->fp->scopeChain, script, NULL, cx->fp, - JS_FALSE, &pn3->pn_val); + ok = js_Execute(cx, cx->fp->scopeChain, script, NULL, + cx->fp, JS_FALSE, &pn3->pn_val); js_DestroyScript(cx, script); if (!ok) return JS_FALSE; - if (!JSVAL_IS_NUMBER(pn3->pn_val) && !JSVAL_IS_STRING(pn3->pn_val) && !JSVAL_IS_BOOLEAN(pn3->pn_val)) { @@ -741,7 +748,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) return JS_FALSE; } - if (switchop == JSOP_LOOKUPSWITCH) + if (switchop != JSOP_TABLESWITCH) continue; if (!JSVAL_IS_INT(pn3->pn_val)) { switchop = JSOP_LOOKUPSWITCH; @@ -757,7 +764,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (high < i) high = i; } - if (cg2.base) + if (switchop != JSOP_CONDSWITCH && cg2.base) js_ResetCodeGenerator(cx, &cg2); if (switchop == JSOP_TABLESWITCH) { tablen = (uint32)(high - low + 1); @@ -788,8 +795,34 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) if (js_EmitN(cx, cg, switchop, switchsize) < 0) return JS_FALSE; + if (switchop == JSOP_CONDSWITCH) { + /* Emit code for evaluating cases and jumping to case statements. */ + if (!js_SetJumpOffset(cx, cg, CG_CODE(cg, top), + CG_OFFSET(cg) - top)) + return JS_FALSE; + for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (pn3->pn_type == TOK_DEFAULT) + continue; + pn4 = pn3->pn_left; + if (!js_EmitTree(cx, cg, pn4)) + return JS_FALSE; + pn3->pn_offset = js_Emit3(cx, cg, JSOP_CASE, 0, 0); + if (pn3->pn_offset < 0) + return JS_FALSE; + pn3->pn_offset; + } + /* Emit default even if no explicit default statement. */ + defaultOpPc = js_Emit3(cx, cg, JSOP_DEFAULT, 0, 0); + if (defaultOpPc < 0) + return JS_FALSE; + } + /* Emit code for each case's statements, copying pn_offset up to pn3. */ for (pn3 = pn2->pn_head; pn3; pn3 = pn3->pn_next) { + if (switchop == JSOP_CONDSWITCH && pn3->pn_type != TOK_DEFAULT) { + pn3->pn_val = INT_TO_JSVAL(pn3->pn_offset - top); + CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, pn3->pn_offset); + } pn4 = pn3->pn_right; if (!js_EmitTree(cx, cg, pn4)) return JS_FALSE; @@ -805,8 +838,15 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) /* Set the default offset (to end of switch if no default). */ pc = CG_CODE(cg, top); - if (!js_SetJumpOffset(cx, cg, pc, off)) - return JS_FALSE; + if (switchop != JSOP_CONDSWITCH) { + if (!js_SetJumpOffset(cx, cg, pc, off)) + return JS_FALSE; + } + if (defaultOpPc != -1) { + if (!js_SetJumpOffset(cx, cg, CG_CODE(cg, defaultOpPc), + off - (defaultOpPc - top))) + return JS_FALSE; + } pc += 2; /* Set the SRC_SWITCH note's offset operand to tell end of switch. */ diff --git a/mozilla/js/ref/jsinterp.c b/mozilla/js/ref/jsinterp.c index f824cf94382..0db15eb069a 100644 --- a/mozilla/js/ref/jsinterp.c +++ b/mozilla/js/ref/jsinterp.c @@ -1126,6 +1126,11 @@ js_Interpret(JSContext *cx, jsval *result) fp->rval = POP(); goto out; +#if JS_HAS_SWITCH_STATEMENT + case JSOP_DEFAULT: + POP(); + /* fall through */ +#endif case JSOP_GOTO: len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); @@ -1662,6 +1667,20 @@ js_Interpret(JSContext *cx, jsval *result) case JSOP_NEW_NE: NEW_EQUALITY_OP(!=, JS_TRUE); break; + +#if JS_HAS_SWITCH_STATEMENT + case JSOP_CASE: + NEW_EQUALITY_OP(==, JS_FALSE); + POP(); + if (cond) { + len = GET_JUMP_OFFSET(pc); + CHECK_BRANCH(len); + } else { + PUSH(lval); + } + break; +#endif + #endif /* !JS_BUG_FALLIBLE_EQOPS */ case JSOP_LT: @@ -2306,6 +2325,11 @@ js_Interpret(JSContext *cx, jsval *result) } #undef SEARCH_PAIRS break; + + case JSOP_CONDSWITCH: + len = GET_JUMP_OFFSET(pc); + goto advance_pc; + #endif /* JS_HAS_SWITCH_STATEMENT */ #if JS_HAS_LEXICAL_CLOSURE diff --git a/mozilla/js/ref/jsopcode.c b/mozilla/js/ref/jsopcode.c index 250d86ed30b..f0d4e4bd627 100644 --- a/mozilla/js/ref/jsopcode.c +++ b/mozilla/js/ref/jsopcode.c @@ -192,7 +192,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, uintN loc, pc2 += JUMP_OFFSET_LEN; npairs = (uintN) GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; - fprintf(fp, " defaultOffset %d npairs %u", off, npairs); + fprintf(fp, " offset %d npairs %u", off, npairs); while (npairs) { atom = GET_ATOM(cx, script, pc2); pc2 += ATOM_INDEX_LEN; @@ -567,13 +567,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb); static JSBool DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, - jsbytecode *pc, ptrdiff_t switchLength, ptrdiff_t defaultOffset) + jsbytecode *pc, ptrdiff_t switchLength, + ptrdiff_t defaultOffset, JSBool isCondSwitch) { JSContext *cx; JSPrinter *jp; char *lval, *rval; uintN i; - ptrdiff_t diff, off, off2; + ptrdiff_t diff, off, off2, caseExprOff; jsval key; JSString *str; @@ -593,6 +594,8 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, return JS_FALSE; jp->indent -= 4; } + caseExprOff = 1 + JUMP_OFFSET_LEN + ATOM_INDEX_LEN + + tableLength * (ATOM_INDEX_LEN + JUMP_OFFSET_LEN); for (i = 0; i < tableLength; i++) { off = table[i].offset; if (i + 1 < tableLength) @@ -600,18 +603,28 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, else off2 = switchLength; key = table[i].key; - str = js_ValueToString(cx, key); - if (!str) - return JS_FALSE; - jp->indent += 2; - if (JSVAL_IS_STRING(key)) { - rval = QuoteString(&ss->sprinter, str, '"'); - if (!rval) + if (isCondSwitch) { + uint32 caseLen = js_CodeSpec[JSOP_CASE].length; + ptrdiff_t caseOff = JSVAL_TO_INT(key); + jp->indent += 2; + if (!Decompile(ss, pc + caseExprOff, + caseOff - caseExprOff + caseLen)) return JS_FALSE; - } else { - rval = JS_GetStringBytes(str); - } - js_printf(jp, "\tcase %s:\n", rval); + caseExprOff = caseOff + caseLen; + } else { + str = js_ValueToString(cx, key); + if (!str) + return JS_FALSE; + jp->indent += 2; + if (JSVAL_IS_STRING(key)) { + rval = QuoteString(&ss->sprinter, str, '"'); + if (!rval) + return JS_FALSE; + } else { + rval = JS_GetStringBytes(str); + } + js_printf(jp, "\tcase %s:\n", rval); + } jp->indent += 2; if (off <= defaultOffset && defaultOffset < off2) { diff = defaultOffset - off; @@ -1580,7 +1593,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } js_qsort(table, (size_t)j, sizeof *table, CompareOffsets, NULL); - ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off); + ok = DecompileSwitch(ss, table, (uintN)j, pc, len, off, + JS_FALSE); JS_free(cx, table); if (!ok) return ok; @@ -1589,6 +1603,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } case JSOP_LOOKUPSWITCH: + case JSOP_CONDSWITCH: { jsbytecode *pc2 = pc; ptrdiff_t off, off2; @@ -1614,14 +1629,38 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) table[i].key = ATOM_KEY(atom); table[i].offset = off2; } + if (op == JSOP_CONDSWITCH) { + /* + * Find offset of default code by finding the default + * instruction and adding its offset. + */ + jsbytecode *pc3; - ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off); + off = JSVAL_TO_INT(table[npairs-1].key) + + js_CodeSpec[JSOP_CASE].length; + pc3 = pc + off; + off += GET_JUMP_OFFSET(pc3); + } + + ok = DecompileSwitch(ss, table, (uintN)npairs, pc, len, off, + op == JSOP_CONDSWITCH); JS_free(cx, table); if (!ok) return ok; todo = -2; break; } + + case JSOP_CASE: + { + lval = POP_STR(); + if (!lval) + return JS_FALSE; + js_printf(jp, "\tcase %s:\n", lval); + todo = -2; + break; + } + #endif /* JS_HAS_SWITCH_STATEMENT */ #if !JS_BUG_FALLIBLE_EQOPS diff --git a/mozilla/js/ref/jsopcode.def b/mozilla/js/ref/jsopcode.def index 201a4755405..8f87eff39f0 100644 --- a/mozilla/js/ref/jsopcode.def +++ b/mozilla/js/ref/jsopcode.def @@ -195,9 +195,17 @@ OPDEF(JSOP_INSTANCEOF,112,js_instanceof_str,js_instanceof_str,1,2,1,6,JOF_BYTE) OPDEF(JSOP_DEBUGGER, 113,"debugger", NULL, 1, 0, 0, 0, JOF_BYTE) /* gosub/retsub for finally handling */ -OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 1, 0, JOF_JUMP) +OPDEF(JSOP_GOSUB, 114,"gosub", NULL, 3, 0, 1, 0, JOF_JUMP) OPDEF(JSOP_RETSUB, 115,"retsub", NULL, 1, 1, 0, 0, JOF_BYTE) /* more exception handling ops */ OPDEF(JSOP_EXCEPTION, 116,"exception", NULL, 1, 0, 1, 0, JOF_BYTE) -OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16) +OPDEF(JSOP_SETSP, 117,"setsp", NULL, 3, 0, 0, 0, JOF_UINT16) + +/* + * ECMA-compliant switch statement ops. + * "switch" is essentially "nop" and "default" is essentially "pop" + "goto". + */ +OPDEF(JSOP_CONDSWITCH,118,"switch", NULL, -1, 0, 0, 0, JOF_LOOKUPSWITCH) +OPDEF(JSOP_CASE, 119,"case", NULL, 3, 1, 0, 0, JOF_JUMP) +OPDEF(JSOP_DEFAULT, 120,"default", NULL, 3, 1, 0, 0, JOF_JUMP) diff --git a/mozilla/js/ref/jsopcode.h b/mozilla/js/ref/jsopcode.h index d00f1d88b57..54f28616e98 100644 --- a/mozilla/js/ref/jsopcode.h +++ b/mozilla/js/ref/jsopcode.h @@ -47,8 +47,9 @@ typedef enum JSOp { #define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ #define JOF_TABLESWITCH 4 /* table switch */ #define JOF_LOOKUPSWITCH 5 /* lookup switch */ -#define JOF_QARG 6 /* quickened get/set function argument ops */ -#define JOF_QVAR 7 /* quickened get/set local variable ops */ +#define JOF_CONDSWITCH 6 /* cond switch */ +#define JOF_QARG 7 /* quickened get/set function argument ops */ +#define JOF_QVAR 8 /* quickened get/set local variable ops */ #define JOF_TYPEMASK 0x000f /* mask for above immediate types */ #define JOF_NAME 0x0010 /* name operation */ #define JOF_PROP 0x0020 /* obj.prop operation */