bug 469233 - using interrupt hook support in the interpreter for trace recording. r=brendan
authorIgor Bukanov <igor@mir2.org>
Sun, 21 Dec 2008 12:55:09 +0100
changeset 23111 7dd3e4a4ceffa6459573229efc3a474ab7e3adbd
parent 23110 07ba1e2cdf9d60e3d05f27c99038fcf0eb9f1780
child 23112 7184e014cd05187008c3c579b66e79799e423710
push id4346
push userrsayre@mozilla.com
push dateFri, 26 Dec 2008 01:26:36 +0000
treeherdermozilla-central@8eb5a5b83a93 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs469233
milestone1.9.2a1pre
bug 469233 - using interrupt hook support in the interpreter for trace recording. r=brendan
js/src/jsinterp.cpp
js/src/jsopcode.tbl
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jsxdrapi.h
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -237,18 +237,18 @@ js_FillPropertyCache(JSContext *cx, JSOb
                     vword = JSVAL_OBJECT_TO_PCVAL(v);
                     break;
                 }
             }
         }
 
         /* If getting a value via a stub getter, we can cache the slot. */
         if (!(cs->format & JOF_SET) &&
-            !((cs->format & (JOF_INCDEC | JOF_FOR)) && 
-              (sprop->attrs & JSPROP_READONLY)) && 
+            !((cs->format & (JOF_INCDEC | JOF_FOR)) &&
+              (sprop->attrs & JSPROP_READONLY)) &&
             SPROP_HAS_STUB_GETTER(sprop) &&
             SPROP_HAS_VALID_SLOT(sprop, scope)) {
             /* Great, let's cache sprop's slot and use it on cache hit. */
             vword = SLOT_TO_PCVAL(sprop->slot);
         } else {
             /* Best we can do is to cache sprop (still a nice speedup). */
             vword = SPROP_TO_PCVAL(sprop);
         }
@@ -2411,22 +2411,16 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(IN
     __SUNPRO_C >= 0x570)
 #  define JS_THREADED_INTERP 1
 # else
 #  define JS_THREADED_INTERP 0
 # endif
 #endif
 
 /*
- * Interpreter assumes the following to implement condition-free interrupt
- * implementation when !JS_THREADED_INTERP.
- */
-JS_STATIC_ASSERT(JSOP_INTERRUPT == 0);
-
-/*
  * Ensure that the intrepreter switch can close call-bytecode cases in the
  * same way as non-call bytecodes.
  */
 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
@@ -2472,22 +2466,16 @@ js_Interpret(JSContext *cx)
     JSProperty *prop;
     JSScopeProperty *sprop;
     JSString *str, *str2;
     jsint i, j;
     jsdouble d, d2;
     JSClass *clasp;
     JSFunction *fun;
     JSType type;
-#if JS_THREADED_INTERP
-    register void * const *jumpTable;
-#else
-    register uint32 switchMask;
-    uintN switchOp;
-#endif
     jsint low, high, off, npairs;
     JSBool match;
 #if JS_HAS_GETTER_SETTER
     JSPropertyOp getter, setter;
 #endif
     JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
 
 #ifdef __GNUC__
@@ -2501,73 +2489,77 @@ js_Interpret(JSContext *cx)
 #if JS_THREADED_INTERP
     static void *const normalJumpTable[] = {
 # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
         JS_EXTENSION &&L_##op,
 # include "jsopcode.tbl"
 # undef OPDEF
     };
 
-#ifdef JS_TRACER
-    static void *const recordingJumpTable[] = {
-# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) \
-        JS_EXTENSION &&R_##op,
+    static void *const interruptJumpTable[] = {
+# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \
+        JS_EXTENSION &&interrupt,
 # include "jsopcode.tbl"
 # undef OPDEF
     };
-#endif /* JS_TRACER */
-
-    static void *const interruptJumpTable[] = {
-# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)              \
-        JS_EXTENSION &&L_JSOP_INTERRUPT,
-# include "jsopcode.tbl"
-# undef OPDEF
-    };
+
+    register void * const *jumpTable = normalJumpTable;
 
     METER_OP_INIT(op);      /* to nullify first METER_OP_PAIR */
 
+# define ENABLE_INTERRUPTS() ((void) (jumpTable = interruptJumpTable))
+
 # ifdef JS_TRACER
-#  define CHECK_RECORDER()  JS_BEGIN_MACRO                                    \
-                                JS_ASSERT(!TRACE_RECORDER(cx) ^               \
-                                          (jumpTable == recordingJumpTable)); \
-                            JS_END_MACRO
+#  define CHECK_RECORDER()                                                    \
+    JS_ASSERT_IF(TRACE_RECORDER(cx), jumpTable == interruptJumpTable)
 # else
 #  define CHECK_RECORDER()  ((void)0)
 # endif
 
 # define DO_OP()            JS_BEGIN_MACRO                                    \
                                 CHECK_RECORDER();                             \
                                 JS_EXTENSION_(goto *jumpTable[op]);           \
                             JS_END_MACRO
 # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
                                 METER_OP_PAIR(op, regs.pc[n]);                \
                                 op = (JSOp) *(regs.pc += (n));                \
                                 DO_OP();                                      \
                             JS_END_MACRO
 
-# define BEGIN_CASE(OP)     L_##OP:                                           \
-                                CHECK_RECORDER();
+# define BEGIN_CASE(OP)     L_##OP: CHECK_RECORDER();
 # define END_CASE(OP)       DO_NEXT_OP(OP##_LENGTH);
 # define END_VARLEN_CASE    DO_NEXT_OP(len);
 # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP)                                    \
                                 JS_ASSERT(js_CodeSpec[OP].length == 1);       \
                                 op = (JSOp) *++regs.pc;                       \
                                 DO_OP();
 
 # define END_EMPTY_CASES
 
 #else /* !JS_THREADED_INTERP */
 
+    register intN switchMask = 0;
+    intN switchOp;
+
+# define ENABLE_INTERRUPTS() ((void) (switchMask = -1))
+
+# ifdef JS_TRACER
+#  define CHECK_RECORDER()                                                    \
+    JS_ASSERT_IF(TRACE_RECORDER(cx), switchMask == -1)
+# else
+#  define CHECK_RECORDER()  ((void)0)
+# endif
+
 # define DO_OP()            goto do_op
 # define DO_NEXT_OP(n)      JS_BEGIN_MACRO                                    \
                                 JS_ASSERT((n) == len);                        \
                                 goto advance_pc;                              \
                             JS_END_MACRO
 
-# define BEGIN_CASE(OP)     case OP:
+# define BEGIN_CASE(OP)     case OP: CHECK_RECORDER();
 # define END_CASE(OP)       END_CASE_LEN(OP##_LENGTH)
 # define END_CASE_LEN(n)    END_CASE_LENX(n)
 # define END_CASE_LENX(n)   END_CASE_LEN##n
 
 /*
  * To share the code for all len == 1 cases we use the specialized label with
  * code that falls through to advance_pc: .
  */
@@ -2644,17 +2636,20 @@ js_Interpret(JSContext *cx)
 #define LOAD_FUNCTION(PCOFF)                                                  \
     JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
 
 #ifdef JS_TRACER
 
 #define MONITOR_BRANCH()                                                      \
     JS_BEGIN_MACRO                                                            \
         if (TRACING_ENABLED(cx)) {                                            \
-            ENABLE_TRACER(js_MonitorLoopEdge(cx, inlineCallCount));           \
+            if (js_MonitorLoopEdge(cx, inlineCallCount)) {                    \
+                JS_ASSERT(TRACE_RECORDER(cx));                                \
+                ENABLE_INTERRUPTS();                                          \
+            }                                                                 \
             fp = cx->fp;                                                      \
             script = fp->script;                                              \
             atoms = fp->imacpc                                                \
                     ? COMMON_ATOMS_START(&rt->atomState)                      \
                     : script->atomMap.vector;                                 \
             currentVersion = (JSVersion) script->version;                     \
             JS_ASSERT(fp->regs == &regs);                                     \
             if (cx->throwing)                                                 \
@@ -2713,63 +2708,29 @@ js_Interpret(JSContext *cx)
         JSStackFrame **disp = &cx->display[script->staticDepth];
         fp->displaySave = *disp;
         *disp = fp;
     }
 #ifdef DEBUG
     fp->pcDisabledSave = JS_PROPERTY_CACHE(cx).disabled;
 #endif
 
+# define CHECK_INTERRUPT_HANDLER()                                            \
+    JS_BEGIN_MACRO                                                            \
+        if (cx->debugHooks->interruptHandler)                                 \
+            ENABLE_INTERRUPTS();                                              \
+    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 when it is non-null.
      */
-#if JS_THREADED_INTERP
-#ifdef JS_TRACER
-# define LOAD_INTERRUPT_HANDLER(cx)                                           \
-    ((void) (jumpTable = (cx)->debugHooks->interruptHandler                   \
-                         ? interruptJumpTable                                 \
-                         : TRACE_RECORDER(cx)                                 \
-                         ? recordingJumpTable                                 \
-                         : normalJumpTable))
-# define ENABLE_TRACER(flag)                                                  \
-    JS_BEGIN_MACRO                                                            \
-        bool flag_ = (flag);                                                  \
-        JS_ASSERT(flag_ == !!TRACE_RECORDER(cx));                             \
-        jumpTable = flag_ ? recordingJumpTable : normalJumpTable;             \
-    JS_END_MACRO
-#else /* !JS_TRACER */
-# define LOAD_INTERRUPT_HANDLER(cx)                                           \
-    ((void) (jumpTable = (cx)->debugHooks->interruptHandler                   \
-                         ? interruptJumpTable                                 \
-                         : normalJumpTable))
-# define ENABLE_TRACER(flag) ((void)0)
-#endif /* !JS_TRACER */
-#else /* !JS_THREADED_INTERP */
-#ifdef JS_TRACER
-# define LOAD_INTERRUPT_HANDLER(cx)                                           \
-    ((void) (switchMask = ((cx)->debugHooks->interruptHandler ||              \
-                           TRACE_RECORDER(cx)) ? 0 : 255))
-# define ENABLE_TRACER(flag)                                                  \
-    JS_BEGIN_MACRO                                                            \
-        bool flag_ = (flag);                                                  \
-        JS_ASSERT(flag_ == !!TRACE_RECORDER(cx));                             \
-        switchMask = flag_ ? 0 : 255;                                         \
-    JS_END_MACRO
-#else /* !JS_TRACER */
-# define LOAD_INTERRUPT_HANDLER(cx)                                           \
-    ((void) (switchMask = ((cx)->debugHooks->interruptHandler                 \
-                           ? 0 : 255)))
-# define ENABLE_TRACER(flag) ((void)0)
-#endif /* !JS_TRACER */
-#endif /* !JS_THREADED_INTERP */
-
-    LOAD_INTERRUPT_HANDLER(cx);
+    CHECK_INTERRUPT_HANDLER();
 
 #if !JS_HAS_GENERATORS
     JS_ASSERT(!fp->regs);
 #else
     /* Initialize the pc and sp registers unless we're resuming a generator. */
     if (JS_LIKELY(!fp->regs)) {
 #endif
         ASSERT_NOT_THROWING(cx);
@@ -2815,75 +2776,91 @@ js_Interpret(JSContext *cx)
     len = 0;
     DO_NEXT_OP(len);
 
 #if JS_THREADED_INTERP
     /*
      * This is a loop, but it does not look like a loop. The loop-closing
      * jump is distributed throughout goto *jumpTable[op] inside of DO_OP.
      * When interrupts are enabled, jumpTable is set to interruptJumpTable
-     * where all jumps point to the JSOP_INTERRUPT case. The latter, after
+     * where all jumps point to the interrupt label. The latter, after
      * calling the interrupt handler, dispatches through normalJumpTable to
      * continue the normal bytecode processing.
      */
-#else
+  interrupt:
+#else /* !JS_THREADED_INTERP */
     for (;;) {
       advance_pc_by_one:
         JS_ASSERT(js_CodeSpec[op].length == 1);
         len = 1;
       advance_pc:
         regs.pc += len;
         op = (JSOp) *regs.pc;
-#ifdef DEBUG
+# ifdef DEBUG
         if (cx->tracefp)
             js_TraceOpcode(cx, len);
-#endif
+# endif
 
       do_op:
-        switchOp = op & switchMask;
+        CHECK_RECORDER();
+        switchOp = intN(op) | switchMask;
       do_switch:
         switch (switchOp) {
+          case -1:
+            JS_ASSERT(switchMask == -1);
 #endif /* !JS_THREADED_INTERP */
-
-          BEGIN_CASE(JSOP_INTERRUPT)
           {
-            JSTrapHandler handler;
-
-            handler = cx->debugHooks->interruptHandler;
+            bool moreInterrupts = false;
+            JSTrapHandler handler = cx->debugHooks->interruptHandler;
             if (handler) {
+#ifdef JS_TRACER
+                if (TRACE_RECORDER(cx))
+                    js_AbortRecording(cx, "interrupt handler");
+#endif
                 switch (handler(cx, script, regs.pc, &rval,
                                 cx->debugHooks->interruptHandlerData)) {
                   case JSTRAP_ERROR:
                     goto error;
                   case JSTRAP_CONTINUE:
                     break;
                   case JSTRAP_RETURN:
                     fp->rval = rval;
                     ok = JS_TRUE;
                     goto forced_return;
                   case JSTRAP_THROW:
                     cx->throwing = JS_TRUE;
                     cx->exception = rval;
                     goto error;
                   default:;
                 }
-#if !JS_THREADED_INTERP
-            } else {
-                /* this was not a real interrupt, the tracer is trying to
-                   record a trace */
-                switchOp = op + 256;
-                goto do_switch;
-#endif
+                moreInterrupts = true;
             }
-            LOAD_INTERRUPT_HANDLER(cx);
+
+#ifdef JS_TRACER
+            TraceRecorder* tr = TRACE_RECORDER(cx);
+            if (tr) {
+                JSMonitorRecordingStatus status = tr->monitorRecording(op);
+                if (status == JSMRS_CONTINUE) {
+                    moreInterrupts = true;
+                } else if (status == JSMRS_IMACRO) {
+                    atoms = COMMON_ATOMS_START(&rt->atomState);
+                    op = JSOp(*regs.pc);
+                    DO_OP();    /* keep interrupting for op. */
+                } else {
+                    JS_ASSERT(status == JSMRS_STOP);
+                }
+            }
+#endif /* !JS_TRACER */
 
 #if JS_THREADED_INTERP
+            jumpTable = moreInterrupts ? interruptJumpTable : normalJumpTable;
             JS_EXTENSION_(goto *normalJumpTable[op]);
 #else
-            switchOp = op;
+            switchMask = moreInterrupts ? -1 : 0;
+            switchOp = intN(op);
             goto do_switch;
 #endif
           }
 
           /* No-ops for ease of decompilation. */
           ADD_EMPTY_CASE(JSOP_NOP)
           ADD_EMPTY_CASE(JSOP_CONDSWITCH)
           ADD_EMPTY_CASE(JSOP_TRY)
@@ -3019,17 +2996,17 @@ js_Interpret(JSContext *cx)
                     if (hook) {
                         /*
                          * Do not pass &ok directly as exposing the address
                          * inhibits optimizations and uninitialised warnings.
                          */
                         status = ok;
                         hook(cx, fp, JS_FALSE, &status, hookData);
                         ok = status;
-                        LOAD_INTERRUPT_HANDLER(cx);
+                        CHECK_INTERRUPT_HANDLER();
                     }
                 }
 
                 /*
                  * If fp has a call object, sync values and clear the back-
                  * pointer. This can happen for a lightweight function if it
                  * calls eval unexpectedly (in a way that is hidden from the
                  * compiler). See bug 325540.
@@ -3234,27 +3211,27 @@ js_Interpret(JSContext *cx)
             STORE_OPND(-1, BOOLEAN_TO_JSVAL(cond));
           END_CASE(JSOP_IN)
 
           BEGIN_CASE(JSOP_ITER)
             JS_ASSERT(regs.sp > StackBase(fp));
             flags = regs.pc[1];
             if (!js_ValueToIterator(cx, flags, &regs.sp[-1]))
                 goto error;
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-1]));
             PUSH(JSVAL_VOID);
           END_CASE(JSOP_ITER)
 
           BEGIN_CASE(JSOP_NEXTITER)
             JS_ASSERT(regs.sp - 2 >= StackBase(fp));
             JS_ASSERT(!JSVAL_IS_PRIMITIVE(regs.sp[-2]));
             if (!js_CallIteratorNext(cx, JSVAL_TO_OBJECT(regs.sp[-2]), &regs.sp[-1]))
                 goto error;
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
             rval = BOOLEAN_TO_JSVAL(regs.sp[-1] != JSVAL_HOLE);
             PUSH(rval);
             TRACE_0(IteratorNextComplete);
           END_CASE(JSOP_NEXTITER)
 
           BEGIN_CASE(JSOP_ENDITER)
             /*
              * Decrease the stack pointer even when !ok -- see comments in the
@@ -4650,20 +4627,18 @@ js_Interpret(JSContext *cx)
                     if (entry)
                         TRACE_1(SetPropMiss, entry);
 #endif
                 } else {
                     if (!OBJ_SET_PROPERTY(cx, obj, id, &rval))
                         goto error;
                 }
 #ifdef JS_TRACER
-                if (!entry && TRACE_RECORDER(cx)) {
+                if (!entry && TRACE_RECORDER(cx))
                     js_AbortRecording(cx, "SetPropUncached");
-                    ENABLE_TRACER(0);
-                }
 #endif
             } while (0);
           END_SET_CASE_STORE_RVAL(JSOP_SETPROP, 2);
 
           BEGIN_CASE(JSOP_GETELEM)
             /* Open-coded ELEMENT_OP optimized for strings and dense arrays. */
             lval = FETCH_OPND(-2);
             rval = FETCH_OPND(-1);
@@ -4803,17 +4778,17 @@ js_Interpret(JSContext *cx)
                     flags = JSFRAME_CONSTRUCTING;
                     goto inline_call;
                 }
             }
 
             if (!js_InvokeConstructor(cx, argc, JS_FALSE, vp))
                 goto error;
             regs.sp = vp + 1;
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
           END_CASE(JSOP_NEW)
           
           BEGIN_CASE(JSOP_CALL)
           BEGIN_CASE(JSOP_EVAL)
           BEGIN_CASE(JSOP_APPLY)
             argc = GET_ARGC(regs.pc);
             vp = regs.sp - (argc + 2);
             
@@ -4943,17 +4918,17 @@ js_Interpret(JSContext *cx)
                     while (nvars--)
                         *newsp++ = JSVAL_VOID;
 
                     /* Call the debugger hook if present. */
                     hook = cx->debugHooks->callHook;
                     if (hook) {
                         newifp->hookData = hook(cx, &newifp->frame, JS_TRUE, 0,
                                                 cx->debugHooks->callHookData);
-                        LOAD_INTERRUPT_HANDLER(cx);
+                        CHECK_INTERRUPT_HANDLER();
                     } else {
                         newifp->hookData = NULL;
                     }
 
                     /* Scope with a call object parented by callee's parent. */
                     if (JSFUN_HEAVYWEIGHT_TEST(fun->flags) &&
                         !js_GetCallObject(cx, &newifp->frame, parent)) {
                         goto bad_inline_call;
@@ -5041,17 +5016,17 @@ js_Interpret(JSContext *cx)
             if (VALUE_IS_FUNCTION(cx, lval)) {
                 if (JAVASCRIPT_FUNCTION_RVAL_ENABLED())
                     jsdtrace_function_rval(cx, fp, fun);
                 if (JAVASCRIPT_FUNCTION_RETURN_ENABLED())
                     jsdtrace_function_return(cx, fp, fun);
             }
 #endif
             regs.sp = vp + 1;
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
             if (!ok)
                 goto error;
             JS_RUNTIME_METER(rt, nonInlineCalls);
 
           end_call:
 #if JS_HAS_LVALUE_RETURN
             if (cx->rval2set) {
                 /*
@@ -5079,17 +5054,17 @@ js_Interpret(JSContext *cx)
           END_CASE(JSOP_CALL)
 
 #if JS_HAS_LVALUE_RETURN
           BEGIN_CASE(JSOP_SETCALL)
             argc = GET_ARGC(regs.pc);
             vp = regs.sp - argc - 2;
             ok = js_Invoke(cx, argc, vp, 0);
             regs.sp = vp + 1;
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
             if (!ok)
                 goto error;
             if (!cx->rval2set) {
                 op2 = (JSOp) regs.pc[JSOP_SETCALL_LENGTH];
                 if (op2 != JSOP_DELELEM) {
                     JS_ASSERT(!(js_CodeSpec[op2].format & JOF_DEL));
                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                          JSMSG_BAD_LEFTSIDE_OF_ASS);
@@ -5517,17 +5492,17 @@ js_Interpret(JSContext *cx)
               case JSTRAP_THROW:
                 cx->throwing = JS_TRUE;
                 cx->exception = rval;
                 goto error;
               default:;
                 break;
             }
             JS_ASSERT(status == JSTRAP_CONTINUE);
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
             JS_ASSERT(JSVAL_IS_INT(rval));
             op = (JSOp) JSVAL_TO_INT(rval);
             JS_ASSERT((uintN)op < (uintN)JSOP_LIMIT);
             DO_OP();
           }
 
           BEGIN_CASE(JSOP_ARGUMENTS)
             if (!js_GetArgsValue(cx, fp, &rval))
@@ -5849,17 +5824,17 @@ js_Interpret(JSContext *cx)
             obj = FUN_OBJECT(fun);
             if (OBJ_GET_PARENT(cx, obj) != parent) {
                 obj = js_CloneFunctionObject(cx, fun, parent);
                 if (!obj)
                     goto error;
             }
 
             TRACE_2(DefLocalFunSetSlot, slot, obj);
-            
+
             fp->slots[slot] = OBJECT_TO_JSVAL(obj);
           END_CASE(JSOP_DEFLOCALFUN)
 
           BEGIN_CASE(JSOP_ANONFUNOBJ)
             /* Load the specified function object literal. */
             LOAD_FUNCTION(0);
 
             /* If re-parenting, push a clone of the function object. */
@@ -6074,17 +6049,17 @@ js_Interpret(JSContext *cx)
             JS_ASSERT(i == JSProto_Array || i == JSProto_Object);
             obj = (i == JSProto_Array)
                   ? js_NewArrayObject(cx, 0, NULL)
                   : js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
             if (!obj)
                 goto error;
             PUSH_OPND(OBJECT_TO_JSVAL(obj));
             fp->sharpDepth++;
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
           END_CASE(JSOP_NEWINIT)
 
           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(regs.sp - StackBase(fp) >= 1);
@@ -6440,17 +6415,17 @@ js_Interpret(JSContext *cx)
                     ok = JS_TRUE;
                     goto forced_return;
                   case JSTRAP_THROW:
                     cx->throwing = JS_TRUE;
                     cx->exception = rval;
                     goto error;
                   default:;
                 }
-                LOAD_INTERRUPT_HANDLER(cx);
+                CHECK_INTERRUPT_HANDLER();
             }
           }
           END_CASE(JSOP_DEBUGGER)
 #endif /* JS_HAS_DEBUGGER_KEYWORD */
 
 #if JS_HAS_XML_SUPPORT
           BEGIN_CASE(JSOP_DEFXMLNS)
             rval = POP();
@@ -6854,16 +6829,17 @@ js_Interpret(JSContext *cx)
           L_JSOP_TOATTRNAME:
           L_JSOP_QNAME:
           L_JSOP_QNAMECONST:
           L_JSOP_QNAMEPART:
           L_JSOP_ANYNAME:
           L_JSOP_DEFXMLNS:
 # endif
 
+          L_JSOP_UNUSED135:
           L_JSOP_UNUSED203:
           L_JSOP_UNUSED204:
           L_JSOP_UNUSED205:
           L_JSOP_UNUSED206:
           L_JSOP_UNUSED207:
           L_JSOP_UNUSED208:
           L_JSOP_UNUSED209:
           L_JSOP_UNUSED219:
@@ -6875,34 +6851,19 @@ js_Interpret(JSContext *cx)
           {
             char numBuf[12];
             JS_snprintf(numBuf, sizeof numBuf, "%d", op);
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_BAD_BYTECODE, numBuf);
             goto error;
           }
 
-#ifdef JS_TRACER
-
-#if JS_THREADED_INTERP
-# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \
-    R_##x: RECORD(x); goto L_##x;
-#else
-# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \
-    case 256 + x: RECORD(x); op = x; switchOp = x; goto do_switch;
-#endif
-#include "jsopcode.tbl"
-#undef OPDEF
-
-#endif /* JS_TRACER */
-
 #if !JS_THREADED_INTERP
-
         } /* switch (op) */
-    }
+    } /* for (;;) */
 #endif /* !JS_THREADED_INTERP */
 
   error:
     if (fp->imacpc && cx->throwing) {
         // To keep things simple, we hard-code imacro exception handlers here.
         if (*fp->imacpc == JSOP_NEXTITER) {
             JS_ASSERT(*regs.pc == JSOP_CALL);
             if (js_ValueIsStopIteration(cx->exception)) {
@@ -6942,17 +6903,17 @@ js_Interpret(JSContext *cx)
                 fp->rval = rval;
                 ok = JS_TRUE;
                 goto forced_return;
               case JSTRAP_THROW:
                 cx->exception = rval;
               case JSTRAP_CONTINUE:
               default:;
             }
-            LOAD_INTERRUPT_HANDLER(cx);
+            CHECK_INTERRUPT_HANDLER();
         }
 
         /*
          * Look for a try block in script that can catch this exception.
          */
         if (script->trynotesOffset == 0)
             goto no_catch;
 
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -100,18 +100,22 @@
  * argument lists, so they have the lowest precedence.
  *
  * This file is best viewed with 128 columns:
 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
  */
 
 /* legend: op         val name          image       len use def prec  format */
 
+/*
+ * Generic nop for the decompiler.
+ */
+OPDEF(JSOP_NOP,       0,  "nop",        NULL,         1,  0,  0,  0,  JOF_BYTE)
+
 /* Longstanding JavaScript bytecodes. */
-OPDEF(JSOP_INTERRUPT, 0,  "interrupt",  NULL,         1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_PUSH,      1,  "push",       NULL,         1,  0,  1,  0,  JOF_BYTE)
 OPDEF(JSOP_POPV,      2,  "popv",       NULL,         1,  1,  0,  2,  JOF_BYTE)
 OPDEF(JSOP_ENTERWITH, 3,  "enterwith",  NULL,         1,  1,  1,  0,  JOF_BYTE|JOF_PARENHEAD)
 OPDEF(JSOP_LEAVEWITH, 4,  "leavewith",  NULL,         1,  1,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_RETURN,    5,  "return",     NULL,         1,  1,  0,  2,  JOF_BYTE)
 OPDEF(JSOP_GOTO,      6,  "goto",       NULL,         3,  0,  0,  0,  JOF_JUMP)
 OPDEF(JSOP_IFEQ,      7,  "ifeq",       NULL,         3,  1,  0,  4,  JOF_JUMP|JOF_DETECTING)
 OPDEF(JSOP_IFNE,      8,  "ifne",       NULL,         3,  1,  0,  0,  JOF_JUMP|JOF_PARENHEAD)
@@ -344,20 +348,17 @@ OPDEF(JSOP_SETCALL,     132, "setcall", 
 
 /*
  * Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
  * srcnote-annotated JSOP_NOPs and to simply stack balance handling.
  */
 OPDEF(JSOP_TRY,         133,"try",        NULL,       1,  0,  0,  0,  JOF_BYTE)
 OPDEF(JSOP_FINALLY,     134,"finally",    NULL,       1,  0,  2,  0,  JOF_BYTE)
 
-/*
- * Generic nop for the decompiler.
- */
-OPDEF(JSOP_NOP,         135,"nop",        NULL,       1,  0,  0,  0,  JOF_BYTE)
+OPDEF(JSOP_UNUSED135,   135,"unused135",  NULL,       1,  0,  0,  0,  JOF_BYTE)
 
 /*
  * Bytecodes that avoid making an arguments object in most cases:
  * JSOP_ARGSUB gets arguments[i] from fp->argv, iff i is in [0, fp->argc-1].
  * JSOP_ARGCNT returns fp->argc.
  */
 OPDEF(JSOP_ARGSUB,      136,"argsub",     NULL,       3,  0,  1, 18,  JOF_QARG |JOF_NAME)
 OPDEF(JSOP_ARGCNT,      137,"argcnt",     NULL,       1,  0,  1, 18,  JOF_BYTE)
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -3829,70 +3829,107 @@ monitor_loop:
             return js_AttemptToExtendTree(cx, innermostNestedGuard, lr, NULL);
         return false;
       default:
         /* No, this was an unusual exit (i.e. out of memory/GC), so just resume interpretation. */
         return false;
     }
 }
 
-JS_REQUIRES_STACK bool
-js_MonitorRecording(TraceRecorder* tr)
-{
-    JSContext* cx = tr->cx;
-
-    if (tr->lirbuf->outOMem()) {
+JS_REQUIRES_STACK JSMonitorRecordingStatus
+TraceRecorder::monitorRecording(JSOp op)
+{
+    if (lirbuf->outOMem()) {
         js_AbortRecording(cx, "no more LIR memory");
         js_FlushJITCache(cx);
-        return false;
-    }
-
-    // Process deepAbort() requests now.
-    if (tr->wasDeepAborted()) {
+        return JSMRS_STOP;
+    }
+
+    /* Process deepAbort() requests now. */
+    if (wasDeepAborted()) {
         js_AbortRecording(cx, "deep abort requested");
-        return false;
-    }
-
-    if (tr->walkedOutOfLoop())
-        return js_CloseLoop(cx);
-
-    // Clear one-shot state used to communicate between record_JSOP_CALL and post-                                                                                            
-    // opcode-case-guts record hook (record_FastNativeCallComplete).
-    tr->pendingTraceableNative = NULL;
-
-    // In the future, handle dslots realloc by computing an offset from dslots instead.
-    if (tr->global_dslots != tr->globalObj->dslots) {
-        js_AbortRecording(cx, "globalObj->dslots reallocated");
-        return false;
-    }
-
-    jsbytecode* pc = cx->fp->regs->pc;
-
-    /* If we hit a break, end the loop and generate an always taken loop exit guard. For other
-       downward gotos (like if/else) continue recording. */
-    if (*pc == JSOP_GOTO || *pc == JSOP_GOTOX) {
-        jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
-        if (sn && SN_TYPE(sn) == SRC_BREAK) {
-            AUDIT(breakLoopExits);
-            tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
+        return JSMRS_STOP;
+    }
+
+    if (walkedOutOfLoop()) {
+        if (!js_CloseLoop(cx))
+            return JSMRS_STOP;
+    } else {
+        // Clear one-shot state used to communicate between record_JSOP_CALL and post-
+        // opcode-case-guts record hook (record_FastNativeCallComplete).
+        pendingTraceableNative = NULL;
+
+        // In the future, handle dslots realloc by computing an offset from dslots instead.
+        if (global_dslots != globalObj->dslots) {
+            js_AbortRecording(cx, "globalObj->dslots reallocated");
+            return JSMRS_STOP;
+        }
+
+        jsbytecode* pc = cx->fp->regs->pc;
+
+        /* If we hit a break, end the loop and generate an always taken loop exit guard. For other
+           downward gotos (like if/else) continue recording. */
+        if (*pc == JSOP_GOTO || *pc == JSOP_GOTOX) {
+            jssrcnote* sn = js_GetSrcNote(cx->fp->script, pc);
+            if (sn && SN_TYPE(sn) == SRC_BREAK) {
+                AUDIT(breakLoopExits);
+                endLoop(JS_TRACE_MONITOR(cx).fragmento);
+                js_DeleteRecorder(cx);
+                return JSMRS_STOP; /* done recording */
+            }
+        }
+
+        /* An explicit return from callDepth 0 should end the loop, not abort it. */
+        if (*pc == JSOP_RETURN && callDepth == 0) {
+            AUDIT(returnLoopExits);
+            endLoop(JS_TRACE_MONITOR(cx).fragmento);
             js_DeleteRecorder(cx);
-            return false; /* done recording */
+            return JSMRS_STOP; /* done recording */
         }
     }
 
-    /* An explicit return from callDepth 0 should end the loop, not abort it. */
-    if (*pc == JSOP_RETURN && tr->callDepth == 0) {
-        AUDIT(returnLoopExits);
-        tr->endLoop(JS_TRACE_MONITOR(cx).fragmento);
-        js_DeleteRecorder(cx);
-        return false; /* done recording */
-    }
-
     /* If it's not a break or a return from a loop, continue recording and follow the trace. */
-    return true;
+
+    /* We check for imacro-calling bytecodes inside the switch cases to resolve
+       the "if" condition at the compile time. */
+    bool flag;
+    switch (op) {
+      default: goto abort_recording;
+# define OPDEF(x,val,name,token,length,nuses,ndefs,prec,format)               \
+        case x:                                                               \
+          flag = record_##x();                                                \
+          if (x == JSOP_ITER || x == JSOP_NEXTITER || x == JSOP_APPLY ||      \
+              JSOP_IS_BINARY(x) || JSOP_IS_UNARY(x)) {                       \
+              goto imacro;                                                    \
+          }                                                                   \
+        break;
+# include "jsopcode.tbl"
+# undef OPDEF
+    }
+
+    if (flag)
+        return JSMRS_CONTINUE;
+    goto abort_recording;
+
+  imacro:
+    /* We save macro-generated code size also via bool TraceRecorder::record_JSOP_*
+       return type, instead of a three-state: OK, ABORTED, IMACRO_STARTED. But the
+       price of this is the JSFRAME_IMACRO_START frame flag. We need one more bit
+       to detect that TraceRecorder::call_imacro was invoked by the record_JSOP_
+       method. */
+    if (flag)
+        return JSMRS_CONTINUE;
+    if (cx->fp->flags & JSFRAME_IMACRO_START) {
+        cx->fp->flags &= ~JSFRAME_IMACRO_START;
+        return JSMRS_IMACRO;
+    }
+
+  abort_recording:
+    js_AbortRecording(cx, js_CodeName[op]);
+    return JSMRS_STOP;
 }
 
 /* If used on a loop trace, blacklists the root peer instead of the given fragment. */
 void
 js_BlacklistPC(Fragmento* frago, Fragment* frag)
 {
     if (frag->kind == LoopTrace)
         frag = frago->getLoop(frag->ip);
@@ -5499,22 +5536,16 @@ TraceRecorder::record_LeaveFrame()
     // LeaveFrame gets called after the interpreter popped the frame and
     // stored rval, so cx->fp not cx->fp->down, and -1 not 0.
     atoms = cx->fp->script->atomMap.vector;
     set(&stackval(-1), rval_ins, true);
     return true;
 }
 
 JS_REQUIRES_STACK bool
-TraceRecorder::record_JSOP_INTERRUPT()
-{
-    return false;
-}
-
-JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_PUSH()
 {
     stack(0, INS_CONST(JSVAL_TO_BOOLEAN(JSVAL_VOID)));
     return true;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::record_JSOP_POPV()
@@ -6830,17 +6861,17 @@ TraceRecorder::record_FastNativeCallComp
       default:
         /* Convert the result to double if the builtin returns int32. */
         if (JSVAL_IS_NUMBER(v) &&
             (pendingTraceableNative->builtin->_argtypes & 3) == nanojit::ARGSIZE_LO) {
             set(&v, lir->ins1(LIR_i2f, v_ins));
         }
     }
 
-    // We'll null pendingTraceableNative in js_MonitorRecording, on the next op cycle.
+    // We'll null pendingTraceableNative in monitorRecording, on the next op cycle.
     // There must be a next op since the stack is non-empty.
     return ok;
 }
 
 JS_REQUIRES_STACK bool
 TraceRecorder::name(jsval*& vp)
 {
     JSObject* obj = cx->fp->scopeChain;
@@ -8619,16 +8650,17 @@ InitIMacroCode()
 
 #define UNUSED(n)                                                             \
     JS_REQUIRES_STACK bool                                                    \
     TraceRecorder::record_JSOP_UNUSED##n() {                                  \
         JS_NOT_REACHED("JSOP_UNUSED" # n);                                    \
         return false;                                                         \
     }
 
+UNUSED(135)
 UNUSED(203)
 UNUSED(204)
 UNUSED(205)
 UNUSED(206)
 UNUSED(207)
 UNUSED(208)
 UNUSED(209)
 UNUSED(219)
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -256,16 +256,22 @@ struct FrameInfo {
         struct {
             uint16  spdist;     // distance from fp->slots to fp->regs->sp at JSOP_CALL
             uint16  argc;       // actual argument count, may be < fun->nargs
         } s;
         uint32      word;       // for spdist/argc LIR store in record_JSOP_CALL
     };
 };
 
+enum JSMonitorRecordingStatus {
+    JSMRS_CONTINUE,
+    JSMRS_STOP,
+    JSMRS_IMACRO
+};
+
 class TraceRecorder : public avmplus::GCObject {
     JSContext*              cx;
     JSTraceMonitor*         traceMonitor;
     JSObject*               globalObj;
     Tracker                 tracker;
     Tracker                 nativeFrameTracker;
     char*                   entryTypeMap;
     unsigned                callDepth;
@@ -311,16 +317,17 @@ class TraceRecorder : public avmplus::GC
                                   unsigned callDepth, uint8* globalTypeMap, uint8* stackTypeMap);
     void trackNativeStackUse(unsigned slots);
 
     JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot);
 
     JS_REQUIRES_STACK nanojit::LIns* guard(bool expected, nanojit::LIns* cond,
                                            ExitType exitType);
     nanojit::LIns* guard(bool expected, nanojit::LIns* cond, nanojit::LIns* exit);
+
     nanojit::LIns* addName(nanojit::LIns* ins, const char* name);
 
     JS_REQUIRES_STACK nanojit::LIns* get(jsval* p) const;
     nanojit::LIns* writeBack(nanojit::LIns* i, nanojit::LIns* base, ptrdiff_t offset);
     JS_REQUIRES_STACK void set(jsval* p, nanojit::LIns* l, bool initializing = false);
 
     JS_REQUIRES_STACK bool checkType(jsval& v, uint8 t, jsval*& stage_val,
                                      nanojit::LIns*& stage_ins, unsigned& stage_count);
@@ -419,24 +426,24 @@ class TraceRecorder : public avmplus::GC
     JS_REQUIRES_STACK void trackCfgMerges(jsbytecode* pc);
     JS_REQUIRES_STACK void flipIf(jsbytecode* pc, bool& cond);
     JS_REQUIRES_STACK void fuseIf(jsbytecode* pc, bool cond, nanojit::LIns* x);
 
     bool hasMethod(JSObject* obj, jsid id);
     bool hasIteratorMethod(JSObject* obj);
 
 public:
-    friend JS_REQUIRES_STACK bool js_MonitorRecording(TraceRecorder* tr);
-
     JS_REQUIRES_STACK
     TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*,
                   unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap, 
                   VMSideExit* expectedInnerExit, nanojit::Fragment* outerToBlacklist);
     ~TraceRecorder();
 
+    JS_REQUIRES_STACK JSMonitorRecordingStatus monitorRecording(JSOp op);
+
     JS_REQUIRES_STACK uint8 determineSlotType(jsval* vp) const;
     JS_REQUIRES_STACK nanojit::LIns* snapshot(ExitType exitType);
     nanojit::Fragment* getFragment() const { return fragment; }
     JS_REQUIRES_STACK bool isLoopHeader(JSContext* cx) const;
     JS_REQUIRES_STACK void compile(nanojit::Fragmento* fragmento);
     JS_REQUIRES_STACK bool closeLoop(nanojit::Fragmento* fragmento, bool& demote,
                                      unsigned *demotes);
     JS_REQUIRES_STACK void endLoop(nanojit::Fragmento* fragmento);
@@ -476,68 +483,36 @@ public:
 };
 #define TRACING_ENABLED(cx)       JS_HAS_OPTION(cx, JSOPTION_JIT)
 #define TRACE_RECORDER(cx)        (JS_TRACE_MONITOR(cx).recorder)
 #define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
 
 #define JSOP_IS_BINARY(op) ((uintN)((op) - JSOP_BITOR) <= (uintN)(JSOP_MOD - JSOP_BITOR))
 #define JSOP_IS_UNARY(op) ((uintN)((op) - JSOP_NEG) <= (uintN)(JSOP_POS - JSOP_NEG))
 
-/*
- * See jsinterp.cpp for the ENABLE_TRACER definition. Also note how comparing x
- * to JSOP_* constants specializes trace-recording code at compile time either
- * to include imacro support, or exclude it altogether for this particular x.
- *
- * We save macro-generated code size also via bool TraceRecorder::record_JSOP_*
- * return type, instead of a three-state: OK, ABORTED, IMACRO_STARTED. But the
- * price of this is the JSFRAME_IMACRO_START frame flag. We need one more bit
- * to detect that TraceRecorder::call_imacro was invoked by the record_JSOP_*
- * method invoked by TRACE_ARGS_.
- */
-#define RECORD_ARGS(x,args)                                                   \
+#define TRACE_ARGS_(x,args)                                                   \
     JS_BEGIN_MACRO                                                            \
-        if (!js_MonitorRecording(TRACE_RECORDER(cx))) {                       \
-            ENABLE_TRACER(0);                                                 \
-        } else {                                                              \
-            TRACE_ARGS_(x, args,                                              \
-                if ((fp->flags & JSFRAME_IMACRO_START) &&                     \
-                    (x == JSOP_ITER || x == JSOP_NEXTITER ||                  \
-                     x == JSOP_APPLY || JSOP_IS_BINARY(x) ||                  \
-                     JSOP_IS_UNARY(op))) {                                    \
-                    fp->flags &= ~JSFRAME_IMACRO_START;                       \
-                    atoms = COMMON_ATOMS_START(&rt->atomState);               \
-                    op = JSOp(*regs.pc);                                      \
-                    DO_OP();                                                  \
-                }                                                             \
-            );                                                                \
-         }                                                                    \
+        TraceRecorder* tr_ = TRACE_RECORDER(cx);                              \
+        if (tr_ && !tr_->record_##x args)                                     \
+            js_AbortRecording(cx, #x);                                        \
     JS_END_MACRO
 
-#define TRACE_ARGS_(x,args,onfalse)                                           \
-    JS_BEGIN_MACRO                                                            \
-        TraceRecorder* tr_ = TRACE_RECORDER(cx);                              \
-        if (tr_ && !tr_->record_##x args) {                                   \
-            onfalse                                                           \
-            js_AbortRecording(cx, #x);                                        \
-            ENABLE_TRACER(0);                                                 \
-        }                                                                     \
-    JS_END_MACRO
-
-#define TRACE_ARGS(x,args)      TRACE_ARGS_(x, args, )
-
-#define RECORD(x)               RECORD_ARGS(x, ())
+#define TRACE_ARGS(x,args)      TRACE_ARGS_(x, args)
 #define TRACE_0(x)              TRACE_ARGS(x, ())
 #define TRACE_1(x,a)            TRACE_ARGS(x, (a))
 #define TRACE_2(x,a,b)          TRACE_ARGS(x, (a, b))
 
 extern JS_REQUIRES_STACK bool
 js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount);
 
-extern JS_REQUIRES_STACK bool
-js_MonitorRecording(TraceRecorder *tr);
+#ifdef DEBUG
+# define js_AbortRecording(cx, reason) js_AbortRecordingImpl(cx, reason)
+#else
+# define js_AbortRecording(cx, reason) js_AbortRecordingImpl(cx)
+#endif
 
 extern JS_REQUIRES_STACK void
 js_AbortRecording(JSContext* cx, const char* reason);
 
 extern void
 js_InitJIT(JSTraceMonitor *tm);
 
 extern void
@@ -546,16 +521,15 @@ js_FinishJIT(JSTraceMonitor *tm);
 extern void
 js_FlushJITCache(JSContext* cx);
 
 extern void
 js_FlushJITOracle(JSContext* cx);
 
 #else  /* !JS_TRACER */
 
-#define RECORD(x)               ((void)0)
 #define TRACE_0(x)              ((void)0)
 #define TRACE_1(x,a)            ((void)0)
 #define TRACE_2(x,a,b)          ((void)0)
 
 #endif /* !JS_TRACER */
 
 #endif /* jstracer_h___ */
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -199,17 +199,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 38)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 39)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool