Bug 487676 - Nesting deep-aborting trace calls don't work. r=gal.
authorJason Orendorff <jorendorff@mozilla.com>
Thu, 09 Apr 2009 18:07:00 -0500
changeset 27166 162de46bc0f2ae012bf6d2934cfaca28de2a7ac0
parent 27165 38c0b853764c96a460ef614321077c9be9d79920
child 27167 ec6218a54521bc31fea8dab92f8d2793a861f211
push id6416
push userrsayre@mozilla.com
push dateFri, 10 Apr 2009 08:17:12 +0000
treeherdermozilla-central@99d61ba5f125 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgal
bugs487676
milestone1.9.2a1pre
Bug 487676 - Nesting deep-aborting trace calls don't work. r=gal.
js/src/jsarray.cpp
js/src/jsbuiltins.cpp
js/src/jsbuiltins.h
js/src/jscntxt.h
js/src/jsobj.cpp
js/src/jstracer.cpp
js/src/jstracer.h
js/src/xpconnect/src/qsgen.py
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1685,28 +1685,28 @@ InitArrayObject(JSContext *cx, JSObject 
 }
 
 #ifdef JS_TRACER
 static JSString* FASTCALL
 Array_p_join(JSContext* cx, JSObject* obj, JSString *str)
 {
     JSAutoTempValueRooter tvr(cx);
     if (!array_join_sub(cx, obj, TO_STRING, str, tvr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return NULL;
     }
     return JSVAL_TO_STRING(tvr.value());
 }
 
 static JSString* FASTCALL
 Array_p_toString(JSContext* cx, JSObject* obj)
 {
     JSAutoTempValueRooter tvr(cx);
     if (!array_join_sub(cx, obj, TO_STRING, NULL, tvr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return NULL;
     }
     return JSVAL_TO_STRING(tvr.value());
 }
 #endif
 
 /*
  * Perl-inspired join, reverse, and sort.
@@ -2312,17 +2312,17 @@ static jsval FASTCALL
 Array_p_push1(JSContext* cx, JSObject* obj, jsval v)
 {
     JSAutoTempValueRooter tvr(cx, v);
     if (OBJ_IS_DENSE_ARRAY(cx, obj)
         ? array_push1_dense(cx, obj, v, tvr.addr())
         : array_push_slowly(cx, obj, 1, tvr.addr(), tvr.addr())) {
         return tvr.value();
     }
-    cx->builtinStatus |= JSBUILTIN_ERROR;
+    js_SetBuiltinError(cx);
     return JSVAL_VOID;
 }
 #endif
 
 static JSBool
 array_push(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
@@ -2384,17 +2384,17 @@ static jsval FASTCALL
 Array_p_pop(JSContext* cx, JSObject* obj)
 {
     JSAutoTempValueRooter tvr(cx);
     if (OBJ_IS_DENSE_ARRAY(cx, obj)
         ? array_pop_dense(cx, obj, tvr.addr())
         : array_pop_slowly(cx, obj, tvr.addr())) {
         return tvr.value();
     }
-    cx->builtinStatus |= JSBUILTIN_ERROR;
+    js_SetBuiltinError(cx);
     return JSVAL_VOID;
 }
 #endif
 
 static JSBool
 array_pop(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
--- a/js/src/jsbuiltins.cpp
+++ b/js/src/jsbuiltins.cpp
@@ -58,16 +58,22 @@
 #include "jsbuiltins.h"
 #include "jstracer.h"
 
 using namespace avmplus;
 using namespace nanojit;
 
 extern jsdouble js_NaN;
 
+JS_FRIEND_API(void)
+js_SetTraceableNativeFailed(JSContext *cx)
+{
+    js_SetBuiltinError(cx);
+}
+
 /*
  * NB: bool FASTCALL is not compatible with Nanojit's calling convention usage.
  * Do not use bool FASTCALL, use JSBool only!
  */
 
 jsdouble FASTCALL
 js_dmod(jsdouble a, jsdouble b)
 {
--- a/js/src/jsbuiltins.h
+++ b/js/src/jsbuiltins.h
@@ -126,17 +126,17 @@ struct JSTraceableNative {
  *
  *   - If a traceable native's return type ends with _FAIL, it always runs to
  *     completion.  It can either succeed or fail with an error or exception;
  *     on success, it may or may not stay on trace.  There may be side effects
  *     in any case.  If the call succeeds but bails off trace, we resume in the
  *     interpreter at the next opcode.
  *
  *     _FAIL builtins indicate failure or bailing off trace by setting bits in
- *     cx->builtinStatus.
+ *     cx->interpState->builtinStatus.
  *
  *   - If a traceable native's return type contains _RETRY, it can either
  *     succeed, fail with a JS exception, or tell the caller to bail off trace
  *     and retry the call from the interpreter.  The last case happens if the
  *     builtin discovers that it can't do its job without examining the JS
  *     stack, reentering the interpreter, accessing properties of the global
  *     object, etc.
  *
@@ -149,17 +149,17 @@ struct JSTraceableNative {
  *
  *         BOOL_RETRY: JSVAL_TO_BOOLEAN(JSVAL_VOID)
  *         INT32_RETRY: any negative value
  *         STRING_RETRY: NULL
  *         OBJECT_RETRY_NULL: NULL
  *         JSVAL_RETRY: JSVAL_ERROR_COOKIE
  *
  *     _RETRY function calls are faster than _FAIL calls.  Each _RETRY call
- *     saves a write to cx->bailExit and a read from cx->builtinStatus.
+ *     saves two writes to cx->bailExit and a read from state->builtinStatus.
  *
  *   - All other traceable natives are infallible (e.g. Date.now, Math.log).
  *
  * Special builtins known to the tracer can have their own idiosyncratic
  * error codes.
  *
  * When a traceable native returns a value indicating failure, we fall off
  * trace.  If an exception is pending, it is thrown; otherwise, we assume the
@@ -392,16 +392,20 @@ js_Int32ToId(JSContext* cx, int32 index,
         return JS_TRUE;
     }
     JSString* str = js_NumberToString(cx, index);
     if (!str)
         return JS_FALSE;
     return js_ValueToStringId(cx, STRING_TO_JSVAL(str), id);
 }
 
+/* Extern version of js_SetBuiltinError. */
+extern JS_FRIEND_API(void)
+js_SetTraceableNativeFailed(JSContext *cx);
+
 #else
 
 #define JS_DEFINE_CALLINFO_1(linkage, rt, op, at0, cse, fold)
 #define JS_DEFINE_CALLINFO_2(linkage, rt, op, at0, at1, cse, fold)
 #define JS_DEFINE_CALLINFO_3(linkage, rt, op, at0, at1, at2, cse, fold)
 #define JS_DEFINE_CALLINFO_4(linkage, rt, op, at0, at1, at2, at3, cse, fold)
 #define JS_DEFINE_CALLINFO_5(linkage, rt, op, at0, at1, at2, at3, at4, cse, fold)
 #define JS_DECLARE_CALLINFO(name)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -295,24 +295,16 @@ typedef enum JSDestroyContextMode {
 
 typedef enum JSRuntimeState {
     JSRTS_DOWN,
     JSRTS_LAUNCHING,
     JSRTS_UP,
     JSRTS_LANDING
 } JSRuntimeState;
 
-#ifdef JS_TRACER
-typedef enum JSBuiltinStatus {
-    JSBUILTIN_OK = 0,
-    JSBUILTIN_BAILED = 1,
-    JSBUILTIN_ERROR = 2
-} JSBuiltinStatus;
-#endif
-
 typedef enum JSBuiltinFunctionId {
     JSBUILTIN_ObjectToIterator,
     JSBUILTIN_CallIteratorNext,
     JSBUILTIN_GetProperty,
     JSBUILTIN_GetElement,
     JSBUILTIN_SetProperty,
     JSBUILTIN_SetElement,
     JSBUILTIN_LIMIT
@@ -1003,23 +995,16 @@ struct JSContext {
 #ifdef JS_TRACER
     /*
      * State for the current tree execution.  bailExit is valid if the tree has
      * called back into native code via a _FAIL builtin and has not yet bailed,
      * else garbage (NULL in debug builds).
      */
     InterpState         *interpState;
     VMSideExit          *bailExit;
-
-    /*
-     * Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the
-     * JSBUILTIN_BAILED bit if it bails off trace and the JSBUILTIN_ERROR bit
-     * if an error or exception occurred. Cleared on side exit.
-     */
-    uint32              builtinStatus;
 #endif
 };
 
 #ifdef JS_THREADSAFE
 # define JS_THREAD_ID(cx)       ((cx)->thread ? (cx)->thread->id : 0)
 #endif
 
 #ifdef __cplusplus
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1692,17 +1692,17 @@ js_HasOwnProperty(JSContext *cx, JSLooku
 static JSBool FASTCALL
 Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
 {
     jsid id;
     jsval v;
 
     if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) ||
         !js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &v)) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return JSVAL_TO_BOOLEAN(JSVAL_VOID);
     }
 
     JS_ASSERT(JSVAL_IS_BOOLEAN(v));
     return JSVAL_TO_BOOLEAN(v);
 }
 #endif
 
@@ -1737,17 +1737,17 @@ obj_propertyIsEnumerable(JSContext *cx, 
 #ifdef JS_TRACER
 static JSBool FASTCALL
 Object_p_propertyIsEnumerable(JSContext* cx, JSObject* obj, JSString *str)
 {
     jsid id = ATOM_TO_JSID(STRING_TO_JSVAL(str));
     jsval v;
 
     if (!js_PropertyIsEnumerable(cx, obj, id, &v)) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return JSVAL_TO_BOOLEAN(JSVAL_VOID);
     }
 
     JS_ASSERT(JSVAL_IS_BOOLEAN(v));
     return JSVAL_TO_BOOLEAN(v);
 }
 #endif
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -3966,17 +3966,16 @@ LeaveTree(InterpState&, VMSideExit* lr);
 /**
  * Executes a tree.
  */
 static JS_REQUIRES_STACK VMSideExit*
 js_ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
                VMSideExit** innermostNestedGuardp)
 {
     JS_ASSERT(f->root == f && f->code() && f->vmprivate);
-    JS_ASSERT(cx->builtinStatus == 0);
 
     JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
     TreeInfo* ti = (TreeInfo*)f->vmprivate;
     unsigned ngslots = ti->globalSlots->length();
     uint16* gslots = ti->globalSlots->data();
     unsigned globalFrameSize = STOBJ_NSLOTS(globalObj);
 
@@ -3994,16 +3993,17 @@ js_ExecuteTree(JSContext* cx, Fragment* 
     InterpState* state = (InterpState*)alloca(sizeof(InterpState) + (globalFrameSize+1)*sizeof(double));
     state->cx = cx;
     state->inlineCallCountp = &inlineCallCount;
     state->innermostNestedGuardp = innermostNestedGuardp;
     state->outermostTree = ti;
     state->lastTreeExitGuard = NULL;
     state->lastTreeCallGuard = NULL;
     state->rpAtLastTreeCall = NULL;
+    state->builtinStatus = 0;
 
     /* Setup the native global frame. */
     double* global = (double*)(state+1);
 
     /* Setup the native stack frame. */
     double stack_buffer[MAX_NATIVE_STACK_SLOTS];
     state->stackBase = stack_buffer;
     state->sp = stack_buffer + (ti->nativeStackBase/sizeof(double));
@@ -4115,32 +4115,31 @@ LeaveTree(InterpState& state, VMSideExit
         if (state.innermostNestedGuardp)
             *state.innermostNestedGuardp = nested;
         JS_ASSERT(nested);
         JS_ASSERT(nested->exitType == NESTED_EXIT);
         JS_ASSERT(state.lastTreeExitGuard);
         JS_ASSERT(state.lastTreeExitGuard->exitType != NESTED_EXIT);
     }
 
-    int32_t bs = cx->builtinStatus;
-    cx->builtinStatus = 0;
+    int32_t bs = state.builtinStatus;
     bool bailed = innermost->exitType == STATUS_EXIT && (bs & JSBUILTIN_BAILED);
     if (bailed) {
         /*
          * Deep-bail case.
          *
          * A _FAIL native already called LeaveTree. We already reconstructed
          * the interpreter stack, in pre-call state, with pc pointing to the
          * CALL/APPLY op, for correctness. Then we continued in native code.
          */
         if (!(bs & JSBUILTIN_ERROR)) {
             /*
              * The native succeeded (no exception or error). After it returned, the
              * trace stored the return value (at the top of the native stack) and
-             * then immediately flunked the guard on cx->builtinStatus.
+             * then immediately flunked the guard on state->builtinStatus.
              *
              * Now LeaveTree has been called again from the tail of
              * js_ExecuteTree. We are about to return to the interpreter. Adjust
              * the top stack frame to resume on the next op.
              */
             JS_ASSERT(*cx->fp->regs->pc == JSOP_CALL || *cx->fp->regs->pc == JSOP_APPLY);
             uintN argc = GET_ARGC(cx->fp->regs->pc);
             cx->fp->regs->pc += JSOP_CALL_LENGTH;
@@ -4938,17 +4937,17 @@ js_DeepBail(JSContext *cx)
 
     /* It's a bug if a non-FAIL_STATUS builtin gets here. */
     JS_ASSERT(cx->bailExit);
 
     JS_TRACE_MONITOR(cx).onTrace = false;
     JS_TRACE_MONITOR(cx).prohibitRecording = true;
     LeaveTree(*cx->interpState, cx->bailExit);
     cx->bailExit = NULL;
-    cx->builtinStatus |= JSBUILTIN_BAILED;
+    cx->interpState->builtinStatus |= JSBUILTIN_BAILED;
 }
 
 JS_REQUIRES_STACK jsval&
 TraceRecorder::argval(unsigned n) const
 {
     JS_ASSERT(n < cx->fp->fun->nargs);
     return cx->fp->argv[n];
 }
@@ -7455,17 +7454,17 @@ GetProperty(JSContext *cx, uintN argc, j
 static jsval FASTCALL
 GetProperty_tn(JSContext *cx, jsbytecode *pc, JSObject *obj, JSString *name)
 {
     JSAutoTempIdRooter idr(cx);
     JSAutoTempValueRooter tvr(cx);
 
     if (!js_ValueToStringId(cx, STRING_TO_JSVAL(name), idr.addr()) ||
         !OBJ_GET_PROPERTY(cx, obj, idr.id(), tvr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         *tvr.addr() = JSVAL_ERROR_COOKIE;
     }
     return tvr.value();
 }
 
 static JSBool
 GetElement(JSContext *cx, uintN argc, jsval *vp)
 {
@@ -7484,21 +7483,21 @@ GetElement(JSContext *cx, uintN argc, js
 
 static jsval FASTCALL
 GetElement_tn(JSContext* cx, jsbytecode *pc, JSObject* obj, int32 index)
 {
     JSAutoTempValueRooter tvr(cx);
     JSAutoTempIdRooter idr(cx);
 
     if (!js_Int32ToId(cx, index, idr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return JSVAL_ERROR_COOKIE;
     }
     if (!OBJ_GET_PROPERTY(cx, obj, idr.id(), tvr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         *tvr.addr() = JSVAL_ERROR_COOKIE;
     }
     return tvr.value();
 }
 
 JS_DEFINE_TRCINFO_1(GetProperty,
     (4, (static, JSVAL_FAIL,    GetProperty_tn, CONTEXT, PC, THIS, STRING,      0, 0)))
 JS_DEFINE_TRCINFO_1(GetElement,
@@ -7599,17 +7598,17 @@ SetProperty(JSContext *cx, uintN argc, j
 static JSBool FASTCALL
 SetProperty_tn(JSContext* cx, JSObject* obj, JSString* idstr, jsval v)
 {
     JSAutoTempValueRooter tvr(cx, v);
     JSAutoTempIdRooter idr(cx);
 
     if (!js_ValueToStringId(cx, STRING_TO_JSVAL(idstr), idr.addr()) ||
         !OBJ_SET_PROPERTY(cx, obj, idr.id(), tvr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
     }
     return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID);
 }
 
 static JSBool
 SetElement(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval *argv;
@@ -7630,17 +7629,17 @@ SetElement(JSContext *cx, uintN argc, js
 static JSBool FASTCALL
 SetElement_tn(JSContext* cx, JSObject* obj, int32 index, jsval v)
 {
     JSAutoTempIdRooter idr(cx);
     JSAutoTempValueRooter tvr(cx, v);
 
     if (!js_Int32ToId(cx, index, idr.addr()) ||
         !OBJ_SET_PROPERTY(cx, obj, idr.id(), tvr.addr())) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
     }
     return JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID);
 }
 
 JS_DEFINE_TRCINFO_1(SetProperty,
     (4, (extern, BOOL_FAIL,     SetProperty_tn, CONTEXT, THIS, STRING, JSVAL,   0, 0)))
 JS_DEFINE_TRCINFO_1(SetElement,
     (4, (extern, BOOL_FAIL,     SetElement_tn,  CONTEXT, THIS, INT32, JSVAL,    0, 0)))
@@ -8040,17 +8039,17 @@ TraceRecorder::record_FastNativeCallComp
        type is jsval, snapshot() will also indicate in the type map that the
        element on top of the stack is a boxed value which doesn't need to be
        boxed if the type guard generated by unbox_jsval() fails. */
 
     if (JSTN_ERRTYPE(pendingTraceableNative) == FAIL_STATUS) {
         // Keep cx->bailExit null when it's invalid.
         lir->insStorei(INS_CONSTPTR(NULL), cx_ins, (int) offsetof(JSContext, bailExit));
 
-        LIns* status = lir->insLoad(LIR_ld, cx_ins, (int) offsetof(JSContext, builtinStatus));
+        LIns* status = lir->insLoad(LIR_ld, lirbuf->state, (int) offsetof(InterpState, builtinStatus));
         if (pendingTraceableNative == generatedTraceableNative) {
             LIns* ok_ins = v_ins;
 
             /*
              * Custom implementations of Iterator.next() throw a StopIteration exception.
              * Catch and clear it and set the return value to JSVAL_HOLE in this case.
              */
             if (uintptr_t(cx->fp->regs->pc - nextiter_imacros.custom_iter_next) <
@@ -8078,17 +8077,17 @@ TraceRecorder::record_FastNativeCallComp
             JS_STATIC_ASSERT((1 - JS_FALSE) << 1 == JSBUILTIN_ERROR);
             status = lir->ins2(LIR_or,
                                status,
                                lir->ins2i(LIR_lsh,
                                           lir->ins2i(LIR_xor,
                                                      lir->ins2i(LIR_and, ok_ins, 1),
                                                      1),
                                           1));
-            lir->insStorei(status, cx_ins, (int) offsetof(JSContext, builtinStatus));
+            lir->insStorei(status, lirbuf->state, (int) offsetof(InterpState, builtinStatus));
         }
         guard(true,
               lir->ins_eq0(status),
               STATUS_EXIT);
     }
 
     bool ok = true;
     if (pendingTraceableNative->flags & JSTN_UNBOX_AFTER) {
@@ -9753,17 +9752,17 @@ ObjectToIterator(JSContext *cx, uintN ar
 
 static JSObject* FASTCALL
 ObjectToIterator_tn(JSContext* cx, jsbytecode* pc, JSObject *obj, int32 flags)
 {
     jsval v = OBJECT_TO_JSVAL(obj);
     JSBool ok = js_ValueToIterator(cx, flags, &v);
 
     if (!ok) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return NULL;
     }
     return JSVAL_TO_OBJECT(v);
 }
 
 static JSBool
 CallIteratorNext(JSContext *cx, uintN argc, jsval *vp)
 {
@@ -9772,17 +9771,17 @@ CallIteratorNext(JSContext *cx, uintN ar
 
 static jsval FASTCALL
 CallIteratorNext_tn(JSContext* cx, jsbytecode* pc, JSObject* iterobj)
 {
     JSAutoTempValueRooter tvr(cx);
     JSBool ok = js_CallIteratorNext(cx, iterobj, tvr.addr());
 
     if (!ok) {
-        cx->builtinStatus |= JSBUILTIN_ERROR;
+        js_SetBuiltinError(cx);
         return JSVAL_ERROR_COOKIE;
     }
     return tvr.value();
 }
 
 JS_DEFINE_TRCINFO_1(ObjectToIterator,
     (4, (static, OBJECT_FAIL, ObjectToIterator_tn, CONTEXT, PC, THIS, INT32, 0, 0)))
 JS_DEFINE_TRCINFO_1(CallIteratorNext,
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -336,42 +336,60 @@ public:
         return typeMap.data();
     }
 };
 
 #if defined(JS_JIT_SPEW) && (defined(NANOJIT_IA32) || (defined(NANOJIT_AMD64) && defined(__GNUC__)))
 # define EXECUTE_TREE_TIMER
 #endif
 
+typedef enum JSBuiltinStatus {
+    JSBUILTIN_BAILED = 1,
+    JSBUILTIN_ERROR = 2
+} JSBuiltinStatus;
+
 struct InterpState
 {
     double        *sp;                  // native stack pointer, stack[0] is spbase[0]
     void          *rp;                  // call stack pointer
     JSContext     *cx;                  // current VM context handle
     double        *eos;                 // first unusable word after the native stack
     void          *eor;                 // first unusable word after the call stack
     VMSideExit*    lastTreeExitGuard;   // guard we exited on during a tree call
     VMSideExit*    lastTreeCallGuard;   // guard we want to grow from if the tree
                                         // call exit guard mismatched
     void*          rpAtLastTreeCall;    // value of rp at innermost tree call guard
     TreeInfo*      outermostTree;       // the outermost tree we initially invoked
     double*        stackBase;           // native stack base
     FrameInfo**    callstackBase;       // call stack base
     uintN*         inlineCallCountp;    // inline call count counter
-    VMSideExit** innermostNestedGuardp;
+    VMSideExit**   innermostNestedGuardp;
     void*          stackMark;
     VMSideExit*    innermost;
 #ifdef EXECUTE_TREE_TIMER
     uint64         startTime;
 #endif
 #ifdef DEBUG
     bool           jsframe_pop_blocks_set_on_entry;
 #endif
+
+    /*
+     * Used by _FAIL builtins; see jsbuiltins.h. The builtin sets the
+     * JSBUILTIN_BAILED bit if it bails off trace and the JSBUILTIN_ERROR bit
+     * if an error or exception occurred.
+     */
+    uint32         builtinStatus;
 };
 
+static JS_INLINE void
+js_SetBuiltinError(JSContext *cx)
+{
+    cx->interpState->builtinStatus |= JSBUILTIN_ERROR;
+}
+
 enum JSMonitorRecordingStatus {
     JSMRS_CONTINUE,
     JSMRS_STOP,
     JSMRS_IMACRO
 };
 
 class TraceRecorder : public avmplus::GCObject {
     JSContext*              cx;
--- a/js/src/xpconnect/src/qsgen.py
+++ b/js/src/xpconnect/src/qsgen.py
@@ -866,17 +866,17 @@ def getTraceInfoType(type):
 def getTraceInfoDefaultReturn(type):
     traceType = traceTypeMap.get(type) or traceTypeMap.get("_default")
     assert traceType
     return traceType[2]
 
 def getFailureString(retval, indent):
     assert indent > 0
     ret = " " * (4 * indent)
-    ret += "cx->builtinStatus |= JSBUILTIN_ERROR;\n"
+    ret += "js_SetTraceableNativeFailed(cx);\n"
     ret += " " * (4 * indent)
     ret += "return %s;\n" % retval
     ret += " " * (4 * (indent - 1))
     ret += "}\n"
     return ret
  
 def writeFailure(f, retval, indent):
     f.write(getFailureString(retval, indent))