From bc2d3f53074f706cd25e2ff7685a38e796ca908a Mon Sep 17 00:00:00 2001 From: "norris%netscape.com" Date: Fri, 4 Sep 1998 00:45:21 +0000 Subject: [PATCH] Add support for ECMA switch statements. These are like LISP cond in that they evaluate the case label each time. Still to do is to optimize so that TABLESWITCH and LOOKUPSWITCH are created when possible. git-svn-id: svn://10.0.0.236/trunk@9296 18797224-902f-48f8-a5cc-f745e15eee43 --- mozilla/js/ref/jsemit.c | 64 ++++++++++++++++++++++++++------- mozilla/js/ref/jsinterp.c | 24 +++++++++++++ mozilla/js/ref/jsopcode.c | 71 ++++++++++++++++++++++++++++--------- mozilla/js/ref/jsopcode.def | 12 +++++-- mozilla/js/ref/jsopcode.h | 5 +-- 5 files changed, 144 insertions(+), 32 deletions(-) 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 */