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
This commit is contained in:
parent
6e086b553a
commit
bc2d3f5307
@ -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. */
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user