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, &ltmp);
-            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, &ltmp);
+                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) : "<null>");
             }
             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___ */
