Index: jsemit.c =================================================================== RCS file: /cvsroot/mozilla/js/src/jsemit.c,v retrieving revision 3.135 diff -p -u -8 -r3.135 jsemit.c --- jsemit.c 18 Nov 2005 06:19:46 -0000 3.135 +++ jsemit.c 19 Nov 2005 03:18:31 -0000 @@ -2769,17 +2769,17 @@ js_EmitFunctionBody(JSContext *cx, JSCod memset(&frame, 0, sizeof frame); frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING; cx->fp = &frame; - ok = js_EmitTree(cx, cg, body); + ok = js_EmitTree(cx, cg, body) && js_Emit1(cx, cg, JSOP_STOP) >= 0; cx->fp = fp; if (!ok) return JS_FALSE; fun->u.script = js_NewScriptFromCG(cx, cg, fun); if (!fun->u.script) return JS_FALSE; JS_ASSERT(fun->interpreted); Index: jsfun.c =================================================================== RCS file: /cvsroot/mozilla/js/src/jsfun.c,v retrieving revision 3.126 diff -p -u -8 -r3.126 jsfun.c --- jsfun.c 18 Nov 2005 06:19:46 -0000 3.126 +++ jsfun.c 19 Nov 2005 03:18:31 -0000 @@ -1926,19 +1926,20 @@ js_InitFunctionClass(JSContext *cx, JSOb return NULL; atom = js_Atomize(cx, js_FunctionClass.name, strlen(js_FunctionClass.name), 0); if (!atom) goto bad; fun = js_NewFunction(cx, proto, NULL, 0, 0, obj, NULL); if (!fun) goto bad; - fun->u.script = js_NewScript(cx, 0, 0, 0); + fun->u.script = js_NewScript(cx, 1, 0, 0); if (!fun->u.script) goto bad; + fun->u.script->code[0] = JSOP_STOP; fun->interpreted = JS_TRUE; return proto; bad: cx->newborn[GCX_OBJECT] = NULL; return NULL; } Index: jsinterp.c =================================================================== RCS file: /cvsroot/mozilla/js/src/jsinterp.c,v retrieving revision 3.205 diff -p -u -8 -r3.205 jsinterp.c --- jsinterp.c 18 Nov 2005 06:19:46 -0000 3.205 +++ jsinterp.c 19 Nov 2005 03:18:32 -0000 @@ -166,16 +166,22 @@ static JSClass prop_iterator_class = { (JS_ASSERT((fp)->script || !(fp)->spbase || (sp) == (fp)->spbase), \ (fp)->sp = sp) #else #define SAVE_SP(fp) ((fp)->sp = sp) #endif #define RESTORE_SP(fp) (sp = (fp)->sp) /* + * Commit deferred stores of interpreter registers to their homes in fp, when + * calling out of the interpreter loop or threaded code. + */ +#define SAVE_SP_AND_PC(fp) (SAVE_SP(fp), (fp)->pc = pc) + +/* * Push the generating bytecode's pc onto the parallel pc stack that runs * depth slots below the operands. * * NB: PUSH_OPND uses sp, depth, and pc from its lexical environment. See * js_Interpret for these local variables' declarations and uses. */ #define PUSH_OPND(v) (sp[-depth] = (jsval)pc, PUSH(v)) #define STORE_OPND(n,v) (sp[(n)-depth] = (jsval)pc, sp[n] = (v)) @@ -211,31 +217,31 @@ static JSClass prop_iterator_class = { JS_END_MACRO #define FETCH_INT(cx, n, i) \ JS_BEGIN_MACRO \ jsval v_ = FETCH_OPND(n); \ if (JSVAL_IS_INT(v_)) { \ i = JSVAL_TO_INT(v_); \ } else { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = js_ValueToECMAInt32(cx, v_, &i); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #define FETCH_UINT(cx, n, ui) \ JS_BEGIN_MACRO \ jsval v_ = FETCH_OPND(n); \ jsint i_; \ if (JSVAL_IS_INT(v_) && (i_ = JSVAL_TO_INT(v_)) >= 0) { \ ui = (uint32) i_; \ } else { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = js_ValueToECMAUint32(cx, v_, &ui); \ if (!ok) \ goto out; \ } \ JS_END_MACRO /* * Optimized conversion macros that test for the desired type in v before @@ -243,45 +249,45 @@ static JSClass prop_iterator_class = { */ #define VALUE_TO_NUMBER(cx, v, d) \ JS_BEGIN_MACRO \ if (JSVAL_IS_INT(v)) { \ d = (jsdouble)JSVAL_TO_INT(v); \ } else if (JSVAL_IS_DOUBLE(v)) { \ d = *JSVAL_TO_DOUBLE(v); \ } else { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = js_ValueToNumber(cx, v, &d); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #define POP_BOOLEAN(cx, v, b) \ JS_BEGIN_MACRO \ v = FETCH_OPND(-1); \ if (v == JSVAL_NULL) { \ b = JS_FALSE; \ } else if (JSVAL_IS_BOOLEAN(v)) { \ b = JSVAL_TO_BOOLEAN(v); \ } else { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = js_ValueToBoolean(cx, v, &b); \ if (!ok) \ goto out; \ } \ sp--; \ JS_END_MACRO #define VALUE_TO_OBJECT(cx, v, obj) \ JS_BEGIN_MACRO \ if (!JSVAL_IS_PRIMITIVE(v)) { \ obj = JSVAL_TO_OBJECT(v); \ } else { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ obj = js_ValueToNonNullObject(cx, v); \ if (!obj) { \ ok = JS_FALSE; \ goto out; \ } \ } \ JS_END_MACRO @@ -1686,17 +1692,17 @@ InternNonIntElementId(JSContext *cx, jsv return InternStringElementId(cx, idval, idp); } #if JS_HAS_XML_SUPPORT #define CHECK_ELEMENT_ID(obj, id) \ JS_BEGIN_MACRO \ if (JSID_IS_OBJECT(id) && !OBJECT_IS_XML(cx, obj)) { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = InternStringElementId(cx, OBJECT_JSID_TO_JSVAL(id), &id); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #else #define CHECK_ELEMENT_ID(obj, id) JS_ASSERT(!JSID_IS_OBJECT(id)) @@ -1707,16 +1713,38 @@ InternNonIntElementId(JSContext *cx, jsv #define MAX_INTERP_LEVEL 250 #else #define MAX_INTERP_LEVEL 1000 #endif #endif #define MAX_INLINE_CALL_COUNT 1000 +/* + * Threaded interpretation via computed goto appears to be well-supported by + * GCC 3 and higher. IBM's C compiler when run with the right options (e.g., + * -qlanglvl=extended) also supports threading. Ditto the SunPro C compiler. + * Add your compiler support macros here. + */ +#if __GNUC__ >= 3 || \ + (__IBMC__ >= 700 && defined __IBM_COMPUTED_GOTO) || \ + __SUNPRO_C >= 0x570 +# define JS_THREADED_INTERP 1 +#else +# undef JS_THREADED_INTERP +#endif + +typedef enum JSOpLength { +#define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + op##_LENGTH = length, +#include "jsopcode.tbl" +#undef OPDEF + JSOP_LIMIT_LENGTH +} JSOpLength; + JSBool js_Interpret(JSContext *cx, jsbytecode *pc, jsval *result) { JSRuntime *rt; JSStackFrame *fp; JSScript *script; uintN inlineCallCount; JSObject *obj, *obj2, *proto, *parent; @@ -1724,33 +1752,32 @@ js_Interpret(JSContext *cx, jsbytecode * JSBranchCallback onbranch; JSBool ok, cond; JSTrapHandler interruptHandler; jsint depth, len; jsval *sp, *newsp; void *mark; jsbytecode *endpc, *pc2; JSOp op, op2; - const JSCodeSpec *cs; jsatomid atomIndex; JSAtom *atom; uintN argc, slot, attrs; jsval *vp, lval, rval, ltmp, rtmp; jsid id; JSObject *withobj, *origobj, *propobj; jsval iter_state; JSProperty *prop; JSScopeProperty *sprop; JSString *str, *str2; jsint i, j; jsdouble d, d2; JSClass *clasp, *funclasp; JSFunction *fun; JSType type; -#ifdef DEBUG +#if !defined JS_THREADED_INTERP && defined DEBUG FILE *tracefp; #endif #if JS_HAS_EXPORT_IMPORT JSIdArray *ida; #endif #if JS_HAS_SWITCH_STATEMENT jsint low, high, off, npairs; JSBool match; @@ -1758,22 +1785,55 @@ js_Interpret(JSContext *cx, jsbytecode * #if JS_HAS_GETTER_SETTER JSPropertyOp getter, setter; #endif #if JS_HAS_XML_SUPPORT JSBool foreach = JS_FALSE; #endif int stackDummy; +#ifdef JS_THREADED_INTERP + static void *normalJumpTable[] = { +# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + &&L_##op, +# include "jsopcode.tbl" +# undef OPDEF + }; + + static void *interruptJumpTable[] = { +# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \ + &&interrupt, +# include "jsopcode.tbl" +# undef OPDEF + }; + + register void **jumpTable = normalJumpTable; + +# define DO_OP() goto *jumpTable[op] +# define DO_NEXT_OP(n) do { op = *(pc += (n)); DO_OP(); } while (0) +# define BEGIN_CASE(OP) case OP: L_##OP: +# define END_CASE(OP) DO_NEXT_OP(OP##_LENGTH); +# define END_VARLEN_CASE DO_NEXT_OP(len); +# define EMPTY_CASE(OP) BEGIN_CASE(OP) op = *++pc; DO_OP(); +#else +# define DO_OP() goto do_op +# define DO_NEXT_OP(n) goto advance_pc +# define BEGIN_CASE(OP) case OP: +# define END_CASE(OP) break; +# define END_VARLEN_CASE break; +# define EMPTY_CASE(OP) BEGIN_CASE(OP) END_CASE(OP) +#endif + *result = JSVAL_VOID; rt = cx->runtime; /* Set registerized frame pointer and derived script pointer. */ fp = cx->fp; script = fp->script; + JS_ASSERT(script->length != 0); /* Count of JS function calls that nest in this C js_Interpret frame. */ inlineCallCount = 0; /* * Optimized Get and SetVersion for proper script language versioning. * * If any native method or JSClass/JSObjectOps hook calls js_SetVersion @@ -1794,30 +1854,40 @@ js_Interpret(JSContext *cx, jsbytecode * */ #define LOAD_BRANCH_CALLBACK(cx) (onbranch = (cx)->branchCallback) LOAD_BRANCH_CALLBACK(cx); ok = JS_TRUE; #define CHECK_BRANCH(len) \ JS_BEGIN_MACRO \ if (len <= 0 && onbranch) { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ if (!(ok = (*onbranch)(cx, script))) \ goto out; \ } \ JS_END_MACRO /* * Load the debugger's interrupt hook here and after calling out to native * functions (but not to getters, setters, or other native hooks), so we do * not have to reload it each time through the interpreter loop -- we hope - * the compiler can keep it in a register. - * XXX if it spills, we still lose + * the compiler can keep it in a register when it is non-null. */ -#define LOAD_INTERRUPT_HANDLER(rt) (interruptHandler = (rt)->interruptHandler) +#ifdef JS_THREADED_INTERP +# define LOAD_JUMP_TABLE() \ + (jumpTable = interruptHandler ? interruptJumpTable : normalJumpTable) +#else +# define LOAD_JUMP_TABLE() /* nothing */ +#endif + +#define LOAD_INTERRUPT_HANDLER(rt) \ + JS_BEGIN_MACRO \ + interruptHandler = (rt)->interruptHandler; \ + LOAD_JUMP_TABLE(); \ + JS_END_MACRO LOAD_INTERRUPT_HANDLER(rt); /* Check for too much js_Interpret nesting, or too deep a C stack. */ if (++cx->interpLevel == MAX_INTERP_LEVEL || !JS_CHECK_STACK_SIZE(cx, stackDummy)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED); ok = JS_FALSE; @@ -1841,51 +1911,86 @@ js_Interpret(JSContext *cx, jsbytecode * SAVE_SP(fp); } else { sp = fp->sp; JS_ASSERT(JS_UPTRDIFF(sp, fp->spbase) <= depth * sizeof(jsval)); newsp = fp->spbase - depth; mark = NULL; } - endpc = script->code + script->length; - while (pc < endpc) { - fp->pc = pc; +#ifdef JS_THREADED_INTERP + + /* + * This is a loop, but it does not look like a loop. The loop-closing + * jump is distributed throughout interruptJumpTable, and comes back to + * the interrupt label. The dispatch on op is through normalJumpTable. + * The trick is LOAD_INTERRUPT_HANDLER setting jumpTable appropriately. + */ + if (interruptHandler) { +interrupt: + SAVE_SP_AND_PC(fp); + switch (interruptHandler(cx, script, pc, &rval, + rt->interruptHandlerData)) { + case JSTRAP_ERROR: + ok = JS_FALSE; + goto out; + case JSTRAP_CONTINUE: + break; + case JSTRAP_RETURN: + fp->rval = rval; + goto out; +#if JS_HAS_EXCEPTIONS + case JSTRAP_THROW: + cx->throwing = JS_TRUE; + cx->exception = rval; + ok = JS_FALSE; + goto out; +#endif /* JS_HAS_EXCEPTIONS */ + default:; + } + LOAD_INTERRUPT_HANDLER(rt); + } + + op = (JSOp) *pc; + goto *normalJumpTable[op]; + +#else /* !JS_THREADED_INTERP */ + + for (;;) { op = (JSOp) *pc; do_op: - cs = &js_CodeSpec[op]; - len = cs->length; + len = js_CodeSpec[op].length; #ifdef DEBUG tracefp = (FILE *) cx->tracefp; if (tracefp) { intN nuses, n; fprintf(tracefp, "%4u: ", js_PCToLineNumber(cx, script, pc)); js_Disassemble1(cx, script, pc, PTRDIFF(pc, script->code, jsbytecode), JS_FALSE, tracefp); - nuses = cs->nuses; + nuses = js_CodeSpec[op].nuses; if (nuses) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); for (n = -nuses; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); if (str) { fprintf(tracefp, "%s %s", (n == -nuses) ? " inputs:" : ",", JS_GetStringBytes(str)); } } fprintf(tracefp, " @ %d\n", sp - fp->spbase); } } -#endif +#endif /* DEBUG */ if (interruptHandler) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); switch (interruptHandler(cx, script, pc, &rval, rt->interruptHandlerData)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: @@ -1898,83 +2003,87 @@ js_Interpret(JSContext *cx, jsbytecode * ok = JS_FALSE; goto out; #endif /* JS_HAS_EXCEPTIONS */ default:; } LOAD_INTERRUPT_HANDLER(rt); } +#endif /* !JS_THREADED_INTERP */ + switch (op) { - case JSOP_NOP: - break; + BEGIN_CASE(JSOP_STOP) + goto out; - case JSOP_GROUP: + EMPTY_CASE(JSOP_NOP) + + BEGIN_CASE(JSOP_GROUP) obj = NULL; - break; + END_CASE(JSOP_GROUP) - case JSOP_PUSH: + BEGIN_CASE(JSOP_PUSH) PUSH_OPND(JSVAL_VOID); - break; + END_CASE(JSOP_PUSH) - case JSOP_POP: + BEGIN_CASE(JSOP_POP) sp--; - break; + END_CASE(JSOP_POP) - case JSOP_POP2: + BEGIN_CASE(JSOP_POP2) sp -= 2; - break; + END_CASE(JSOP_POP2) - case JSOP_SWAP: + BEGIN_CASE(JSOP_SWAP) /* * N.B. JSOP_SWAP doesn't swap the corresponding generating pcs * for the operands it swaps. */ ltmp = sp[-1]; sp[-1] = sp[-2]; sp[-2] = ltmp; - break; + END_CASE(JSOP_SWAP) - case JSOP_POPV: + BEGIN_CASE(JSOP_POPV) *result = POP_OPND(); - break; + END_CASE(JSOP_POPV) - case JSOP_ENTERWITH: + BEGIN_CASE(JSOP_ENTERWITH) FETCH_OBJECT(cx, -1, rval, obj); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); withobj = js_NewObject(cx, &js_WithClass, obj, fp->scopeChain); if (!withobj) goto out; rval = INT_TO_JSVAL(sp - fp->spbase); OBJ_SET_SLOT(cx, withobj, JSSLOT_PRIVATE, rval); fp->scopeChain = withobj; STORE_OPND(-1, OBJECT_TO_JSVAL(withobj)); - break; + END_CASE(JSOP_ENTERWITH) - case JSOP_LEAVEWITH: + BEGIN_CASE(JSOP_LEAVEWITH) rval = POP_OPND(); JS_ASSERT(JSVAL_IS_OBJECT(rval)); withobj = JSVAL_TO_OBJECT(rval); JS_ASSERT(OBJ_GET_CLASS(cx, withobj) == &js_WithClass); rval = OBJ_GET_SLOT(cx, withobj, JSSLOT_PARENT); JS_ASSERT(JSVAL_IS_OBJECT(rval)); fp->scopeChain = JSVAL_TO_OBJECT(rval); - break; + END_CASE(JSOP_LEAVEWITH) - case JSOP_SETRVAL: + BEGIN_CASE(JSOP_SETRVAL) fp->rval = POP_OPND(); - break; + END_CASE(JSOP_SETRVAL) - case JSOP_RETURN: + BEGIN_CASE(JSOP_RETURN) CHECK_BRANCH(-1); fp->rval = POP_OPND(); /* FALL THROUGH */ - case JSOP_RETRVAL: /* fp->rval already set */ + BEGIN_CASE(JSOP_RETRVAL) /* fp->rval already set */ if (inlineCallCount) inline_return: { JSInlineFrame *ifp = (JSInlineFrame *) fp; void *hookData = ifp->hookData; if (hookData) { JSInterpreterHook hook = cx->runtime->callHook; @@ -1984,17 +2093,17 @@ js_Interpret(JSContext *cx, jsbytecode * } } #if JS_HAS_ARGS_OBJECT if (fp->argsobj) ok &= js_PutArgsObject(cx, fp); #endif /* Restore context version only if callee hasn't set version. */ - if (cx->version == currentVersion) { + if (JS_LIKELY(cx->version == currentVersion)) { currentVersion = ifp->callerVersion; if (currentVersion != cx->version) js_SetVersion(cx, currentVersion); } /* Store the return value in the caller's operand frame. */ vp = fp->argv - 2; *vp = fp->rval; @@ -2006,153 +2115,160 @@ js_Interpret(JSContext *cx, jsbytecode * /* Restore sp to point just above the return value. */ fp->sp = vp + 1; RESTORE_SP(fp); /* Restore the calling script's interpreter registers. */ script = fp->script; depth = (jsint) script->depth; pc = fp->pc; +#ifndef JS_THREADED_INTERP endpc = script->code + script->length; +#endif /* Store the generating pc for the return value. */ vp[-depth] = (jsval)pc; - /* Set remaining variables for 'goto advance_pc'. */ - op = (JSOp) *pc; - cs = &js_CodeSpec[op]; - len = cs->length; - /* Resume execution in the calling frame. */ inlineCallCount--; - if (ok) - goto advance_pc; + if (JS_LIKELY(ok)) { + JS_ASSERT((JSOp)*pc == JSOP_CALL); + len = JSOP_CALL_LENGTH; + DO_NEXT_OP(len); + } } goto out; #if JS_HAS_SWITCH_STATEMENT - case JSOP_DEFAULT: + BEGIN_CASE(JSOP_DEFAULT) (void) POP(); /* FALL THROUGH */ #endif - case JSOP_GOTO: + BEGIN_CASE(JSOP_GOTO) len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); - break; + END_VARLEN_CASE - case JSOP_IFEQ: + BEGIN_CASE(JSOP_IFEQ) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_IFEQ) - case JSOP_IFNE: + BEGIN_CASE(JSOP_IFNE) POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_IFNE) - case JSOP_OR: + BEGIN_CASE(JSOP_OR) POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_OR) - case JSOP_AND: + BEGIN_CASE(JSOP_AND) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMP_OFFSET(pc); PUSH_OPND(rval); + DO_NEXT_OP(len); } - break; - + END_CASE(JSOP_AND) #if JS_HAS_SWITCH_STATEMENT - case JSOP_DEFAULTX: + BEGIN_CASE(JSOP_DEFAULTX) (void) POP(); /* FALL THROUGH */ #endif - case JSOP_GOTOX: + BEGIN_CASE(JSOP_GOTOX) len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); - break; + END_VARLEN_CASE - case JSOP_IFEQX: + BEGIN_CASE(JSOP_IFEQX) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_IFEQX) - case JSOP_IFNEX: + BEGIN_CASE(JSOP_IFNEX) POP_BOOLEAN(cx, rval, cond); if (cond != JS_FALSE) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_IFNEX) - case JSOP_ORX: + BEGIN_CASE(JSOP_ORX) POP_BOOLEAN(cx, rval, cond); if (cond == JS_TRUE) { len = GET_JUMPX_OFFSET(pc); PUSH_OPND(rval); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_ORX) - case JSOP_ANDX: + BEGIN_CASE(JSOP_ANDX) POP_BOOLEAN(cx, rval, cond); if (cond == JS_FALSE) { len = GET_JUMPX_OFFSET(pc); PUSH_OPND(rval); + DO_NEXT_OP(len); } - break; + END_CASE(JSOP_ANDX) - case JSOP_TOOBJECT: + BEGIN_CASE(JSOP_TOOBJECT) rval = FETCH_OPND(-1); if (!JSVAL_IS_PRIMITIVE(rval)) { obj = JSVAL_TO_OBJECT(rval); } else { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_ValueToObject(cx, rval, &obj); if (!ok) goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - break; + END_CASE(JSOP_TOOBJECT) /* * If the index value at sp[n] is not an int that fits in a jsval, it could * be an object (an XML QName, AttributeName, or AnyName), but only if we are * compiling with JS_HAS_XML_SUPPORT. Otherwise convert the index value to a * string atom id. */ #define FETCH_ELEMENT_ID(n, id) \ JS_BEGIN_MACRO \ jsval idval_ = FETCH_OPND(n); \ if (JSVAL_IS_INT(idval_)) { \ id = INT_JSVAL_TO_JSID(idval_); \ } else { \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = InternNonIntElementId(cx, idval_, &id); \ if (!ok) \ goto out; \ } \ JS_END_MACRO #if JS_HAS_IN_OPERATOR - case JSOP_IN: - SAVE_SP(fp); + BEGIN_CASE(JSOP_IN) + SAVE_SP_AND_PC(fp); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval)) { str = js_DecompileValueGenerator(cx, -1, rval, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_IN_NOT_OBJECT, JS_GetStringBytes(str)); } @@ -2164,61 +2280,61 @@ js_Interpret(JSContext *cx, jsbytecode * CHECK_ELEMENT_ID(obj, id); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; sp--; STORE_OPND(-1, BOOLEAN_TO_JSVAL(prop != NULL)); if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); - break; + END_CASE(JSOP_IN) #endif /* JS_HAS_IN_OPERATOR */ - case JSOP_FORPROP: + BEGIN_CASE(JSOP_FORPROP) /* * Handle JSOP_FORPROP first, so the cost of the goto do_forinloop * is not paid for the more common cases. */ lval = FETCH_OPND(-1); atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); i = -2; goto do_forinloop; - case JSOP_FORNAME: + BEGIN_CASE(JSOP_FORNAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); /* * ECMA 12.6.3 says to eval the LHS after looking for properties * to enumerate, and bail without LHS eval if there are no props. * We do Find here to share the most code at label do_forinloop. * If looking for enumerable properties could have side effects, * then we'd have to move this into the common code and condition * it on op == JSOP_FORNAME. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (prop) OBJ_DROP_PROPERTY(cx, obj2, prop); lval = OBJECT_TO_JSVAL(obj); /* FALL THROUGH */ - case JSOP_FORARG: - case JSOP_FORVAR: + BEGIN_CASE(JSOP_FORARG) + BEGIN_CASE(JSOP_FORVAR) /* * JSOP_FORARG and JSOP_FORVAR don't require any lval computation * here, because they address slots on the stack (in fp->args and * fp->vars, respectively). */ /* FALL THROUGH */ - case JSOP_FORELEM: + BEGIN_CASE(JSOP_FORELEM) /* * JSOP_FORELEM simply initializes or updates the iteration state * and leaves the index expression evaluation and assignment to the * enumerator until after the next property has been acquired, via * a JSOP_ENUMELEM bytecode. */ i = -1; @@ -2247,20 +2363,20 @@ js_Interpret(JSContext *cx, jsbytecode * * JSObject that contains the iteration state. (An object is used * rather than a native struct so that the iteration state is * cleaned up via GC if the for-in loop terminates abruptly.) */ vp = &sp[i - 1]; rval = *vp; /* - * Save sp in fp now, before any OBJ_* call-outs that might nest - * an interpreter or GC activation on this context. + * Save sp and pc in fp now, before any OBJ_* call-outs that might + * nest an interpreter or GC activation on this context. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); /* Is this the first iteration ? */ if (JSVAL_IS_VOID(rval)) { /* Yes, create a new JSObject to hold the iterator state */ propobj = js_NewObject(cx, &prop_iterator_class, NULL, obj); if (!propobj) { ok = JS_FALSE; goto out; @@ -2478,40 +2594,41 @@ js_Interpret(JSContext *cx, jsbytecode * } /* Push true to keep looping through properties. */ rval = JSVAL_TRUE; end_forinloop: sp += i + 1; PUSH_OPND(rval); - break; + len = js_CodeSpec[op].length; + DO_NEXT_OP(len); } - case JSOP_DUP: + BEGIN_CASE(JSOP_DUP) JS_ASSERT(sp > fp->spbase); rval = sp[-1]; PUSH_OPND(rval); - break; + END_CASE(JSOP_DUP) - case JSOP_DUP2: + BEGIN_CASE(JSOP_DUP2) JS_ASSERT(sp - 1 > fp->spbase); lval = FETCH_OPND(-2); rval = FETCH_OPND(-1); PUSH_OPND(lval); PUSH_OPND(rval); - break; + END_CASE(JSOP_DUP2) #define PROPERTY_OP(n, call) \ JS_BEGIN_MACRO \ /* Fetch the left part and resolve it to a non-null object. */ \ FETCH_OBJECT(cx, n, lval, obj); \ \ /* Get or set the property, set ok false if error, true if success. */\ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ call; \ if (!ok) \ goto out; \ JS_END_MACRO #define ELEMENT_OP(n, call) \ JS_BEGIN_MACRO \ /* Fetch the right part and resolve it to an internal id. */ \ @@ -2519,27 +2636,27 @@ js_Interpret(JSContext *cx, jsbytecode * \ /* Fetch the left part and resolve it to a non-null object. */ \ FETCH_OBJECT(cx, n - 1, lval, obj); \ \ /* Ensure that id has a type suitable for use with obj. */ \ CHECK_ELEMENT_ID(obj, id); \ \ /* Get or set the element, set ok false if error, true if success. */ \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ call; \ if (!ok) \ goto out; \ JS_END_MACRO /* * Direct callers, i.e. those who do not wrap CACHED_GET and CACHED_SET calls - * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP(fp); beforehand, just - * in case a getter or setter function is invoked. CACHED_GET and CACHED_SET - * use cx, obj, id, and rval from their caller's lexical environment. + * in PROPERTY_OP or ELEMENT_OP macro calls must SAVE_SP_AND_PC(fp); before + * calling, just in case a getter or setter function is invoked. CACHED_GET + * and CACHED_SET use cx, obj, id, and rval from their callers' environments. */ #define CACHED_GET(call) CACHED_GET_VP(call, &rval) #define CACHED_GET_VP(call,vp) \ JS_BEGIN_MACRO \ if (!OBJ_IS_NATIVE(obj)) { \ ok = call; \ } else { \ @@ -2588,89 +2705,89 @@ js_Interpret(JSContext *cx, jsbytecode * JS_UNLOCK_OBJ(cx, obj); \ ok = call; \ /* No fill here: js_SetProperty writes through the cache. */ \ } \ } \ JS_END_MACRO #define BEGIN_LITOPX_CASE(OP,PCOFF) \ - case OP: \ + BEGIN_CASE(OP) \ atomIndex = GET_ATOM_INDEX(pc + PCOFF); \ do_##OP: \ atom = js_GetAtom(cx, &script->atomMap, atomIndex); -#define END_LITOPX_CASE \ - break; \ +#define END_LITOPX_CASE(OP) \ + END_CASE(OP) BEGIN_LITOPX_CASE(JSOP_SETCONST, 0) obj = fp->varobj; rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), rval, NULL, NULL, JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY, NULL); if (!ok) goto out; STORE_OPND(-1, rval); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_SETCONST) BEGIN_LITOPX_CASE(JSOP_BINDNAME, 0) - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_BINDNAME) - case JSOP_SETNAME: + BEGIN_CASE(JSOP_SETNAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); JS_ASSERT(!JSVAL_IS_PRIMITIVE(lval)); obj = JSVAL_TO_OBJECT(lval); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; sp--; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_SETNAME) #define INTEGER_OP(OP, EXTRA_CODE) \ JS_BEGIN_MACRO \ FETCH_INT(cx, -1, j); \ FETCH_INT(cx, -2, i); \ if (!ok) \ goto out; \ EXTRA_CODE \ d = i OP j; \ sp--; \ STORE_NUMBER(cx, -1, d); \ JS_END_MACRO #define BITWISE_OP(OP) INTEGER_OP(OP, (void) 0;) #define SIGNED_SHIFT_OP(OP) INTEGER_OP(OP, j &= 31;) - case JSOP_BITOR: + BEGIN_CASE(JSOP_BITOR) BITWISE_OP(|); - break; + END_CASE(JSOP_BITOR) - case JSOP_BITXOR: + BEGIN_CASE(JSOP_BITXOR) BITWISE_OP(^); - break; + END_CASE(JSOP_BITXOR) - case JSOP_BITAND: + BEGIN_CASE(JSOP_BITAND) BITWISE_OP(&); - break; + END_CASE(JSOP_BITAND) #define RELATIONAL_OP(OP) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ /* Optimize for two int-tagged operands (typical loop control). */ \ if ((lval & rval) & JSVAL_INT) { \ ltmp = lval ^ JSVAL_VOID; \ @@ -2712,39 +2829,39 @@ js_Interpret(JSContext *cx, jsbytecode * (rtmp == JSVAL_OBJECT && \ (obj2 = JSVAL_TO_OBJECT(rval)) && \ OBJECT_IS_XML(cx, obj2))) { \ JSXMLObjectOps *ops; \ \ ops = (JSXMLObjectOps *) obj2->map->ops; \ if (obj2 == JSVAL_TO_OBJECT(rval)) \ rval = lval; \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = ops->equality(cx, obj2, rval, &cond); \ if (!ok) \ goto out; \ cond = cond OP JS_TRUE; \ } else -#define XML_NAME_EQUALITY_OP(OP) \ +#define EXTENDED_EQUALITY_OP(OP) \ if (ltmp == JSVAL_OBJECT && \ (obj2 = JSVAL_TO_OBJECT(lval)) && \ ((clasp = OBJ_GET_CLASS(cx, obj2))->flags & JSCLASS_IS_EXTENDED)) { \ JSExtendedClass *xclasp; \ \ xclasp = (JSExtendedClass *) clasp; \ - SAVE_SP(fp); \ + SAVE_SP_AND_PC(fp); \ ok = xclasp->equality(cx, obj2, rval, &cond); \ if (!ok) \ goto out; \ cond = cond OP JS_TRUE; \ } else #else #define XML_EQUALITY_OP(OP) /* nothing */ -#define XML_NAME_EQUALITY_OP(OP) /* nothing */ +#define EXTENDED_EQUALITY_OP(OP) /* nothing */ #endif #define EQUALITY_OP(OP, IFNAN) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ ltmp = JSVAL_TAG(lval); \ rtmp = JSVAL_TAG(rval); \ @@ -2754,17 +2871,17 @@ js_Interpret(JSContext *cx, jsbytecode * str = JSVAL_TO_STRING(lval); \ str2 = JSVAL_TO_STRING(rval); \ cond = js_CompareStrings(str, str2) OP 0; \ } else if (ltmp == JSVAL_DOUBLE) { \ d = *JSVAL_TO_DOUBLE(lval); \ d2 = *JSVAL_TO_DOUBLE(rval); \ cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } else { \ - XML_NAME_EQUALITY_OP(OP) \ + EXTENDED_EQUALITY_OP(OP) \ /* Handle all undefined (=>NaN) and int combinations. */ \ cond = lval OP rval; \ } \ } else { \ if (JSVAL_IS_NULL(lval) || JSVAL_IS_VOID(lval)) { \ cond = (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) OP 1; \ } else if (JSVAL_IS_NULL(rval) || JSVAL_IS_VOID(rval)) { \ cond = 1 OP 0; \ @@ -2786,256 +2903,257 @@ js_Interpret(JSContext *cx, jsbytecode * cond = JSDOUBLE_COMPARE(d, OP, d2, IFNAN); \ } \ } \ } \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO - case JSOP_EQ: + BEGIN_CASE(JSOP_EQ) EQUALITY_OP(==, JS_FALSE); - break; + END_CASE(JSOP_EQ) - case JSOP_NE: + BEGIN_CASE(JSOP_NE) EQUALITY_OP(!=, JS_TRUE); - break; + END_CASE(JSOP_NE) #if !JS_BUG_FALLIBLE_EQOPS #define NEW_EQUALITY_OP(OP) \ JS_BEGIN_MACRO \ rval = FETCH_OPND(-1); \ lval = FETCH_OPND(-2); \ cond = js_StrictlyEqual(lval, rval) OP JS_TRUE; \ sp--; \ STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); \ JS_END_MACRO - case JSOP_NEW_EQ: + BEGIN_CASE(JSOP_NEW_EQ) NEW_EQUALITY_OP(==); - break; + END_CASE(JSOP_NEW_EQ) - case JSOP_NEW_NE: + BEGIN_CASE(JSOP_NEW_NE) NEW_EQUALITY_OP(!=); - break; + END_CASE(JSOP_NEW_NE) #if JS_HAS_SWITCH_STATEMENT - case JSOP_CASE: + BEGIN_CASE(JSOP_CASE) NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMP_OFFSET(pc); CHECK_BRANCH(len); - } else { - PUSH(lval); + DO_NEXT_OP(len); } - break; + PUSH(lval); + END_CASE(JSOP_CASE) - case JSOP_CASEX: + BEGIN_CASE(JSOP_CASEX) NEW_EQUALITY_OP(==); (void) POP(); if (cond) { len = GET_JUMPX_OFFSET(pc); CHECK_BRANCH(len); - } else { - PUSH(lval); + DO_NEXT_OP(len); } - break; + PUSH(lval); + END_CASE(JSOP_CASEX) #endif #endif /* !JS_BUG_FALLIBLE_EQOPS */ - case JSOP_LT: + BEGIN_CASE(JSOP_LT) RELATIONAL_OP(<); - break; + END_CASE(JSOP_LT) - case JSOP_LE: + BEGIN_CASE(JSOP_LE) RELATIONAL_OP(<=); - break; + END_CASE(JSOP_LE) - case JSOP_GT: + BEGIN_CASE(JSOP_GT) RELATIONAL_OP(>); - break; + END_CASE(JSOP_GT) - case JSOP_GE: + BEGIN_CASE(JSOP_GE) RELATIONAL_OP(>=); - break; + END_CASE(JSOP_GE) #undef EQUALITY_OP #undef RELATIONAL_OP - case JSOP_LSH: + BEGIN_CASE(JSOP_LSH) SIGNED_SHIFT_OP(<<); - break; + END_CASE(JSOP_LSH) - case JSOP_RSH: + BEGIN_CASE(JSOP_RSH) SIGNED_SHIFT_OP(>>); - break; + END_CASE(JSOP_RSH) - case JSOP_URSH: + BEGIN_CASE(JSOP_URSH) { uint32 u; FETCH_INT(cx, -1, j); FETCH_UINT(cx, -2, u); j &= 31; d = u >> j; sp--; STORE_NUMBER(cx, -1, d); - break; } + END_CASE(JSOP_URSH) #undef INTEGER_OP #undef BITWISE_OP #undef SIGNED_SHIFT_OP - case JSOP_ADD: + BEGIN_CASE(JSOP_ADD) rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); #if JS_HAS_XML_SUPPORT if (!JSVAL_IS_PRIMITIVE(lval) && (obj2 = JSVAL_TO_OBJECT(lval), OBJECT_IS_XML(cx, obj2)) && VALUE_IS_XML(cx, rval)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj2->map->ops; - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = ops->concatenate(cx, obj2, rval, &rval); if (!ok) goto out; sp--; STORE_OPND(-1, rval); - break; - } + } else #endif - VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, <mp); - VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rtmp); - if ((cond = JSVAL_IS_STRING(ltmp)) || JSVAL_IS_STRING(rtmp)) { - SAVE_SP(fp); - if (cond) { - str = JSVAL_TO_STRING(ltmp); - ok = (str2 = js_ValueToString(cx, rtmp)) != NULL; + { + VALUE_TO_PRIMITIVE(cx, lval, JSTYPE_VOID, <mp); + VALUE_TO_PRIMITIVE(cx, rval, JSTYPE_VOID, &rtmp); + if ((cond = JSVAL_IS_STRING(ltmp)) || JSVAL_IS_STRING(rtmp)) { + SAVE_SP_AND_PC(fp); + if (cond) { + str = JSVAL_TO_STRING(ltmp); + ok = (str2 = js_ValueToString(cx, rtmp)) != NULL; + } else { + str2 = JSVAL_TO_STRING(rtmp); + ok = (str = js_ValueToString(cx, ltmp)) != NULL; + } + if (!ok) + goto out; + str = js_ConcatStrings(cx, str, str2); + if (!str) { + ok = JS_FALSE; + goto out; + } + sp--; + STORE_OPND(-1, STRING_TO_JSVAL(str)); } else { - str2 = JSVAL_TO_STRING(rtmp); - ok = (str = js_ValueToString(cx, ltmp)) != NULL; - } - if (!ok) - goto out; - str = js_ConcatStrings(cx, str, str2); - if (!str) { - ok = JS_FALSE; - goto out; + VALUE_TO_NUMBER(cx, lval, d); + VALUE_TO_NUMBER(cx, rval, d2); + d += d2; + sp--; + STORE_NUMBER(cx, -1, d); } - sp--; - STORE_OPND(-1, STRING_TO_JSVAL(str)); - } else { - VALUE_TO_NUMBER(cx, lval, d); - VALUE_TO_NUMBER(cx, rval, d2); - d += d2; - sp--; - STORE_NUMBER(cx, -1, d); } - break; + END_CASE(JSOP_ADD) #define BINARY_OP(OP) \ JS_BEGIN_MACRO \ FETCH_NUMBER(cx, -1, d2); \ FETCH_NUMBER(cx, -2, d); \ d = d OP d2; \ sp--; \ STORE_NUMBER(cx, -1, d); \ JS_END_MACRO - case JSOP_SUB: + BEGIN_CASE(JSOP_SUB) BINARY_OP(-); - break; + END_CASE(JSOP_SUB) - case JSOP_MUL: + BEGIN_CASE(JSOP_MUL) BINARY_OP(*); - break; + END_CASE(JSOP_MUL) - case JSOP_DIV: + BEGIN_CASE(JSOP_DIV) FETCH_NUMBER(cx, -1, d2); FETCH_NUMBER(cx, -2, d); sp--; if (d2 == 0) { -#if defined(XP_WIN) +#ifdef XP_WIN /* XXX MSVC miscompiles such that (NaN == 0) */ if (JSDOUBLE_IS_NaN(d2)) rval = DOUBLE_TO_JSVAL(rt->jsNaN); else #endif if (d == 0 || JSDOUBLE_IS_NaN(d)) rval = DOUBLE_TO_JSVAL(rt->jsNaN); else if ((JSDOUBLE_HI32(d) ^ JSDOUBLE_HI32(d2)) >> 31) rval = DOUBLE_TO_JSVAL(rt->jsNegativeInfinity); else rval = DOUBLE_TO_JSVAL(rt->jsPositiveInfinity); STORE_OPND(-1, rval); } else { d /= d2; STORE_NUMBER(cx, -1, d); } - break; + END_CASE(JSOP_DIV) - case JSOP_MOD: + BEGIN_CASE(JSOP_MOD) FETCH_NUMBER(cx, -1, d2); FETCH_NUMBER(cx, -2, d); sp--; if (d2 == 0) { STORE_OPND(-1, DOUBLE_TO_JSVAL(rt->jsNaN)); } else { -#if defined(XP_WIN) +#ifdef XP_WIN /* Workaround MS fmod bug where 42 % (1/0) => NaN, not 42. */ if (!(JSDOUBLE_IS_FINITE(d) && JSDOUBLE_IS_INFINITE(d2))) #endif d = fmod(d, d2); STORE_NUMBER(cx, -1, d); } - break; + END_CASE(JSOP_MOD) - case JSOP_NOT: + BEGIN_CASE(JSOP_NOT) POP_BOOLEAN(cx, rval, cond); PUSH_OPND(BOOLEAN_TO_JSVAL(!cond)); - break; + END_CASE(JSOP_NOT) - case JSOP_BITNOT: + BEGIN_CASE(JSOP_BITNOT) FETCH_INT(cx, -1, i); d = (jsdouble) ~i; STORE_NUMBER(cx, -1, d); - break; + END_CASE(JSOP_BITNOT) - case JSOP_NEG: + BEGIN_CASE(JSOP_NEG) FETCH_NUMBER(cx, -1, d); #ifdef HPUX /* * Negation of a zero doesn't produce a negative * zero on HPUX. Perform the operation by bit * twiddling. */ JSDOUBLE_HI32(d) ^= JSDOUBLE_HI32_SIGNBIT; #else d = -d; #endif STORE_NUMBER(cx, -1, d); - break; + END_CASE(JSOP_NEG) - case JSOP_POS: + BEGIN_CASE(JSOP_POS) FETCH_NUMBER(cx, -1, d); STORE_NUMBER(cx, -1, d); - break; + END_CASE(JSOP_POS) - case JSOP_NEW: + BEGIN_CASE(JSOP_NEW) /* Get immediate argc and find the constructor function. */ argc = GET_ARGC(pc); #if JS_HAS_INITIALIZERS do_new: #endif - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); vp = sp - (2 + argc); JS_ASSERT(vp >= fp->spbase); fun = NULL; obj2 = NULL; lval = *vp; if (!JSVAL_IS_OBJECT(lval) || (obj2 = JSVAL_TO_OBJECT(lval)) == NULL || @@ -3092,126 +3210,133 @@ js_Interpret(JSContext *cx, jsbytecode * if (!ok) { cx->newborn[GCX_OBJECT] = NULL; goto out; } /* Check the return value and update obj from it. */ rval = *vp; if (JSVAL_IS_PRIMITIVE(rval)) { - if (fun || !JS_VERSION_IS_ECMA(cx)) { - *vp = OBJECT_TO_JSVAL(obj); - break; + if (!fun && JS_VERSION_IS_ECMA(cx)) { + /* native [[Construct]] returning primitive is error */ + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, + JSMSG_BAD_NEW_RESULT, + js_ValueToPrintableString(cx, rval)); + ok = JS_FALSE; + goto out; } - /* native [[Construct]] returning primitive is error */ - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_BAD_NEW_RESULT, - js_ValueToPrintableString(cx, rval)); - ok = JS_FALSE; - goto out; + *vp = OBJECT_TO_JSVAL(obj); + } else { + obj = JSVAL_TO_OBJECT(rval); } - obj = JSVAL_TO_OBJECT(rval); JS_RUNTIME_METER(rt, constructs); - break; + len = js_CodeSpec[op].length; + DO_NEXT_OP(len); - case JSOP_DELNAME: + BEGIN_CASE(JSOP_DELNAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; /* ECMA says to return true if name is undefined or inherited. */ rval = JSVAL_TRUE; if (prop) { OBJ_DROP_PROPERTY(cx, obj2, prop); ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; } PUSH_OPND(rval); - break; + END_CASE(JSOP_DELNAME) - case JSOP_DELPROP: + BEGIN_CASE(JSOP_DELPROP) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); STORE_OPND(-1, rval); - break; + END_CASE(JSOP_DELPROP) - case JSOP_DELELEM: + BEGIN_CASE(JSOP_DELELEM) ELEMENT_OP(-1, ok = OBJ_DELETE_PROPERTY(cx, obj, id, &rval)); sp--; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_DELELEM) - case JSOP_TYPEOF: + BEGIN_CASE(JSOP_TYPEOF) rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); type = JS_TypeOfValue(cx, rval); atom = rt->atomState.typeAtoms[type]; STORE_OPND(-1, ATOM_KEY(atom)); - break; + END_CASE(JSOP_TYPEOF) - case JSOP_VOID: + BEGIN_CASE(JSOP_VOID) (void) POP_OPND(); PUSH_OPND(JSVAL_VOID); - break; + END_CASE(JSOP_VOID) - case JSOP_INCNAME: - case JSOP_DECNAME: - case JSOP_NAMEINC: - case JSOP_NAMEDEC: + BEGIN_CASE(JSOP_INCNAME) + BEGIN_CASE(JSOP_DECNAME) + BEGIN_CASE(JSOP_NAMEINC) + BEGIN_CASE(JSOP_NAMEDEC) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (!prop) goto atom_not_defined; OBJ_DROP_PROPERTY(cx, obj2, prop); lval = OBJECT_TO_JSVAL(obj); i = 0; goto do_incop; - case JSOP_INCPROP: - case JSOP_DECPROP: - case JSOP_PROPINC: - case JSOP_PROPDEC: + BEGIN_CASE(JSOP_INCPROP) + BEGIN_CASE(JSOP_DECPROP) + BEGIN_CASE(JSOP_PROPINC) + BEGIN_CASE(JSOP_PROPDEC) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); lval = FETCH_OPND(-1); i = -1; goto do_incop; - case JSOP_INCELEM: - case JSOP_DECELEM: - case JSOP_ELEMINC: - case JSOP_ELEMDEC: + BEGIN_CASE(JSOP_INCELEM) + BEGIN_CASE(JSOP_DECELEM) + BEGIN_CASE(JSOP_ELEMINC) + BEGIN_CASE(JSOP_ELEMDEC) FETCH_ELEMENT_ID(-1, id); lval = FETCH_OPND(-2); i = -2; do_incop: + { + const JSCodeSpec *cs; + VALUE_TO_OBJECT(cx, lval, obj); if (i < 0) STORE_OPND(i, OBJECT_TO_JSVAL(obj)); CHECK_ELEMENT_ID(obj, id); /* The operand must contain a number. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; + /* Preload for use in the if/else immediately below. */ + cs = &js_CodeSpec[op]; + /* The expression result goes in rtmp, the updated value in rval. */ if (JSVAL_IS_INT(rval) && rval != INT_TO_JSVAL(JSVAL_INT_MIN) && rval != INT_TO_JSVAL(JSVAL_INT_MAX)) { if (cs->format & JOF_POST) { rtmp = rval; (cs->format & JOF_INC) ? (rval += 2) : (rval -= 2); } else { @@ -3251,17 +3376,19 @@ js_Interpret(JSContext *cx, jsbytecode * fp->flags |= JSFRAME_ASSIGNING; CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); fp->flags &= ~JSFRAME_ASSIGNING; if (!ok) goto out; sp += i; PUSH_OPND(rtmp); - break; + len = js_CodeSpec[op].length; + DO_NEXT_OP(len); + } /* * NB: This macro can't use JS_BEGIN_MACRO/JS_END_MACRO around its body because * it must break from the switch case that calls it, not from the do...while(0) * loop created by the JS_BEGIN/END_MACRO brackets. */ #define FAST_INCREMENT_OP(SLOT,COUNT,BASE,PRE,OP,MINMAX) \ slot = SLOT; \ @@ -3269,154 +3396,172 @@ js_Interpret(JSContext *cx, jsbytecode * vp = fp->BASE + slot; \ rval = *vp; \ if (JSVAL_IS_INT(rval) && \ rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ PRE = rval; \ rval OP 2; \ *vp = rval; \ PUSH_OPND(PRE); \ - break; \ + goto end_nonint_fast_incop; \ } \ goto do_nonint_fast_incop; - case JSOP_INCARG: + BEGIN_CASE(JSOP_INCARG) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, +=, MAX); - case JSOP_DECARG: + BEGIN_CASE(JSOP_DECARG) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rval, -=, MIN); - case JSOP_ARGINC: + BEGIN_CASE(JSOP_ARGINC) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, +=, MAX); - case JSOP_ARGDEC: + BEGIN_CASE(JSOP_ARGDEC) FAST_INCREMENT_OP(GET_ARGNO(pc), nargs, argv, rtmp, -=, MIN); - case JSOP_INCVAR: + BEGIN_CASE(JSOP_INCVAR) FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, +=, MAX); - case JSOP_DECVAR: + BEGIN_CASE(JSOP_DECVAR) FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rval, -=, MIN); - case JSOP_VARINC: + BEGIN_CASE(JSOP_VARINC) FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, +=, MAX); - case JSOP_VARDEC: + BEGIN_CASE(JSOP_VARDEC) FAST_INCREMENT_OP(GET_VARNO(pc), nvars, vars, rtmp, -=, MIN); + end_nonint_fast_incop: + len = JSOP_INCARG_LENGTH; /* all arg/var incops are same length */ + DO_NEXT_OP(len); + #undef FAST_INCREMENT_OP do_nonint_fast_incop: + { + const JSCodeSpec *cs = &js_CodeSpec[op]; + NONINT_INCREMENT_OP_MIDDLE(); *vp = rval; PUSH_OPND(rtmp); - break; + len = cs->length; + DO_NEXT_OP(len); + } #define FAST_GLOBAL_INCREMENT_OP(SLOWOP,PRE,OP,MINMAX) \ slot = GET_VARNO(pc); \ JS_ASSERT(slot < fp->nvars); \ lval = fp->vars[slot]; \ if (JSVAL_IS_NULL(lval)) { \ op = SLOWOP; \ - goto do_op; \ + DO_OP(); \ } \ slot = JSVAL_TO_INT(lval); \ obj = fp->varobj; \ rval = OBJ_GET_SLOT(cx, obj, slot); \ if (JSVAL_IS_INT(rval) && \ rval != INT_TO_JSVAL(JSVAL_INT_##MINMAX)) { \ PRE = rval; \ rval OP 2; \ OBJ_SET_SLOT(cx, obj, slot, rval); \ PUSH_OPND(PRE); \ - break; \ + goto end_nonint_fast_global_incop; \ } \ goto do_nonint_fast_global_incop; - case JSOP_INCGVAR: + BEGIN_CASE(JSOP_INCGVAR) FAST_GLOBAL_INCREMENT_OP(JSOP_INCNAME, rval, +=, MAX); - case JSOP_DECGVAR: + BEGIN_CASE(JSOP_DECGVAR) FAST_GLOBAL_INCREMENT_OP(JSOP_DECNAME, rval, -=, MIN); - case JSOP_GVARINC: + BEGIN_CASE(JSOP_GVARINC) FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEINC, rtmp, +=, MAX); - case JSOP_GVARDEC: + BEGIN_CASE(JSOP_GVARDEC) FAST_GLOBAL_INCREMENT_OP(JSOP_NAMEDEC, rtmp, -=, MIN); + end_nonint_fast_global_incop: + len = JSOP_INCGVAR_LENGTH; /* all gvar incops are same length */ + DO_NEXT_OP(len); + #undef FAST_GLOBAL_INCREMENT_OP do_nonint_fast_global_incop: + { + const JSCodeSpec *cs = &js_CodeSpec[op]; + NONINT_INCREMENT_OP_MIDDLE(); OBJ_SET_SLOT(cx, obj, slot, rval); PUSH_OPND(rtmp); - break; + len = cs->length; + DO_NEXT_OP(len); + } - case JSOP_GETPROP: + BEGIN_CASE(JSOP_GETPROP) /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); STORE_OPND(-1, rval); - break; + END_CASE(JSOP_GETPROP) - case JSOP_SETPROP: + BEGIN_CASE(JSOP_SETPROP) /* Pop the right-hand side into rval for OBJ_SET_PROPERTY. */ rval = FETCH_OPND(-1); /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_SETPROP) - case JSOP_GETELEM: + BEGIN_CASE(JSOP_GETELEM) ELEMENT_OP(-1, CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval))); sp--; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_GETELEM) - case JSOP_SETELEM: + BEGIN_CASE(JSOP_SETELEM) rval = FETCH_OPND(-1); ELEMENT_OP(-2, CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval))); sp -= 2; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_SETELEM) - case JSOP_ENUMELEM: + BEGIN_CASE(JSOP_ENUMELEM) /* Funky: the value to set is under the [obj, id] pair. */ FETCH_ELEMENT_ID(-1, id); FETCH_OBJECT(cx, -2, lval, obj); CHECK_ELEMENT_ID(obj, id); rval = FETCH_OPND(-3); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; sp -= 3; - break; + END_CASE(JSOP_ENUMELEM) /* * LAZY_ARGS_THISP allows the JSOP_ARGSUB bytecode to defer creation of the * arguments object until it is truly needed. JSOP_ARGSUB optimizes away * arguments objects when the only uses of the 'arguments' parameter are to * fetch individual actual parameters. But if such a use were then invoked, * e.g., arguments[i](), the 'this' parameter would and must bind to the * caller's arguments object. So JSOP_ARGSUB sets obj to LAZY_ARGS_THISP. */ #define LAZY_ARGS_THISP ((JSObject *) 1) - case JSOP_PUSHOBJ: + BEGIN_CASE(JSOP_PUSHOBJ) if (obj == LAZY_ARGS_THISP && !(obj = js_GetArgsObject(cx, fp))) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - break; + END_CASE(JSOP_PUSHOBJ) - case JSOP_CALL: - case JSOP_EVAL: + BEGIN_CASE(JSOP_CALL) + BEGIN_CASE(JSOP_EVAL) argc = GET_ARGC(pc); vp = sp - (argc + 2); lval = *vp; - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); if (JSVAL_IS_FUNCTION(cx, lval) && (obj = JSVAL_TO_OBJECT(lval), fun = (JSFunction *) JS_GetPrivate(cx, obj), fun->interpreted && !(fun->flags & (JSFUN_HEAVYWEIGHT | JSFUN_BOUND_METHOD)) && argc >= (uintN)(fun->nargs + fun->extra))) /* inline_call: */ @@ -3494,29 +3639,34 @@ js_Interpret(JSContext *cx, jsbytecode * if (hook) { newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0, cx->runtime->callHookData); LOAD_INTERRUPT_HANDLER(rt); } /* Switch to new version if currentVersion wasn't overridden. */ newifp->callerVersion = cx->version; - if (cx->version == currentVersion) { + if (JS_LIKELY(cx->version == currentVersion)) { currentVersion = script->version; if (currentVersion != cx->version) js_SetVersion(cx, currentVersion); } /* Push the frame and set interpreter registers. */ cx->fp = fp = &newifp->frame; pc = script->code; +#ifndef JS_THREADED_INTERP endpc = pc + script->length; +#endif inlineCallCount++; JS_RUNTIME_METER(rt, inlineCalls); - continue; + + /* Load first opcode and dispatch it (safe since JSOP_STOP). */ + op = *pc; + DO_OP(); bad_inline_call: script = fp->script; depth = (jsint) script->depth; goto out; } ok = js_Invoke(cx, argc, 0); @@ -3545,127 +3695,131 @@ js_Interpret(JSContext *cx, jsbytecode * PUSH_OPND(cx->rval2); cx->rval2set = JS_FALSE; ELEMENT_OP(-1, ok = OBJ_GET_PROPERTY(cx, obj, id, &rval)); sp--; STORE_OPND(-1, rval); } #endif obj = NULL; - break; + END_CASE(JSOP_CALL) #if JS_HAS_LVALUE_RETURN - case JSOP_SETCALL: + BEGIN_CASE(JSOP_SETCALL) argc = GET_ARGC(pc); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_Invoke(cx, argc, 0); RESTORE_SP(fp); LOAD_BRANCH_CALLBACK(cx); LOAD_INTERRUPT_HANDLER(rt); if (!ok) goto out; if (!cx->rval2set) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_LEFTSIDE_OF_ASS); ok = JS_FALSE; goto out; } PUSH_OPND(cx->rval2); cx->rval2set = JS_FALSE; obj = NULL; - break; + END_CASE(JSOP_SETCALL) #endif - case JSOP_NAME: + BEGIN_CASE(JSOP_NAME) atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_FindProperty(cx, id, &obj, &obj2, &prop); if (!ok) goto out; if (!prop) { /* Kludge to allow (typeof foo == "undefined") tests. */ + len = JSOP_NAME_LENGTH; + endpc = script->code + script->length; for (pc2 = pc + len; pc2 < endpc; pc2++) { op2 = (JSOp)*pc2; if (op2 == JSOP_TYPEOF) { PUSH_OPND(JSVAL_VOID); - goto advance_pc; + DO_NEXT_OP(len); } if (op2 != JSOP_GROUP) break; } goto atom_not_defined; } /* Take the slow path if prop was not found in a native object. */ if (!OBJ_IS_NATIVE(obj) || !OBJ_IS_NATIVE(obj2)) { OBJ_DROP_PROPERTY(cx, obj2, prop); ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; PUSH_OPND(rval); - break; - } - - /* Get and push the obj[id] property's value. */ - sprop = (JSScopeProperty *)prop; - slot = (uintN)sprop->slot; - rval = (slot != SPROP_INVALID_SLOT) - ? LOCKED_OBJ_GET_SLOT(obj2, slot) - : JSVAL_VOID; - JS_UNLOCK_OBJ(cx, obj2); - ok = SPROP_GET(cx, sprop, obj, obj2, &rval); - JS_LOCK_OBJ(cx, obj2); - if (!ok) { + } else { + /* Get and push the obj[id] property's value. */ + sprop = (JSScopeProperty *)prop; + slot = (uintN)sprop->slot; + rval = (slot != SPROP_INVALID_SLOT) + ? LOCKED_OBJ_GET_SLOT(obj2, slot) + : JSVAL_VOID; + JS_UNLOCK_OBJ(cx, obj2); + ok = SPROP_GET(cx, sprop, obj, obj2, &rval); + JS_LOCK_OBJ(cx, obj2); + if (!ok) { + OBJ_DROP_PROPERTY(cx, obj2, prop); + goto out; + } + if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) + LOCKED_OBJ_SET_SLOT(obj2, slot, rval); OBJ_DROP_PROPERTY(cx, obj2, prop); - goto out; + PUSH_OPND(rval); } - if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(obj2))) - LOCKED_OBJ_SET_SLOT(obj2, slot, rval); - OBJ_DROP_PROPERTY(cx, obj2, prop); - PUSH_OPND(rval); - break; + END_CASE(JSOP_NAME) - case JSOP_UINT16: + BEGIN_CASE(JSOP_UINT16) i = (jsint) GET_ATOM_INDEX(pc); rval = INT_TO_JSVAL(i); PUSH_OPND(rval); obj = NULL; - break; + END_CASE(JSOP_UINT16) - case JSOP_UINT24: + BEGIN_CASE(JSOP_UINT24) i = (jsint) GET_LITERAL_INDEX(pc); rval = INT_TO_JSVAL(i); PUSH_OPND(rval); - break; + END_CASE(JSOP_UINT24) - case JSOP_LITERAL: + BEGIN_CASE(JSOP_LITERAL) atomIndex = GET_LITERAL_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; - break; + END_CASE(JSOP_LITERAL) - case JSOP_FINDNAME: + BEGIN_CASE(JSOP_FINDNAME) atomIndex = GET_LITERAL_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_FindIdentifierBase(cx, ATOM_TO_JSID(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(ATOM_KEY(atom)); - break; + END_CASE(JSOP_FINDNAME) - case JSOP_LITOPX: + BEGIN_CASE(JSOP_LITOPX) atomIndex = GET_LITERAL_INDEX(pc); op = pc[1 + LITERAL_INDEX_LEN]; +#ifdef JS_THREADED_INTERP + pc += JSOP_LITOPX_LENGTH - js_CodeSpec[op].length; +#endif switch (op) { case JSOP_ANONFUNOBJ: goto do_JSOP_ANONFUNOBJ; case JSOP_BINDNAME: goto do_JSOP_BINDNAME; case JSOP_CLOSURE: goto do_JSOP_CLOSURE; case JSOP_DEFCONST: goto do_JSOP_DEFCONST; case JSOP_DEFFUN: goto do_JSOP_DEFFUN; case JSOP_DEFLOCALFUN: goto do_JSOP_DEFLOCALFUN; case JSOP_DEFVAR: goto do_JSOP_DEFVAR; @@ -3691,30 +3845,29 @@ js_Interpret(JSContext *cx, jsbytecode * case JSOP_XMLCDATA: goto do_JSOP_XMLCDATA; case JSOP_XMLCOMMENT: goto do_JSOP_XMLCOMMENT; case JSOP_XMLOBJECT: goto do_JSOP_XMLOBJECT; case JSOP_XMLPI: goto do_JSOP_XMLPI; #endif default: JS_ASSERT(0); } /* NOTREACHED */ - break; - case JSOP_NUMBER: - case JSOP_STRING: - case JSOP_OBJECT: + BEGIN_CASE(JSOP_NUMBER) + BEGIN_CASE(JSOP_STRING) + BEGIN_CASE(JSOP_OBJECT) atomIndex = GET_ATOM_INDEX(pc); do_JSOP_NUMBER: do_JSOP_STRING: do_JSOP_OBJECT: atom = js_GetAtom(cx, &script->atomMap, atomIndex); PUSH_OPND(ATOM_KEY(atom)); obj = NULL; - break; + END_CASE(JSOP_NUMBER) BEGIN_LITOPX_CASE(JSOP_REGEXP, 0) { JSRegExp *re; JSObject *funobj; /* * Push a regexp object for the atom mapped by the bytecode at pc, @@ -3818,34 +3971,34 @@ js_Interpret(JSContext *cx, jsbytecode * } else { fp->vars[slot] = rval; } } PUSH_OPND(rval); obj = NULL; } - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_REGEXP) - case JSOP_ZERO: + BEGIN_CASE(JSOP_ZERO) PUSH_OPND(JSVAL_ZERO); obj = NULL; - break; + END_CASE(JSOP_ZERO) - case JSOP_ONE: + BEGIN_CASE(JSOP_ONE) PUSH_OPND(JSVAL_ONE); obj = NULL; - break; + END_CASE(JSOP_ONE) - case JSOP_NULL: + BEGIN_CASE(JSOP_NULL) PUSH_OPND(JSVAL_NULL); obj = NULL; - break; + END_CASE(JSOP_NULL) - case JSOP_THIS: + BEGIN_CASE(JSOP_THIS) obj = fp->thisp; clasp = OBJ_GET_CLASS(cx, obj); if (clasp->flags & JSCLASS_IS_EXTENDED) { JSExtendedClass *xclasp; xclasp = (JSExtendedClass *) clasp; if (xclasp->outerObject) { obj = xclasp->outerObject(cx, obj); @@ -3853,43 +4006,43 @@ js_Interpret(JSContext *cx, jsbytecode * ok = JS_FALSE; goto out; } } } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - break; + END_CASE(JSOP_THIS) - case JSOP_FALSE: + BEGIN_CASE(JSOP_FALSE) PUSH_OPND(JSVAL_FALSE); obj = NULL; - break; + END_CASE(JSOP_FALSE) - case JSOP_TRUE: + BEGIN_CASE(JSOP_TRUE) PUSH_OPND(JSVAL_TRUE); obj = NULL; - break; + END_CASE(JSOP_TRUE) #if JS_HAS_SWITCH_STATEMENT - case JSOP_TABLESWITCH: + BEGIN_CASE(JSOP_TABLESWITCH) pc2 = pc; len = GET_JUMP_OFFSET(pc2); /* * ECMAv2 forbids conversion of discriminant, so we will skip to * the default case if the discriminant isn't already an int jsval. * (This opcode is emitted only for dense jsint-domain switches.) */ if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { rval = POP_OPND(); if (!JSVAL_IS_INT(rval)) - break; + DO_NEXT_OP(len); i = JSVAL_TO_INT(rval); } else { FETCH_INT(cx, -1, i); sp--; } pc2 += JUMP_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); @@ -3898,42 +4051,42 @@ js_Interpret(JSContext *cx, jsbytecode * i -= low; if ((jsuint)i < (jsuint)(high - low + 1)) { pc2 += JUMP_OFFSET_LEN + JUMP_OFFSET_LEN * i; off = (jsint) GET_JUMP_OFFSET(pc2); if (off) len = off; } - break; + END_VARLEN_CASE - case JSOP_LOOKUPSWITCH: + BEGIN_CASE(JSOP_LOOKUPSWITCH) lval = POP_OPND(); pc2 = pc; len = GET_JUMP_OFFSET(pc2); if (!JSVAL_IS_NUMBER(lval) && !JSVAL_IS_STRING(lval) && !JSVAL_IS_BOOLEAN(lval)) { - goto advance_pc; + DO_NEXT_OP(len); } pc2 += JUMP_OFFSET_LEN; npairs = (jsint) GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; #define SEARCH_PAIRS(MATCH_CODE) \ while (npairs) { \ atom = GET_ATOM(cx, script, pc2); \ rval = ATOM_KEY(atom); \ MATCH_CODE \ if (match) { \ pc2 += ATOM_INDEX_LEN; \ len = GET_JUMP_OFFSET(pc2); \ - goto advance_pc; \ + DO_NEXT_OP(len); \ } \ pc2 += ATOM_INDEX_LEN + JUMP_OFFSET_LEN; \ npairs--; \ } if (JSVAL_IS_STRING(lval)) { str = JSVAL_TO_STRING(lval); SEARCH_PAIRS( match = (JSVAL_IS_STRING(rval) && @@ -3947,32 +4100,32 @@ js_Interpret(JSContext *cx, jsbytecode * *JSVAL_TO_DOUBLE(rval) == d); ) } else { SEARCH_PAIRS( match = (lval == rval); ) } #undef SEARCH_PAIRS - break; + END_VARLEN_CASE - case JSOP_TABLESWITCHX: + BEGIN_CASE(JSOP_TABLESWITCHX) pc2 = pc; len = GET_JUMPX_OFFSET(pc2); /* * ECMAv2 forbids conversion of discriminant, so we will skip to * the default case if the discriminant isn't already an int jsval. * (This opcode is emitted only for dense jsint-domain switches.) */ if ((cx->version & JSVERSION_MASK) == JSVERSION_DEFAULT || (cx->version & JSVERSION_MASK) >= JSVERSION_1_4) { rval = POP_OPND(); if (!JSVAL_IS_INT(rval)) - break; + DO_NEXT_OP(len); i = JSVAL_TO_INT(rval); } else { FETCH_INT(cx, -1, i); sp--; } pc2 += JUMPX_OFFSET_LEN; low = GET_JUMP_OFFSET(pc2); @@ -3981,42 +4134,42 @@ js_Interpret(JSContext *cx, jsbytecode * i -= low; if ((jsuint)i < (jsuint)(high - low + 1)) { pc2 += JUMP_OFFSET_LEN + JUMPX_OFFSET_LEN * i; off = (jsint) GET_JUMPX_OFFSET(pc2); if (off) len = off; } - break; + END_VARLEN_CASE - case JSOP_LOOKUPSWITCHX: + BEGIN_CASE(JSOP_LOOKUPSWITCHX) lval = POP_OPND(); pc2 = pc; len = GET_JUMPX_OFFSET(pc2); if (!JSVAL_IS_NUMBER(lval) && !JSVAL_IS_STRING(lval) && !JSVAL_IS_BOOLEAN(lval)) { - goto advance_pc; + DO_NEXT_OP(len); } pc2 += JUMPX_OFFSET_LEN; npairs = (jsint) GET_ATOM_INDEX(pc2); pc2 += ATOM_INDEX_LEN; #define SEARCH_EXTENDED_PAIRS(MATCH_CODE) \ while (npairs) { \ atom = GET_ATOM(cx, script, pc2); \ rval = ATOM_KEY(atom); \ MATCH_CODE \ if (match) { \ pc2 += ATOM_INDEX_LEN; \ len = GET_JUMPX_OFFSET(pc2); \ - goto advance_pc; \ + DO_NEXT_OP(len); \ } \ pc2 += ATOM_INDEX_LEN + JUMPX_OFFSET_LEN; \ npairs--; \ } if (JSVAL_IS_STRING(lval)) { str = JSVAL_TO_STRING(lval); SEARCH_EXTENDED_PAIRS( match = (JSVAL_IS_STRING(rval) && @@ -4030,27 +4183,26 @@ js_Interpret(JSContext *cx, jsbytecode * *JSVAL_TO_DOUBLE(rval) == d); ) } else { SEARCH_EXTENDED_PAIRS( match = (lval == rval); ) } #undef SEARCH_EXTENDED_PAIRS - break; + END_VARLEN_CASE - case JSOP_CONDSWITCH: - break; + EMPTY_CASE(JSOP_CONDSWITCH) #endif /* JS_HAS_SWITCH_STATEMENT */ #if JS_HAS_EXPORT_IMPORT - case JSOP_EXPORTALL: - SAVE_SP(fp); + BEGIN_CASE(JSOP_EXPORTALL) obj = fp->varobj; + SAVE_SP_AND_PC(fp); ida = JS_Enumerate(cx, obj); if (!ida) { ok = JS_FALSE; } else { for (i = 0, j = ida->length; i < j; i++) { id = ida->vector[i]; ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) @@ -4063,199 +4215,200 @@ js_Interpret(JSContext *cx, jsbytecode * ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); if (!ok) break; } JS_DestroyIdArray(cx, ida); } - break; + END_CASE(JSOP_EXPORTALL) BEGIN_LITOPX_CASE(JSOP_EXPORTNAME, 0) id = ATOM_TO_JSID(atom); obj = fp->varobj; - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop); if (!ok) goto out; if (!prop) { ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, NULL, NULL, JSPROP_EXPORTED, NULL); } else { ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs); if (ok) { attrs |= JSPROP_EXPORTED; ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, &attrs); } OBJ_DROP_PROPERTY(cx, obj2, prop); } if (!ok) goto out; - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_EXPORTNAME) - case JSOP_IMPORTALL: + BEGIN_CASE(JSOP_IMPORTALL) id = (jsid) JSVAL_VOID; PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; - break; + END_CASE(JSOP_IMPORTALL) - case JSOP_IMPORTPROP: + BEGIN_CASE(JSOP_IMPORTPROP) /* Get an immediate atom naming the property. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); PROPERTY_OP(-1, ok = ImportProperty(cx, obj, id)); sp--; - break; + END_CASE(JSOP_IMPORTPROP) - case JSOP_IMPORTELEM: + BEGIN_CASE(JSOP_IMPORTELEM) ELEMENT_OP(-1, ok = ImportProperty(cx, obj, id)); sp -= 2; - break; + END_CASE(JSOP_IMPORTELEM) #endif /* JS_HAS_EXPORT_IMPORT */ - case JSOP_TRAP: + BEGIN_CASE(JSOP_TRAP) + SAVE_SP_AND_PC(fp); switch (JS_HandleTrap(cx, script, pc, &rval)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: JS_ASSERT(JSVAL_IS_INT(rval)); op = (JSOp) JSVAL_TO_INT(rval); JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT); LOAD_INTERRUPT_HANDLER(rt); - goto do_op; + DO_OP(); case JSTRAP_RETURN: fp->rval = rval; goto out; #if JS_HAS_EXCEPTIONS case JSTRAP_THROW: cx->throwing = JS_TRUE; cx->exception = rval; ok = JS_FALSE; goto out; #endif /* JS_HAS_EXCEPTIONS */ default:; } LOAD_INTERRUPT_HANDLER(rt); - break; + END_CASE(JSOP_TRAP) - case JSOP_ARGUMENTS: - SAVE_SP(fp); + BEGIN_CASE(JSOP_ARGUMENTS) + SAVE_SP_AND_PC(fp); ok = js_GetArgsValue(cx, fp, &rval); if (!ok) goto out; PUSH_OPND(rval); - break; + END_CASE(JSOP_ARGUMENTS) - case JSOP_ARGSUB: + BEGIN_CASE(JSOP_ARGSUB) id = INT_TO_JSID(GET_ARGNO(pc)); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) goto out; if (!obj) { /* * If arguments was not overridden by eval('arguments = ...'), * set obj to the magic cookie respected by JSOP_PUSHOBJ, just * in case this bytecode is part of an 'arguments[i](j, k)' or * similar such invocation sequence, where the function that * is invoked expects its 'this' parameter to be the caller's * arguments object. */ obj = LAZY_ARGS_THISP; } PUSH_OPND(rval); - break; + END_CASE(JSOP_ARGSUB) #undef LAZY_ARGS_THISP - case JSOP_ARGCNT: + BEGIN_CASE(JSOP_ARGCNT) id = ATOM_TO_JSID(rt->atomState.lengthAtom); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_GetArgsProperty(cx, fp, id, &obj, &rval); if (!ok) goto out; PUSH_OPND(rval); - break; + END_CASE(JSOP_ARGCNT) - case JSOP_GETARG: + BEGIN_CASE(JSOP_GETARG) slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); PUSH_OPND(fp->argv[slot]); obj = NULL; - break; + END_CASE(JSOP_GETARG) - case JSOP_SETARG: + BEGIN_CASE(JSOP_SETARG) slot = GET_ARGNO(pc); JS_ASSERT(slot < fp->fun->nargs); vp = &fp->argv[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; - break; + END_CASE(JSOP_SETARG) - case JSOP_GETVAR: + BEGIN_CASE(JSOP_GETVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->fun->nvars); PUSH_OPND(fp->vars[slot]); obj = NULL; - break; + END_CASE(JSOP_GETVAR) - case JSOP_SETVAR: + BEGIN_CASE(JSOP_SETVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->fun->nvars); vp = &fp->vars[slot]; GC_POKE(cx, *vp); *vp = FETCH_OPND(-1); obj = NULL; - break; + END_CASE(JSOP_SETVAR) - case JSOP_GETGVAR: + BEGIN_CASE(JSOP_GETGVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->nvars); lval = fp->vars[slot]; if (JSVAL_IS_NULL(lval)) { op = JSOP_NAME; - goto do_op; + DO_OP(); } slot = JSVAL_TO_INT(lval); obj = fp->varobj; rval = OBJ_GET_SLOT(cx, obj, slot); PUSH_OPND(rval); - break; + END_CASE(JSOP_GETGVAR) - case JSOP_SETGVAR: + BEGIN_CASE(JSOP_SETGVAR) slot = GET_VARNO(pc); JS_ASSERT(slot < fp->nvars); rval = FETCH_OPND(-1); lval = fp->vars[slot]; obj = fp->varobj; if (JSVAL_IS_NULL(lval)) { /* * Inline-clone and specialize JSOP_SETNAME code here because * JSOP_SETGVAR has arity 1: [rval], not arity 2: [obj, rval] * as JSOP_SETNAME does, where [obj] is due to JSOP_BINDNAME. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); if (!ok) goto out; STORE_OPND(-1, rval); } else { slot = JSVAL_TO_INT(lval); GC_POKE(cx, obj->slots[slot]); OBJ_SET_SLOT(cx, obj, slot, rval); } - break; + END_CASE(JSOP_SETGVAR) - case JSOP_DEFCONST: - case JSOP_DEFVAR: + BEGIN_CASE(JSOP_DEFCONST) + BEGIN_CASE(JSOP_DEFVAR) atomIndex = GET_ATOM_INDEX(pc); do_JSOP_DEFCONST: do_JSOP_DEFVAR: atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = fp->varobj; attrs = JSPROP_ENUMERATE; if (!(fp->flags & JSFRAME_EVAL)) @@ -4299,17 +4452,17 @@ js_Interpret(JSContext *cx, jsbytecode * * tagged as a jsval. The atomIndex for the global's * name literal is identical to its fp->vars index. */ fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } } OBJ_DROP_PROPERTY(cx, obj2, prop); - break; + END_CASE(JSOP_DEFVAR) BEGIN_LITOPX_CASE(JSOP_DEFFUN, 0) { uintN flags; atomIndex = GET_ATOM_INDEX(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = ATOM_TO_OBJECT(atom); @@ -4417,17 +4570,17 @@ js_Interpret(JSContext *cx, jsbytecode * * its permanent fp->varobj slot number, tagged as a jsval. */ sprop = (JSScopeProperty *) prop; fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } #endif OBJ_DROP_PROPERTY(cx, parent, prop); } - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_DEFFUN) #if JS_HAS_LEXICAL_CLOSURE BEGIN_LITOPX_CASE(JSOP_DEFLOCALFUN, VARNO_LEN) /* * Define a local function (i.e., one nested at the top level of * another function), parented by the current scope chain, and * stored in a local variable slot that the compiler allocated. * This is an optimization over JSOP_DEFFUN that avoids requiring @@ -4435,58 +4588,58 @@ js_Interpret(JSContext *cx, jsbytecode * */ slot = GET_VARNO(pc); atom = js_GetAtom(cx, &script->atomMap, atomIndex); obj = ATOM_TO_OBJECT(atom); fun = (JSFunction *) JS_GetPrivate(cx, obj); parent = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != parent) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; goto out; } } fp->vars[slot] = OBJECT_TO_JSVAL(obj); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_DEFLOCALFUN) BEGIN_LITOPX_CASE(JSOP_ANONFUNOBJ, 0) /* Push the specified function object literal. */ obj = ATOM_TO_OBJECT(atom); /* If re-parenting, push a clone of the function object. */ parent = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != parent) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_CloneFunctionObject(cx, obj, parent); if (!obj) { ok = JS_FALSE; goto out; } } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_ANONFUNOBJ) BEGIN_LITOPX_CASE(JSOP_NAMEDFUNOBJ, 0) /* ECMA ed. 3 FunctionExpression: function Identifier [etc.]. */ rval = ATOM_KEY(atom); JS_ASSERT(JSVAL_IS_FUNCTION(cx, rval)); /* * 1. Create a new object as if by the expression new Object(). * 2. Add Result(1) to the front of the scope chain. * * Step 2 is achieved by making the new object's parent be the * current scope chain, and then making the new object the parent * of the Function object clone. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj2 = fp->scopeChain; parent = js_ConstructObject(cx, &js_ObjectClass, NULL, obj2, 0, NULL); if (!parent) { ok = JS_FALSE; goto out; } @@ -4550,17 +4703,17 @@ js_Interpret(JSContext *cx, jsbytecode * } /* * 5. Remove Result(1) from the front of the scope chain [no-op]. * 6. Return Result(3). */ PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_NAMEDFUNOBJ) BEGIN_LITOPX_CASE(JSOP_CLOSURE, 0) /* * ECMA ed. 3 extension: a named function expression in a compound * statement (not at the top statement level of global code, or at * the top level of a function body). * * Get immediate operand atom, which is a function object literal. @@ -4571,17 +4724,17 @@ js_Interpret(JSContext *cx, jsbytecode * /* * Clone the function object with the current scope chain as the * clone's parent. The original function object is the prototype * of the clone. Do this only if re-parenting; the compiler may * have seen the right parent already and created a sufficiently * well-scoped function object. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj2 = fp->scopeChain; if (OBJ_GET_PARENT(cx, obj) != obj2) { obj = js_CloneFunctionObject(cx, obj, obj2); if (!obj) { ok = JS_FALSE; goto out; } } @@ -4631,26 +4784,23 @@ js_Interpret(JSContext *cx, jsbytecode * * use fp->vars to map the global function name's atomIndex to * its permanent fp->varobj slot number, tagged as a jsval. */ sprop = (JSScopeProperty *) prop; fp->vars[atomIndex] = INT_TO_JSVAL(sprop->slot); } #endif OBJ_DROP_PROPERTY(cx, parent, prop); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_CLOSURE) #endif /* JS_HAS_LEXICAL_CLOSURE */ #if JS_HAS_GETTER_SETTER - case JSOP_GETTER: - case JSOP_SETTER: - JS_ASSERT(len == 1); + BEGIN_CASE(JSOP_GETTER) + BEGIN_CASE(JSOP_SETTER) op2 = (JSOp) *++pc; - cs = &js_CodeSpec[op2]; - len = cs->length; switch (op2) { case JSOP_SETNAME: case JSOP_SETPROP: atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); i = -1; goto gs_pop_lval; @@ -4700,17 +4850,17 @@ js_Interpret(JSContext *cx, jsbytecode * ok = JS_FALSE; goto out; } /* * Getters and setters are just like watchpoints from an access * control point of view. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &rtmp, &attrs); if (!ok) goto out; if (op == JSOP_GETTER) { getter = (JSPropertyOp) JSVAL_TO_OBJECT(rval); setter = NULL; attrs = JSPROP_GETTER; @@ -4727,50 +4877,51 @@ js_Interpret(JSContext *cx, jsbytecode * goto out; ok = OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID, getter, setter, attrs, NULL); if (!ok) goto out; sp += i; - if (cs->ndefs) + if (js_CodeSpec[op2].ndefs) STORE_OPND(-1, rval); - break; + len = js_CodeSpec[op2].length; + DO_NEXT_OP(len); #endif /* JS_HAS_GETTER_SETTER */ #if JS_HAS_INITIALIZERS - case JSOP_NEWINIT: + BEGIN_CASE(JSOP_NEWINIT) argc = 0; fp->sharpDepth++; goto do_new; - case JSOP_ENDINIT: + BEGIN_CASE(JSOP_ENDINIT) if (--fp->sharpDepth == 0) fp->sharpArray = NULL; /* Re-set the newborn root to the top of this object tree. */ JS_ASSERT(sp - fp->spbase >= 1); lval = FETCH_OPND(-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); cx->newborn[GCX_OBJECT] = JSVAL_TO_GCTHING(lval); - break; + END_CASE(JSOP_ENDINIT) - case JSOP_INITPROP: + BEGIN_CASE(JSOP_INITPROP) /* Pop the property's value into rval. */ JS_ASSERT(sp - fp->spbase >= 2); rval = FETCH_OPND(-1); /* Get the immediate property name into id. */ atom = GET_ATOM(cx, script, pc); id = ATOM_TO_JSID(atom); i = -1; goto do_init; - case JSOP_INITELEM: + BEGIN_CASE(JSOP_INITELEM) /* Pop the element's value into rval. */ JS_ASSERT(sp - fp->spbase >= 3); rval = FETCH_OPND(-1); /* Pop and conditionally atomize the element id. */ FETCH_ELEMENT_ID(-2, id); i = -2; @@ -4779,26 +4930,27 @@ js_Interpret(JSContext *cx, jsbytecode * lval = FETCH_OPND(i-1); JS_ASSERT(JSVAL_IS_OBJECT(lval)); obj = JSVAL_TO_OBJECT(lval); /* Ensure that id has a type suitable for use with obj. */ CHECK_ELEMENT_ID(obj, id); /* Set the property named by obj[id] to rval. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; sp += i; - break; + len = js_CodeSpec[op].length; + DO_NEXT_OP(len); #if JS_HAS_SHARP_VARS - case JSOP_DEFSHARP: - SAVE_SP(fp); + BEGIN_CASE(JSOP_DEFSHARP) + SAVE_SP_AND_PC(fp); obj = fp->sharpArray; if (!obj) { obj = js_NewArrayObject(cx, 0, NULL); if (!obj) { ok = JS_FALSE; goto out; } fp->sharpArray = obj; @@ -4812,89 +4964,87 @@ js_Interpret(JSContext *cx, jsbytecode * JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SHARP_DEF, numBuf); ok = JS_FALSE; goto out; } ok = OBJ_SET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; - break; + END_CASE(JSOP_DEFSHARP) - case JSOP_USESHARP: + BEGIN_CASE(JSOP_USESHARP) i = (jsint) GET_ATOM_INDEX(pc); id = INT_TO_JSID(i); obj = fp->sharpArray; if (!obj) { rval = JSVAL_VOID; } else { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_GET_PROPERTY(cx, obj, id, &rval); if (!ok) goto out; } if (!JSVAL_IS_OBJECT(rval)) { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%u", (unsigned) i); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SHARP_USE, numBuf); ok = JS_FALSE; goto out; } PUSH_OPND(rval); - break; + END_CASE(JSOP_USESHARP) #endif /* JS_HAS_SHARP_VARS */ #endif /* JS_HAS_INITIALIZERS */ #if JS_HAS_EXCEPTIONS /* No-ops for ease of decompilation and jit'ing. */ - case JSOP_TRY: - case JSOP_FINALLY: - break; + EMPTY_CASE(JSOP_TRY) + EMPTY_CASE(JSOP_FINALLY) /* Reset the stack to the given depth. */ - case JSOP_SETSP: + BEGIN_CASE(JSOP_SETSP) i = (jsint) GET_ATOM_INDEX(pc); JS_ASSERT(i >= 0); sp = fp->spbase + i; obj = fp->scopeChain; while (OBJ_GET_CLASS(cx, obj) == &js_WithClass && JSVAL_TO_INT(OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE)) > i) { obj = OBJ_GET_PARENT(cx, obj); } fp->scopeChain = obj; - break; + END_CASE(JSOP_SETSP) - case JSOP_GOSUB: - i = PTRDIFF(pc, script->main, jsbytecode) + len; + BEGIN_CASE(JSOP_GOSUB) + i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUB_LENGTH; len = GET_JUMP_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); - break; + END_VARLEN_CASE - case JSOP_GOSUBX: - i = PTRDIFF(pc, script->main, jsbytecode) + len; + BEGIN_CASE(JSOP_GOSUBX) + i = PTRDIFF(pc, script->main, jsbytecode) + JSOP_GOSUBX_LENGTH; len = GET_JUMPX_OFFSET(pc); PUSH(INT_TO_JSVAL(i)); - break; + END_VARLEN_CASE - case JSOP_RETSUB: + BEGIN_CASE(JSOP_RETSUB) rval = POP(); JS_ASSERT(JSVAL_IS_INT(rval)); - i = JSVAL_TO_INT(rval); - pc = script->main + i; - len = 0; - break; + len = JSVAL_TO_INT(rval); + pc = script->main; + END_VARLEN_CASE - case JSOP_EXCEPTION: + BEGIN_CASE(JSOP_EXCEPTION) PUSH(cx->exception); cx->throwing = JS_FALSE; - break; + END_CASE(JSOP_EXCEPTION) - case JSOP_THROW: + BEGIN_CASE(JSOP_THROW) cx->throwing = JS_TRUE; cx->exception = POP_OPND(); ok = JS_FALSE; /* let the code at out try to catch the exception. */ goto out; BEGIN_LITOPX_CASE(JSOP_INITCATCHVAR, 0) /* Load the value into rval, while keeping it live on stack. */ @@ -4905,59 +5055,58 @@ js_Interpret(JSContext *cx, jsbytecode * id = ATOM_TO_JSID(atom); /* Find the object being initialized at top of stack. */ lval = FETCH_OPND(-2); JS_ASSERT(JSVAL_IS_OBJECT(lval)); obj = JSVAL_TO_OBJECT(lval); /* Define obj[id] to contain rval and to be permanent. */ - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = OBJ_DEFINE_PROPERTY(cx, obj, id, rval, NULL, NULL, JSPROP_PERMANENT, NULL); if (!ok) goto out; /* Now that we're done with rval, pop it. */ sp--; - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_INITCATCHVAR) #endif /* JS_HAS_EXCEPTIONS */ #if JS_HAS_INSTANCEOF - case JSOP_INSTANCEOF: + BEGIN_CASE(JSOP_INSTANCEOF) + SAVE_SP_AND_PC(fp); rval = FETCH_OPND(-1); if (JSVAL_IS_PRIMITIVE(rval) || !(obj = JSVAL_TO_OBJECT(rval))->map->ops->hasInstance) { - SAVE_SP(fp); str = js_DecompileValueGenerator(cx, -1, rval, NULL); if (str) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INSTANCEOF_RHS, JS_GetStringBytes(str)); } ok = JS_FALSE; goto out; } lval = FETCH_OPND(-2); cond = JS_FALSE; - SAVE_SP(fp); ok = obj->map->ops->hasInstance(cx, obj, lval, &cond); if (!ok) goto out; sp--; STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond)); - break; + END_CASE(JSOP_INSTANCEOF) #endif /* JS_HAS_INSTANCEOF */ #if JS_HAS_DEBUGGER_KEYWORD - case JSOP_DEBUGGER: + BEGIN_CASE(JSOP_DEBUGGER) { JSTrapHandler handler = rt->debuggerHandler; if (handler) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); switch (handler(cx, script, pc, &rval, rt->debuggerHandlerData)) { case JSTRAP_ERROR: ok = JS_FALSE; goto out; case JSTRAP_CONTINUE: break; case JSTRAP_RETURN: @@ -4969,350 +5118,355 @@ js_Interpret(JSContext *cx, jsbytecode * cx->exception = rval; ok = JS_FALSE; goto out; #endif /* JS_HAS_EXCEPTIONS */ default:; } LOAD_INTERRUPT_HANDLER(rt); } - break; } + END_CASE(JSOP_DEBUGGER) #endif /* JS_HAS_DEBUGGER_KEYWORD */ #if JS_HAS_XML_SUPPORT - case JSOP_DEFXMLNS: + BEGIN_CASE(JSOP_DEFXMLNS) rval = POP(); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_SetDefaultXMLNamespace(cx, rval); if (!ok) goto out; - break; + END_CASE(JSOP_DEFXMLNS) - case JSOP_ANYNAME: - SAVE_SP(fp); + BEGIN_CASE(JSOP_ANYNAME) + SAVE_SP_AND_PC(fp); ok = js_GetAnyName(cx, &rval); if (!ok) goto out; PUSH_OPND(rval); - break; + END_CASE(JSOP_ANYNAME) BEGIN_LITOPX_CASE(JSOP_QNAMEPART, 0) PUSH_OPND(ATOM_KEY(atom)); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_QNAMEPART) BEGIN_LITOPX_CASE(JSOP_QNAMECONST, 0) rval = ATOM_KEY(atom); lval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_ConstructXMLQNameObject(cx, lval, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_QNAMECONST) - case JSOP_QNAME: + BEGIN_CASE(JSOP_QNAME) rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_ConstructXMLQNameObject(cx, lval, rval); if (!obj) { ok = JS_FALSE; goto out; } sp--; STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - break; + END_CASE(JSOP_QNAME) - case JSOP_TOATTRNAME: + BEGIN_CASE(JSOP_TOATTRNAME) rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_ToAttributeName(cx, &rval); if (!ok) goto out; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_TOATTRNAME) - case JSOP_TOATTRVAL: + BEGIN_CASE(JSOP_TOATTRVAL) rval = FETCH_OPND(-1); JS_ASSERT(JSVAL_IS_STRING(rval)); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); str = js_EscapeAttributeValue(cx, JSVAL_TO_STRING(rval)); if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); - break; + END_CASE(JSOP_TOATTRVAL) - case JSOP_ADDATTRNAME: - case JSOP_ADDATTRVAL: + BEGIN_CASE(JSOP_ADDATTRNAME) + BEGIN_CASE(JSOP_ADDATTRVAL) rval = FETCH_OPND(-1); lval = FETCH_OPND(-2); str = JSVAL_TO_STRING(lval); str2 = JSVAL_TO_STRING(rval); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); str = js_AddAttributePart(cx, op == JSOP_ADDATTRNAME, str, str2); if (!str) { ok = JS_FALSE; goto out; } sp--; STORE_OPND(-1, STRING_TO_JSVAL(str)); - break; + END_CASE(JSOP_ADDATTRNAME) - case JSOP_BINDXMLNAME: + BEGIN_CASE(JSOP_BINDXMLNAME) lval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_FindXMLProperty(cx, lval, &obj, &rval); if (!ok) goto out; STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); PUSH_OPND(rval); - break; + END_CASE(JSOP_BINDXMLNAME) - case JSOP_SETXMLNAME: + BEGIN_CASE(JSOP_SETXMLNAME) obj = JSVAL_TO_OBJECT(FETCH_OPND(-3)); lval = FETCH_OPND(-2); rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_SetXMLProperty(cx, obj, lval, &rval); if (!ok) goto out; sp -= 2; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_SETXMLNAME) - case JSOP_XMLNAME: + BEGIN_CASE(JSOP_XMLNAME) lval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_FindXMLProperty(cx, lval, &obj, &rval); if (!ok) goto out; ok = js_GetXMLProperty(cx, obj, rval, &rval); if (!ok) goto out; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_XMLNAME) - case JSOP_DESCENDANTS: - case JSOP_DELDESC: + BEGIN_CASE(JSOP_DESCENDANTS) + BEGIN_CASE(JSOP_DELDESC) FETCH_OBJECT(cx, -2, lval, obj); rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); ok = js_GetXMLDescendants(cx, obj, rval, &rval); if (!ok) goto out; if (op == JSOP_DELDESC) { sp[-1] = rval; /* set local root */ ok = js_DeleteXMLListElements(cx, JSVAL_TO_OBJECT(rval)); if (!ok) goto out; rval = JSVAL_TRUE; /* always succeed */ } sp--; STORE_OPND(-1, rval); - break; + END_CASE(JSOP_DESCENDANTS) - case JSOP_FILTER: + BEGIN_CASE(JSOP_FILTER) FETCH_OBJECT(cx, -1, lval, obj); len = GET_JUMP_OFFSET(pc); - SAVE_SP(fp); - ok = js_FilterXMLList(cx, obj, pc + cs->length, &rval); + SAVE_SP_AND_PC(fp); + ok = js_FilterXMLList(cx, obj, pc + js_CodeSpec[op].length, &rval); if (!ok) goto out; JS_ASSERT(fp->sp == sp); STORE_OPND(-1, rval); - break; + END_VARLEN_CASE - case JSOP_ENDFILTER: + BEGIN_CASE(JSOP_ENDFILTER) *result = POP_OPND(); goto out; - case JSOP_STARTXML: - case JSOP_STARTXMLEXPR: - break; + EMPTY_CASE(JSOP_STARTXML) + EMPTY_CASE(JSOP_STARTXMLEXPR) - case JSOP_TOXML: + BEGIN_CASE(JSOP_TOXML) rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_ValueToXMLObject(cx, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - break; + END_CASE(JSOP_TOXML) - case JSOP_TOXMLLIST: + BEGIN_CASE(JSOP_TOXMLLIST) rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_ValueToXMLListObject(cx, rval); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - break; + END_CASE(JSOP_TOXMLLIST) - case JSOP_XMLTAGEXPR: + BEGIN_CASE(JSOP_XMLTAGEXPR) rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); str = js_ValueToString(cx, rval); if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); - break; + END_CASE(JSOP_XMLTAGEXPR) - case JSOP_XMLELTEXPR: + BEGIN_CASE(JSOP_XMLELTEXPR) rval = FETCH_OPND(-1); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); if (VALUE_IS_XML(cx, rval)) { str = js_ValueToXMLString(cx, rval); } else { str = js_ValueToString(cx, rval); if (str) str = js_EscapeElementValue(cx, str); } if (!str) { ok = JS_FALSE; goto out; } STORE_OPND(-1, STRING_TO_JSVAL(str)); - break; + END_CASE(JSOP_XMLELTEXPR) BEGIN_LITOPX_CASE(JSOP_XMLOBJECT, 0) - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_CloneXMLObject(cx, ATOM_TO_OBJECT(atom)); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); obj = NULL; - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_XMLOBJECT) BEGIN_LITOPX_CASE(JSOP_XMLCDATA, 0) str = ATOM_TO_STRING(atom); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_TEXT, NULL, str); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_XMLCDATA) BEGIN_LITOPX_CASE(JSOP_XMLCOMMENT, 0) str = ATOM_TO_STRING(atom); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_COMMENT, NULL, str); if (!obj) { ok = JS_FALSE; goto out; } PUSH_OPND(OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_XMLCOMMENT) BEGIN_LITOPX_CASE(JSOP_XMLPI, 0) str = ATOM_TO_STRING(atom); rval = FETCH_OPND(-1); str2 = JSVAL_TO_STRING(rval); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); obj = js_NewXMLSpecialObject(cx, JSXML_CLASS_PROCESSING_INSTRUCTION, str, str2); if (!obj) { ok = JS_FALSE; goto out; } STORE_OPND(-1, OBJECT_TO_JSVAL(obj)); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_XMLPI) BEGIN_LITOPX_CASE(JSOP_GETMETHOD, 0) /* Get an immediate atom naming the property. */ id = ATOM_TO_JSID(atom); FETCH_OBJECT(cx, -1, lval, obj); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); /* Special-case XML object method lookup, per ECMA-357. */ if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj->map->ops; obj = ops->getMethod(cx, obj, id, &rval); if (!obj) ok = JS_FALSE; } else { CACHED_GET(OBJ_GET_PROPERTY(cx, obj, id, &rval)); } if (!ok) goto out; STORE_OPND(-1, rval); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_GETMETHOD) BEGIN_LITOPX_CASE(JSOP_SETMETHOD, 0) /* Get an immediate atom naming the property. */ id = ATOM_TO_JSID(atom); rval = FETCH_OPND(-1); FETCH_OBJECT(cx, -2, lval, obj); - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); /* Special-case XML object method lookup, per ECMA-357. */ if (OBJECT_IS_XML(cx, obj)) { JSXMLObjectOps *ops; ops = (JSXMLObjectOps *) obj->map->ops; ok = ops->setMethod(cx, obj, id, &rval); } else { CACHED_SET(OBJ_SET_PROPERTY(cx, obj, id, &rval)); } if (!ok) goto out; --sp; STORE_OPND(-1, rval); - END_LITOPX_CASE + END_LITOPX_CASE(JSOP_SETMETHOD) - case JSOP_GETFUNNS: + BEGIN_CASE(JSOP_GETFUNNS) ok = js_GetFunctionNamespace(cx, &rval); if (!ok) goto out; PUSH_OPND(rval); - break; + END_CASE(JSOP_GETFUNNS) - case JSOP_FOREACH: + BEGIN_CASE(JSOP_FOREACH) foreach = JS_TRUE; - break; + END_CASE(JSOP_FOREACH) #endif /* JS_HAS_XML_SUPPORT */ +#ifdef JS_THREADED_INTERP + L_JSOP_BACKPATCH: + L_JSOP_BACKPATCH_PUSH: + L_JSOP_BACKPATCH_POP: +#endif default: { char numBuf[12]; JS_snprintf(numBuf, sizeof numBuf, "%d", op); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_BYTECODE, numBuf); ok = JS_FALSE; goto out; } } +#ifndef JS_THREADED_INTERP advance_pc: pc += len; #ifdef DEBUG if (tracefp) { intN ndefs, n; jsval *siter; - ndefs = cs->ndefs; + ndefs = js_CodeSpec[op].ndefs; if (ndefs) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); if (op == JSOP_FORELEM && sp[-1] == JSVAL_FALSE) --ndefs; for (n = -ndefs; n < 0; n++) { str = js_DecompileValueGenerator(cx, n, sp[n], NULL); if (str) { fprintf(tracefp, "%s %s", (n == -ndefs) ? " output:" : ",", JS_GetStringBytes(str)); @@ -5323,18 +5477,20 @@ js_Interpret(JSContext *cx, jsbytecode * fprintf(tracefp, " stack: "); for (siter = fp->spbase; siter < sp; siter++) { str = js_ValueToSource(cx, *siter); fprintf(tracefp, "%s ", str ? JS_GetStringBytes(str) : ""); } fputc('\n', tracefp); } -#endif +#endif /* DEBUG */ } +#endif /* !JS_THREADED_INTERP */ + out: #if JS_HAS_EXCEPTIONS if (!ok) { /* * Has an exception been raised? Also insist that we are in the * interpreter activation that pushed fp's operand stack, to avoid * catching exceptions within XML filtering predicate expressions, @@ -5361,17 +5517,17 @@ out: * FIXME: https://bugzilla.mozilla.org/show_bug.cgi?id=309894 */ if (cx->throwing && JS_LIKELY(mark != NULL)) { /* * Call debugger throw hook if set (XXX thread safety?). */ JSTrapHandler handler = rt->throwHook; if (handler) { - SAVE_SP(fp); + SAVE_SP_AND_PC(fp); switch (handler(cx, script, pc, &rval, rt->throwHookData)) { case JSTRAP_ERROR: cx->throwing = JS_FALSE; goto no_catch; case JSTRAP_RETURN: ok = JS_TRUE; cx->throwing = JS_FALSE; fp->rval = rval; @@ -5390,17 +5546,17 @@ out: SCRIPT_FIND_CATCH_START(script, pc, pc); if (pc) { /* Don't clear cx->throwing to save cx->exception from GC. */ len = 0; ok = JS_TRUE; #if JS_HAS_XML_SUPPORT foreach = JS_FALSE; #endif - goto advance_pc; + DO_NEXT_OP(len); } } no_catch:; } #endif /* * Check whether control fell off the end of a lightweight function, or an Index: jsopcode.tbl =================================================================== RCS file: /cvsroot/mozilla/js/src/jsopcode.tbl,v retrieving revision 3.47 diff -p -u -8 -r3.47 jsopcode.tbl --- jsopcode.tbl 18 Nov 2005 06:19:46 -0000 3.47 +++ jsopcode.tbl 19 Nov 2005 03:18:32 -0000 @@ -387,8 +387,14 @@ OPDEF(JSOP_FINDNAME, 190,"findname" OPDEF(JSOP_LITOPX, 191,"litopx", NULL, 5, 0, 0, 12, JOF_LITOPX) /* * Opcodes to help the decompiler deal with XML. */ OPDEF(JSOP_STARTXML, 192,"startxml", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_STARTXMLEXPR, 193,"startxmlexpr",NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_SETMETHOD, 194,"setmethod", NULL, 3, 2, 1, 1, JOF_CONST|JOF_PROP) + +/* + * Stop interpretation, emitted at end of script to save the threaded bytecode + * interpreter an extra branch test on every DO_NEXT_OP (see jsinterp.c). + */ +OPDEF(JSOP_STOP, 195,"stop", NULL, 1, 0, 0, 0, JOF_BYTE) Index: jsparse.c =================================================================== RCS file: /cvsroot/mozilla/js/src/jsparse.c,v retrieving revision 3.150 diff -p -u -8 -r3.150 jsparse.c --- jsparse.c 18 Nov 2005 06:19:46 -0000 3.150 +++ jsparse.c 19 Nov 2005 03:18:32 -0000 @@ -478,24 +478,27 @@ js_CompileTokenStream(JSContext *cx, JSO (char *)sbrk(0) - (char *)before, parsenodes, maxparsenodes, parsenodes - recyclednodes); before = sbrk(0); #endif /* - * No need to emit code here -- Statements already has, for each + * No need to emit bytecode here -- Statements already has, for each * statement in turn. Search for TCF_COMPILING in Statements, below. * That flag is set for every tc == &cg->treeContext, and it implies * that the tc can be downcast to a cg and used to emit code during * parsing, rather than at the end of the parse phase. + * + * Update: the threaded interpreter needs a stop instruction, so we + * do have to emit that here. */ JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); - ok = JS_TRUE; + ok = js_Emit1(cx, cg, JSOP_STOP) >= 0; } #ifdef METER_PARSENODES printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount); #endif #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); @@ -700,20 +703,27 @@ js_CompileFunctionBody(JSContext *cx, JS frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING; cx->fp = &frame; - /* Ensure that the body looks like a block statement to js_EmitTree. */ + /* + * Farble the body so that it looks like a block statement to js_EmitTree, + * which is called beneath FunctionBody (see Statements, further below in + * this file). + * + * NB: with threaded interpretation, we must emit a stop opcode at the end + * of every scripted function and top-level script. + */ CURRENT_TOKEN(ts).type = TOK_LC; pn = FunctionBody(cx, ts, fun, &funcg.treeContext); - if (!pn) { + if (!pn || js_Emit1(cx, &funcg, JSOP_STOP) < 0) { ok = JS_FALSE; } else { /* * No need to emit code here -- Statements (via FunctionBody) already * has. See similar comment in js_CompileTokenStream, and bug 108257. */ fun->u.script = js_NewScriptFromCG(cx, &funcg, fun); if (!fun->u.script) { Index: jsscript.c =================================================================== RCS file: /cvsroot/mozilla/js/src/jsscript.c,v retrieving revision 3.89 diff -p -u -8 -r3.89 jsscript.c --- jsscript.c 18 Nov 2005 06:19:46 -0000 3.89 +++ jsscript.c 19 Nov 2005 03:18:32 -0000 @@ -418,25 +418,31 @@ js_XDRScript(JSXDRState *xdr, JSScript * /* * Encode prologLength and version after script->length (_2 or greater), * but decode both new (>= _2) and old, prolog&version-free (_1) scripts. * Version _3 supports principals serialization. Version _4 reorders the * nsrcnotes and ntrynotes fields to come before everything except magic, * length, prologLength, and version, so that srcnote and trynote storage * can be allocated as part of the JSScript (along with bytecode storage). + * + * So far, the magic number has not changed for every jsopcode.tbl change. + * We stipulate forward compatibility by requiring old bytecodes never to + * change or go away (modulo a few exceptions before the XDR interfaces + * evolved, and a few exceptions during active trunk development). With + * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new + * magic number (_5) so that we know to append JSOP_STOP to old scripts + * when deserializing. */ if (xdr->mode == JSXDR_ENCODE) magic = JSXDR_MAGIC_SCRIPT_CURRENT; if (!JS_XDRUint32(xdr, &magic)) return JS_FALSE; - if (magic != JSXDR_MAGIC_SCRIPT_4 && - magic != JSXDR_MAGIC_SCRIPT_3 && - magic != JSXDR_MAGIC_SCRIPT_2 && - magic != JSXDR_MAGIC_SCRIPT_1) { + JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4); + if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) { if (!hasMagic) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_SCRIPT_MAGIC); return JS_FALSE; } *hasMagic = JS_FALSE; return JS_TRUE; } @@ -479,17 +485,21 @@ js_XDRScript(JSXDRState *xdr, JSScript * if (!JS_XDRUint32(xdr, &nsrcnotes)) return JS_FALSE; if (!JS_XDRUint32(xdr, &ntrynotes)) return JS_FALSE; } } if (xdr->mode == JSXDR_DECODE) { - script = js_NewScript(cx, length, nsrcnotes, ntrynotes); + size_t alloclength = length; + if (magic < JSXDR_MAGIC_SCRIPT_5) + ++alloclength; /* add a byte for JSOP_STOP */ + + script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes); if (!script) return JS_FALSE; if (magic >= JSXDR_MAGIC_SCRIPT_2) { script->main += prologLength; script->version = (JSVersion) (version & 0xffff); script->numGlobalVars = (uint16) (version >> 16); /* If we know nsrcnotes, we allocated space for notes in script. */ @@ -505,23 +515,35 @@ js_XDRScript(JSXDRState *xdr, JSScript * * in the (DECODE and magic < _4) case must point at a temporary vector * allocated just below. */ if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) || !XDRAtomMap(xdr, &script->atomMap)) { goto error; } - if (magic < JSXDR_MAGIC_SCRIPT_4) { - if (!JS_XDRUint32(xdr, &nsrcnotes)) - goto error; + if (magic < JSXDR_MAGIC_SCRIPT_5) { if (xdr->mode == JSXDR_DECODE) { - notes = (jssrcnote *) JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); - if (!notes) + /* + * Append JSOP_STOP to old scripts, to relieve the interpreter + * from having to bounds-check pc. Also take care to increment + * length, as it is used below and must count all bytecode. + */ + script->code[length++] = JSOP_STOP; + } + + if (magic < JSXDR_MAGIC_SCRIPT_4) { + if (!JS_XDRUint32(xdr, &nsrcnotes)) goto error; + if (xdr->mode == JSXDR_DECODE) { + notes = (jssrcnote *) + JS_malloc(cx, nsrcnotes * sizeof(jssrcnote)); + if (!notes) + goto error; + } } } if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) || !JS_XDRCStringOrNull(xdr, (char **)&script->filename) || !JS_XDRUint32(xdr, &lineno) || !JS_XDRUint32(xdr, &depth) || (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) { Index: jsxdrapi.h =================================================================== RCS file: /cvsroot/mozilla/js/src/jsxdrapi.h,v retrieving revision 1.16 diff -p -u -8 -r1.16 jsxdrapi.h --- jsxdrapi.h 18 Nov 2005 06:19:46 -0000 1.16 +++ jsxdrapi.h 19 Nov 2005 03:18:32 -0000 @@ -181,13 +181,14 @@ JS_XDRFindClassById(JSXDRState *xdr, uin /* * Magic numbers. */ #define JSXDR_MAGIC_SCRIPT_1 0xdead0001 #define JSXDR_MAGIC_SCRIPT_2 0xdead0002 #define JSXDR_MAGIC_SCRIPT_3 0xdead0003 #define JSXDR_MAGIC_SCRIPT_4 0xdead0004 -#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_4 +#define JSXDR_MAGIC_SCRIPT_5 0xdead0005 +#define JSXDR_MAGIC_SCRIPT_CURRENT JSXDR_MAGIC_SCRIPT_5 JS_END_EXTERN_C #endif /* ! jsxdrapi_h___ */