Bug 476177 - TM: cx->stackPool must not be accessed on trace. r=brendan.
authorJason Orendorff <jorendorff@mozilla.com>
Fri, 30 Jan 2009 17:40:05 -0600
changeset 24499 932126be5356e732f01757ee6df78f50280f8f0f
parent 24498 313563f92c492433159a42f64d20e2839eade0e1
child 24500 056d0d9757658e5d3976a276e63ddd82b5251fd3
push id5074
push userrsayre@mozilla.com
push dateSat, 31 Jan 2009 19:45:42 +0000
treeherdermozilla-central@f1cade532f6f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs476177
milestone1.9.2a1pre
Bug 476177 - TM: cx->stackPool must not be accessed on trace. r=brendan.
js/src/jsapi.cpp
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsdbgapi.cpp
js/src/jsdbgapi.h
js/src/jsfun.cpp
js/src/jsfun.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsiter.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsstaticcheck.h
js/src/jsstr.cpp
js/src/jstracer.cpp
js/src/jstracer.h
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -74,24 +74,21 @@
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsregexp.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
+#include "jstracer.h"
 #include "jsdbgapi.h"
 #include "prmjtime.h"
 #include "jsstaticcheck.h"
 
-#if !defined JS_THREADSAFE && defined JS_TRACER
-#include "jstracer.h"
-#endif
-
 #if JS_HAS_FILE_OBJECT
 #include "jsfile.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
@@ -318,16 +315,17 @@ JS_PushArgumentsVA(JSContext *cx, void *
         /*
          * Count non-space non-star characters as individual jsval arguments.
          * This may over-allocate stack, but we'll fix below.
          */
         if (isspace(c) || c == '*')
             continue;
         argc++;
     }
+    js_LeaveTrace(cx);
     sp = js_AllocStack(cx, argc, markp);
     if (!sp)
         return NULL;
     argv = sp;
     while ((c = *format++) != '\0') {
         if (isspace(c) || c == '*')
             continue;
         switch (c) {
@@ -414,16 +412,17 @@ bad:
     js_FreeStack(cx, *markp);
     return NULL;
 }
 
 JS_PUBLIC_API(void)
 JS_PopArguments(JSContext *cx, void *mark)
 {
     CHECK_REQUEST(cx);
+    JS_ASSERT_NOT_ON_TRACE(cx);
     js_FreeStack(cx, mark);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_AddArgumentFormatter(JSContext *cx, const char *format,
                         JSArgumentFormatter formatter)
 {
     size_t length;
@@ -2062,16 +2061,17 @@ JS_SetExtraGCRoots(JSRuntime *rt, JSTrac
     rt->gcExtraRootsData = data;
 }
 
 JS_PUBLIC_API(void)
 JS_TraceRuntime(JSTracer *trc)
 {
     JSBool allAtoms = trc->context->runtime->gcKeepAtoms != 0;
 
+    js_LeaveTrace(trc->context);
     js_TraceRuntime(trc, allAtoms);
 }
 
 #ifdef DEBUG
 
 #ifdef HAVE_XPCONNECT
 #include "dump_xpc.h"
 #endif
@@ -2478,16 +2478,18 @@ extern JS_PUBLIC_API(JSBool)
 JS_IsGCMarkingTracer(JSTracer *trc)
 {
     return IS_GC_MARKING_TRACER(trc);
 }
 
 JS_PUBLIC_API(void)
 JS_GC(JSContext *cx)
 {
+    js_LeaveTrace(cx);
+
     /* Don't nuke active arenas if executing or compiling. */
     if (cx->stackPool.current == &cx->stackPool.first)
         JS_FinishArenaPool(&cx->stackPool);
     if (cx->tempPool.current == &cx->tempPool.first)
         JS_FinishArenaPool(&cx->tempPool);
     js_GC(cx, GC_NORMAL);
 }
 
@@ -5441,18 +5443,17 @@ JS_SaveFrameChain(JSContext *cx)
     cx->dormantFrameChain = fp;
     cx->fp = NULL;
     return fp;
 }
 
 JS_PUBLIC_API(void)
 JS_RestoreFrameChain(JSContext *cx, JSStackFrame *fp)
 {
-    JS_ASSERT(!JS_ON_TRACE(cx));
-    VOUCH_DOES_NOT_REQUIRE_STACK();
+    JS_ASSERT_NOT_ON_TRACE(cx);
     JS_ASSERT(!cx->fp);
     if (!fp)
         return;
 
     JS_ASSERT(fp == cx->dormantFrameChain);
     cx->fp = fp;
     cx->dormantFrameChain = fp->dormantNext;
     fp->dormantNext = NULL;
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1615,17 +1615,17 @@ array_reverse(JSContext *cx, uintN argc,
 typedef struct MSortArgs {
     size_t       elsize;
     JSComparator cmp;
     void         *arg;
     JSBool       fastcopy;
 } MSortArgs;
 
 /* Helper function for js_MergeSort. */
-static JSBool
+static JS_REQUIRES_STACK JSBool
 MergeArrays(MSortArgs *msa, void *src, void *dest, size_t run1, size_t run2)
 {
     void *arg, *a, *b, *c;
     size_t elsize, runtotal;
     int cmp_result;
     JSComparator cmp;
     JSBool fastcopy;
 
@@ -1676,17 +1676,17 @@ MergeArrays(MSortArgs *msa, void *src, v
 
     return JS_TRUE;
 }
 
 /*
  * This sort is stable, i.e. sequence of equal elements is preserved.
  * See also bug #224128.
  */
-JSBool
+JS_REQUIRES_STACK JSBool
 js_MergeSort(void *src, size_t nel, size_t elsize,
              JSComparator cmp, void *arg, void *tmp)
 {
     void *swap, *vec1, *vec2;
     MSortArgs msa;
     size_t i, j, lo, hi, run;
     JSBool fastcopy;
     int cmp_result;
@@ -1762,17 +1762,17 @@ js_MergeSort(void *src, size_t nel, size
 }
 
 typedef struct CompareArgs {
     JSContext   *context;
     jsval       fval;
     jsval       *elemroot;      /* stack needed for js_Invoke */
 } CompareArgs;
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 sort_compare(void *arg, const void *a, const void *b, int *result)
 {
     jsval av = *(const jsval *)a, bv = *(const jsval *)b;
     CompareArgs *ca = (CompareArgs *) arg;
     JSContext *cx = ca->context;
     jsval *invokevp, *sp;
     jsdouble cmp;
 
@@ -1830,17 +1830,17 @@ sort_compare_strings(void *arg, const vo
 
 /*
  * The array_sort function below assumes JSVAL_NULL is zero in order to
  * perform initialization using memset.  Other parts of SpiderMonkey likewise
  * "know" that JSVAL_NULL is zero; this static assertion covers all cases.
  */
 JS_STATIC_ASSERT(JSVAL_NULL == 0);
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_sort(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval *argv, fval, *vec, *mergesort_tmp, v;
     JSObject *obj;
     CompareArgs ca;
     jsuint len, newlen, i, undefs;
     JSTempValueRooter tvr;
     JSBool hole;
@@ -2741,17 +2741,17 @@ typedef enum ArrayExtraMode {
     MAP,
     FILTER,
     SOME,
     EVERY
 } ArrayExtraMode;
 
 #define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT)
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, jsval *vp)
 {
     JSObject *obj;
     jsuint length, newlen;
     jsval *argv, *elemroot, *invokevp, *sp;
     JSBool ok, cond, hole;
     JSObject *callable, *thisp, *newarr;
     jsint start, end, step, i;
@@ -2925,53 +2925,53 @@ array_extra(JSContext *cx, ArrayExtraMod
 
   out:
     js_FreeStack(cx, mark);
     if (ok && mode == FILTER)
         ok = js_SetLengthProperty(cx, newarr, newlen);
     return ok;
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_forEach(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, FOREACH, argc, vp);
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_map(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, MAP, argc, vp);
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_reduce(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, REDUCE, argc, vp);
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_reduceRight(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, REDUCE_RIGHT, argc, vp);
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_filter(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, FILTER, argc, vp);
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_some(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, SOME, argc, vp);
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 array_every(JSContext *cx, uintN argc, jsval *vp)
 {
     return array_extra(cx, EVERY, argc, vp);
 }
 #endif
 
 static JSPropertySpec array_props[] = {
     {js_length_str,   -1,   JSPROP_SHARED | JSPROP_PERMANENT,
--- a/js/src/jsarray.h
+++ b/js/src/jsarray.h
@@ -117,17 +117,17 @@ typedef JSBool (*JSComparator)(void *arg
 /*
  * NB: vec is the array to be sorted, tmp is temporary space at least as big
  * as vec. Both should be GC-rooted if appropriate.
  *
  * The sorted result is in vec. vec may be in an inconsistent state if the
  * comparator function cmp returns an error inside a comparison, so remember
  * to check the return value of this function.
  */
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_MergeSort(void *vec, size_t nel, size_t elsize, JSComparator cmp,
              void *arg, void *tmp);
 
 #ifdef DEBUG_ARRAYS
 extern JSBool
 js_ArrayInfo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 #endif
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -59,16 +59,17 @@
 #include "jsgc.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
+#include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jstracer.h"
 
 #ifdef JS_THREADSAFE
 #include "prtypes.h"
 
 /*
  * The index for JSThread info, returned by PR_NewThreadPrivateIndex.  The
@@ -289,16 +290,17 @@ js_NewContext(JSRuntime *rt, size_t stac
     /*
      * First we do the infallible, every-time per-context initializations.
      * Should a later, fallible initialization (js_InitRegExpStatics, e.g.,
      * or the stuff under 'if (first)' below) fail, at least the version
      * and arena-pools will be valid and safe to use (say, from the last GC
      * done by js_DestroyContext).
      */
     cx->version = JSVERSION_DEFAULT;
+    VOUCH_DOES_NOT_REQUIRE_STACK();
     JS_INIT_ARENA_POOL(&cx->stackPool, "stack", stackChunkSize, sizeof(jsval),
                        &cx->scriptStackQuota);
 
     JS_INIT_ARENA_POOL(&cx->tempPool, "temp",
                        1024,  /* FIXME: bug 421435 */
                        sizeof(jsdouble), &cx->scriptStackQuota);
 
     js_InitRegExpStatics(cx);
@@ -502,16 +504,17 @@ js_DestroyContext(JSContext *cx, JSDestr
         if (mode == JSDCM_FORCE_GC)
             js_GC(cx, GC_NORMAL);
         else if (mode == JSDCM_MAYBE_GC)
             JS_MaybeGC(cx);
     }
 
     /* Free the stuff hanging off of cx. */
     js_FreeRegExpStatics(cx);
+    VOUCH_DOES_NOT_REQUIRE_STACK();
     JS_FinishArenaPool(&cx->stackPool);
     JS_FinishArenaPool(&cx->tempPool);
 
     if (cx->lastMessage)
         free(cx->lastMessage);
 
     /* Remove any argument formatters. */
     map = cx->argumentFormatMap;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -884,16 +884,17 @@ struct JSContext {
 
     /* Quota on the size of arenas used to compile and execute scripts. */
     size_t              scriptStackQuota;
 
     /* Data shared by threads in an address space. */
     JSRuntime           *runtime;
 
     /* Stack arena pool and frame pointer register. */
+    JS_REQUIRES_STACK
     JSArenaPool         stackPool;
 
     JS_REQUIRES_STACK
     JSStackFrame        *fp;
 
     /* Temporary arena pool used while compiling and decompiling. */
     JSArenaPool         tempPool;
 
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -669,17 +669,17 @@ js_watch_set(JSContext *cx, JSObject *ob
             DBG_LOCK(rt);
             return DropWatchPointAndUnlock(cx, wp, JSWP_HELD) && ok;
         }
     }
     DBG_UNLOCK(rt);
     return JS_TRUE;
 }
 
-JSBool
+JS_REQUIRES_STACK JSBool
 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                      jsval *rval)
 {
     JSObject *funobj;
     JSFunction *wrapper;
     jsval userid;
 
     funobj = JSVAL_TO_OBJECT(argv[-2]);
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -115,20 +115,20 @@ js_FindWatchPoint(JSRuntime *rt, JSScope
 
 /*
  * NB: callers outside of jsdbgapi.c must pass non-null scope.
  */
 extern JSPropertyOp
 js_GetWatchedSetter(JSRuntime *rt, JSScope *scope,
                     const JSScopeProperty *sprop);
 
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_watch_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                      jsval *rval);
 
 extern JSPropertyOp
 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter);
 
 #endif /* JS_HAS_OBJ_WATCHPOINT */
 
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1582,17 +1582,17 @@ fun_toString(JSContext *cx, uintN argc, 
 #if JS_HAS_TOSOURCE
 static JSBool
 fun_toSource(JSContext *cx, uintN argc, jsval *vp)
 {
     return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp);
 }
 #endif
 
-JSBool
+JS_REQUIRES_STACK JSBool
 js_fun_call(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
     jsval fval, *argv, *invokevp;
     JSString *str;
     void *mark;
     JSBool ok;
 
@@ -1641,17 +1641,17 @@ js_fun_call(JSContext *cx, uintN argc, j
     memcpy(invokevp + 2, argv, argc * sizeof *argv);
 
     ok = js_Invoke(cx, argc, invokevp, 0);
     *vp = *invokevp;
     js_FreeStack(cx, mark);
     return ok;
 }
 
-JSBool
+JS_REQUIRES_STACK JSBool
 js_fun_apply(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj, *aobj;
     jsval fval, *invokevp, *sp;
     JSString *str;
     jsuint length;
     JSBool arraylike, ok;
     void *mark;
@@ -1732,17 +1732,17 @@ js_fun_apply(JSContext *cx, uintN argc, 
     ok = js_Invoke(cx, argc, invokevp, 0);
     *vp = *invokevp;
 out:
     js_FreeStack(cx, mark);
     return ok;
 }
 
 #ifdef NARCISSUS
-static JSBool
+static JS_REQUIRES_STACK JSBool
 fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *aobj;
     uintN length, i;
     void *mark;
     jsval *invokevp, *sp;
     JSBool ok;
 
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -279,18 +279,18 @@ js_GetLocalNameArray(JSContext *cx, JSFu
     ((JSAtom *) ((nameWord) & ~(jsuword) 1))
 
 #define JS_LOCAL_NAME_IS_CONST(nameWord)                                      \
     ((((nameWord) & (jsuword) 1)) != 0)
 
 extern void
 js_FreezeLocalNames(JSContext *cx, JSFunction *fun);
 
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_fun_apply(JSContext *cx, uintN argc, jsval *vp);
 
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_fun_call(JSContext *cx, uintN argc, jsval *vp);
 
 
 JS_END_EXTERN_C
 
 #endif /* jsfun_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -69,16 +69,17 @@
 #include "jsinterp.h"
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
+#include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jstracer.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 /*
@@ -2952,17 +2953,17 @@ TraceWeakRoots(JSTracer *trc, JSWeakRoot
         }
     }
 
     JS_CALL_VALUE_TRACER(trc, wr->lastAtom, "lastAtom");
     JS_SET_TRACING_NAME(trc, "lastInternalResult");
     js_CallValueTracerIfGCThing(trc, wr->lastInternalResult);
 }
 
-JS_FRIEND_API(void)
+JS_REQUIRES_STACK JS_FRIEND_API(void)
 js_TraceContext(JSTracer *trc, JSContext *acx)
 {
     JSStackFrame *fp, *nextChain;
     JSStackHeader *sh;
     JSTempValueRooter *tvr;
 
     if (IS_GC_MARKING_TRACER(trc)) {
 
@@ -3104,17 +3105,17 @@ js_TraceTraceMonitor(JSTracer *trc, JSTr
             uint8 *flagp = GetGCThingFlags(obj);
             JS_ASSERT((*flagp & GCF_TYPEMASK) == GCX_OBJECT);
             JS_ASSERT(*flagp != GCF_FINAL);
             *flagp |= GCF_MARK;
         }
     }
 }
 
-void
+JS_REQUIRES_STACK void
 js_TraceRuntime(JSTracer *trc, JSBool allAtoms)
 {
     JSRuntime *rt = trc->context->runtime;
     JSContext *iter, *acx;
 
     JS_DHashTableEnumerate(&rt->gcRootsHash, gc_root_traversal, trc);
     if (rt->gcLocksHash)
         JS_DHashTableEnumerate(rt->gcLocksHash, gc_lock_traversal, trc);
@@ -3451,16 +3452,17 @@ js_GC(JSContext *cx, JSGCInvocationKind 
         goto restart_at_beginning;
     }
 
     JS_UNLOCK_GC(rt);
 
 #ifdef JS_TRACER
     if (JS_ON_TRACE(cx))
         goto out;
+    VOUCH_HAVE_STACK();
 #endif
 
     /* Reset malloc counter. */
     rt->gcMallocBytes = 0;
 
 #ifdef JS_DUMP_SCOPE_METERS
   { extern void js_DumpScopeMeters(JSRuntime *rt);
     js_DumpScopeMeters(rt);
@@ -3773,17 +3775,18 @@ js_GC(JSContext *cx, JSGCInvocationKind 
 out:
 #endif
     JS_LOCK_GC(rt);
 
     /*
      * We want to restart GC if js_GC was called recursively or if any of the
      * finalizers called js_RemoveRoot or js_UnlockGCThingRT.
      */
-    if (rt->gcLevel > 1 || rt->gcPoke) {
+    if (!JS_ON_TRACE(cx) && (rt->gcLevel > 1 || rt->gcPoke)) {
+        VOUCH_HAVE_STACK();
         rt->gcLevel = 1;
         rt->gcPoke = JS_FALSE;
         JS_UNLOCK_GC(rt);
         goto restart;
     }
 
     if (rt->shapeGen >= SHAPE_OVERFLOW_BIT - 1) {
         /*
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -249,20 +249,20 @@ JS_STATIC_ASSERT(JSTRACE_STRING + 1 == J
  * type behind v.
  */
 extern void
 js_CallValueTracerIfGCThing(JSTracer *trc, jsval v);
 
 extern void
 js_TraceStackFrame(JSTracer *trc, JSStackFrame *fp);
 
-extern void
+extern JS_REQUIRES_STACK void
 js_TraceRuntime(JSTracer *trc, JSBool allAtoms);
 
-extern JS_FRIEND_API(void)
+extern JS_REQUIRES_STACK JS_FRIEND_API(void)
 js_TraceContext(JSTracer *trc, JSContext *acx);
 
 /*
  * Kinds of js_GC invocation.
  */
 typedef enum JSGCInvocationKind {
     /* Normal invocation. */
     GC_NORMAL           = 0,
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -528,17 +528,17 @@ js_EnablePropertyCache(JSContext *cx)
     --JS_PROPERTY_CACHE(cx).disabled;
     JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled >= 0);
 }
 
 /*
  * Check if the current arena has enough space to fit nslots after sp and, if
  * so, reserve the necessary space.
  */
-static JSBool
+static JS_REQUIRES_STACK JSBool
 AllocateAfterSP(JSContext *cx, jsval *sp, uintN nslots)
 {
     uintN surplus;
     jsval *sp2;
 
     JS_ASSERT((jsval *) cx->stackPool.current->base <= sp);
     JS_ASSERT(sp <= (jsval *) cx->stackPool.current->avail);
     surplus = (jsval *) cx->stackPool.current->avail - sp;
@@ -553,17 +553,17 @@ AllocateAfterSP(JSContext *cx, jsval *sp
         return JS_FALSE;
 
     JS_ARENA_ALLOCATE_CAST(sp2, jsval *, &cx->stackPool,
                            (nslots - surplus) * sizeof(jsval));
     JS_ASSERT(sp2 == sp + surplus);
     return JS_TRUE;
 }
 
-JS_STATIC_INTERPRET jsval *
+JS_STATIC_INTERPRET JS_REQUIRES_STACK jsval *
 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp)
 {
     jsval *sp;
 
     if (!cx->stackPool.first.next) {
         int64 *timestamp;
 
         JS_ARENA_ALLOCATE_CAST(timestamp, int64 *,
@@ -578,23 +578,23 @@ js_AllocRawStack(JSContext *cx, uintN ns
     if (markp)
         *markp = JS_ARENA_MARK(&cx->stackPool);
     JS_ARENA_ALLOCATE_CAST(sp, jsval *, &cx->stackPool, nslots * sizeof(jsval));
     if (!sp)
         js_ReportOutOfScriptQuota(cx);
     return sp;
 }
 
-JS_STATIC_INTERPRET void
+JS_STATIC_INTERPRET JS_REQUIRES_STACK void
 js_FreeRawStack(JSContext *cx, void *mark)
 {
     JS_ARENA_RELEASE(&cx->stackPool, mark);
 }
 
-JS_FRIEND_API(jsval *)
+JS_REQUIRES_STACK JS_FRIEND_API(jsval *)
 js_AllocStack(JSContext *cx, uintN nslots, void **markp)
 {
     jsval *sp;
     JSArena *a;
     JSStackHeader *sh;
 
     /* Callers don't check for zero nslots: we do to avoid empty segments. */
     if (nslots == 0) {
@@ -630,17 +630,17 @@ js_AllocStack(JSContext *cx, uintN nslot
      * Store JSVAL_NULL using memset, to let compilers optimize as they see
      * fit, in case a caller allocates and pushes GC-things one by one, which
      * could nest a last-ditch GC that will scan this segment.
      */
     memset(sp, 0, nslots * sizeof(jsval));
     return sp;
 }
 
-JS_FRIEND_API(void)
+JS_REQUIRES_STACK JS_FRIEND_API(void)
 js_FreeStack(JSContext *cx, void *mark)
 {
     JSStackHeader *sh;
     jsuword slotdiff;
 
     /* Check for zero nslots allocation special case. */
     if (!mark)
         return;
@@ -983,17 +983,17 @@ js_OnUnknownMethod(JSContext *cx, jsval 
     }
     ok = JS_TRUE;
 
   out:
     JS_POP_TEMP_ROOT(cx, &tvr);
     return ok;
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 NoSuchMethod(JSContext *cx, uintN argc, jsval *vp, uint32 flags)
 {
     jsval *invokevp;
     void *mark;
     JSBool ok;
     JSObject *obj, *argsobj;
 
     invokevp = js_AllocStack(cx, 2 + 2, &mark);
@@ -1044,17 +1044,17 @@ const uint16 js_PrimitiveTestFlags[] = {
 };
 
 /*
  * Find a function reference and its 'this' object implicit first parameter
  * under argc arguments on cx's stack, and call the function.  Push missing
  * required arguments, allocate declared local variables, and pop everything
  * when done.  Then push the return value.
  */
-JS_FRIEND_API(JSBool)
+JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
 js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags)
 {
     void *mark;
     JSStackFrame frame;
     jsval *sp, *argv, *newvp;
     jsval v;
     JSObject *funobj, *parent;
     JSBool ok;
@@ -1260,17 +1260,17 @@ have_fun:
     frame.script = script;
     frame.callee = funobj;
     frame.fun = fun;
     frame.argc = argc;
     frame.argv = argv;
 
     /* Default return value for a constructor is the new object. */
     frame.rval = (flags & JSINVOKE_CONSTRUCT) ? vp[1] : JSVAL_VOID;
-    frame.down = js_GetTopStackFrame(cx);
+    frame.down = cx->fp;
     frame.annotation = NULL;
     frame.scopeChain = NULL;    /* set below for real, after cx->fp is set */
     frame.regs = NULL;
     frame.imacpc = NULL;
     frame.slots = NULL;
     frame.sharpDepth = 0;
     frame.sharpArray = NULL;
     frame.flags = flags | rootedArgsFlag;
@@ -1367,17 +1367,17 @@ out2:
     return ok;
 
 bad:
     js_ReportIsNotFunction(cx, vp, flags & JSINVOKE_FUNFLAGS);
     ok = JS_FALSE;
     goto out2;
 }
 
-JSBool
+JS_REQUIRES_STACK JSBool
 js_InternalInvoke(JSContext *cx, JSObject *obj, jsval fval, uintN flags,
                   uintN argc, jsval *argv, jsval *rval)
 {
     jsval *invokevp;
     void *mark;
     JSBool ok;
 
     invokevp = js_AllocStack(cx, 2 + argc, &mark);
@@ -1413,16 +1413,18 @@ js_InternalInvoke(JSContext *cx, JSObjec
 }
 
 JSBool
 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
                     JSAccessMode mode, uintN argc, jsval *argv, jsval *rval)
 {
     JSSecurityCallbacks *callbacks;
 
+    js_LeaveTrace(cx);
+
     /*
      * js_InternalInvoke could result in another try to get or set the same id
      * again, see bug 355497.
      */
     JS_CHECK_RECURSION(cx, return JS_FALSE);
 
     /*
      * Check general (not object-ops/class-specific) access from the running
@@ -1726,17 +1728,17 @@ js_StrictlyEqual(JSContext *cx, jsval lv
     if (JSVAL_IS_INT(lval) && rtag == JSVAL_DOUBLE) {
         ld = JSVAL_TO_INT(lval);
         rd = *JSVAL_TO_DOUBLE(rval);
         return JSDOUBLE_COMPARE(ld, ==, rd, JS_FALSE);
     }
     return lval == rval;
 }
 
-JSBool
+JS_REQUIRES_STACK JSBool
 js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp)
 {
     JSFunction *fun, *fun2;
     JSObject *obj, *obj2, *proto, *parent;
     jsval lval, rval;
     JSClass *clasp;
 
     fun = NULL;
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -363,20 +363,20 @@ extern void
 js_DisablePropertyCache(JSContext *cx);
 
 extern void
 js_EnablePropertyCache(JSContext *cx);
 
 /*
  * Interpreter stack arena-pool alloc and free functions.
  */
-extern JS_FRIEND_API(jsval *)
+extern JS_REQUIRES_STACK JS_FRIEND_API(jsval *)
 js_AllocStack(JSContext *cx, uintN nslots, void **markp);
 
-extern JS_FRIEND_API(void)
+extern JS_REQUIRES_STACK JS_FRIEND_API(void)
 js_FreeStack(JSContext *cx, void *mark);
 
 /*
  * Refresh and return fp->scopeChain.  It may be stale if block scopes are
  * active but not yet reflected by objects in the scope chain.  If a block
  * scope contains a with, eval, XML filtering predicate, or similar such
  * dynamically scoped construct, then compile-time block scope at fp->blocks
  * must reflect at runtime.
@@ -417,17 +417,17 @@ extern const uint16 js_PrimitiveTestFlag
  * NB: js_Invoke requires that cx is currently running JS (i.e., that cx->fp
  * is non-null), and that vp points to the callee, |this| parameter, and
  * actual arguments of the call. [vp .. vp + 2 + argc) must belong to the last
  * JS stack segment that js_AllocStack allocated. The function may use the
  * space available after vp + 2 + argc in the stack segment for temporaries,
  * so the caller should not use that space for values that must be preserved
  * across the call.
  */
-extern JS_FRIEND_API(JSBool)
+extern JS_REQUIRES_STACK JS_FRIEND_API(JSBool)
 js_Invoke(JSContext *cx, uintN argc, jsval *vp, uintN flags);
 
 /*
  * Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
  * we can share bits stored in JSStackFrame.flags and passed to:
  *
  *   js_Invoke
  *   js_InternalInvoke
@@ -463,17 +463,17 @@ js_InternalInvoke(JSContext *cx, JSObjec
 extern JSBool
 js_InternalGetOrSet(JSContext *cx, JSObject *obj, jsid id, jsval fval,
                     JSAccessMode mode, uintN argc, jsval *argv, jsval *rval);
 
 extern JSBool
 js_Execute(JSContext *cx, JSObject *chain, JSScript *script,
            JSStackFrame *down, uintN flags, jsval *result);
 
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_InvokeConstructor(JSContext *cx, uintN argc, JSBool clampReturn, jsval *vp);
 
 extern JS_REQUIRES_STACK JSBool
 js_Interpret(JSContext *cx);
 
 #define JSPROP_INITIALIZER 0x100   /* NB: Not a valid property attribute. */
 
 extern JSBool
@@ -502,20 +502,20 @@ js_StrictlyEqual(JSContext *cx, jsval lv
 # endif
 #endif
 
 #if !JS_LONE_INTERPRET
 # define JS_STATIC_INTERPRET    static
 #else
 # define JS_STATIC_INTERPRET
 
-extern jsval *
+extern JS_REQUIRES_STACK jsval *
 js_AllocRawStack(JSContext *cx, uintN nslots, void **markp);
 
-extern void
+extern JS_REQUIRES_STACK void
 js_FreeRawStack(JSContext *cx, void *mark);
 
 /*
  * ECMA requires "the global object", but in embeddings such as the browser,
  * which have multiple top-level objects (windows, frames, etc. in the DOM),
  * we prefer fun's parent.  An example that causes this code to run:
  *
  *   // in window w1
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -59,28 +59,30 @@
 #include "jsiter.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
+#include "jsstaticcheck.h"
+#include "jstracer.h"
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
 
 #if JSSLOT_ITER_FLAGS >= JS_INITIAL_NSLOTS
 #error JS_INITIAL_NSLOTS must be greater than JSSLOT_ITER_FLAGS.
 #endif
 
 #if JS_HAS_GENERATORS
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 CloseGenerator(JSContext *cx, JSObject *genobj);
 
 #endif
 
 /*
  * Shared code to close iterator's state either through an explicit call or
  * when GC detects that the iterator is no longer reachable.
  */
@@ -402,16 +404,17 @@ js_ValueToIterator(JSContext *cx, uintN 
                 goto bad;
 
             /* Store in *vp to protect it from GC (callers must root vp). */
             *vp = OBJECT_TO_JSVAL(iterobj);
 
             if (!InitNativeIterator(cx, iterobj, obj, flags))
                 goto bad;
         } else {
+            js_LeaveTrace(cx);
             arg = BOOLEAN_TO_JSVAL((flags & JSITER_FOREACH) == 0);
             if (!js_InternalInvoke(cx, obj, *vp, JSINVOKE_ITERATOR, 1, &arg,
                                    vp)) {
                 goto bad;
             }
             if (JSVAL_IS_PRIMITIVE(*vp)) {
                 const char *printable = js_AtomToPrintableString(cx, atom);
                 if (printable) {
@@ -444,16 +447,17 @@ js_CloseIterator(JSContext *cx, jsval v)
     obj = JSVAL_TO_OBJECT(v);
     clasp = OBJ_GET_CLASS(cx, obj);
 
     if (clasp == &js_IteratorClass) {
         js_CloseNativeIterator(cx, obj);
     }
 #if JS_HAS_GENERATORS
     else if (clasp == &js_GeneratorClass) {
+        JS_ASSERT_NOT_ON_TRACE(cx);
         if (!CloseGenerator(cx, obj))
             return JS_FALSE;
     }
 #endif
     return JS_TRUE;
 }
 
 static JSBool
@@ -809,17 +813,17 @@ typedef enum JSGeneratorOp {
     JSGENOP_THROW,
     JSGENOP_CLOSE
 } JSGeneratorOp;
 
 /*
  * Start newborn or restart yielding generator and perform the requested
  * operation inside its frame.
  */
-static JSBool
+static JS_REQUIRES_STACK JSBool
 SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
                 JSGenerator *gen, jsval arg)
 {
     JSStackFrame *fp;
     JSArena *arena;
     JSBool ok;
 
     if (gen->state == JSGEN_RUNNING || gen->state == JSGEN_CLOSING) {
@@ -899,17 +903,17 @@ SendToGenerator(JSContext *cx, JSGenerat
 
     /*
      * An error, silent termination by operation callback or an exception.
      * Propagate the condition to the caller.
      */
     return JS_FALSE;
 }
 
-static JSBool
+static JS_REQUIRES_STACK JSBool
 CloseGenerator(JSContext *cx, JSObject *obj)
 {
     JSGenerator *gen;
 
     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_GeneratorClass);
     gen = (JSGenerator *) JS_GetPrivate(cx, obj);
     if (!gen) {
         /* Generator prototype object. */
@@ -927,16 +931,18 @@ CloseGenerator(JSContext *cx, JSObject *
  */
 static JSBool
 generator_op(JSContext *cx, JSGeneratorOp op, jsval *vp, uintN argc)
 {
     JSObject *obj;
     JSGenerator *gen;
     jsval arg;
 
+    js_LeaveTrace(cx);
+
     obj = JS_THIS_OBJECT(cx, vp);
     if (!JS_InstanceOf(cx, obj, &js_GeneratorClass, vp + 2))
         return JS_FALSE;
 
     gen = (JSGenerator *) JS_GetPrivate(cx, obj);
     if (gen == NULL) {
         /* This happens when obj is the generator prototype. See bug 352885. */
         goto closed_generator;
@@ -971,16 +977,18 @@ generator_op(JSContext *cx, JSGeneratorO
             JS_SetPendingException(cx, argc >= 1 ? vp[2] : JSVAL_VOID);
             return JS_FALSE;
           default:
             JS_ASSERT(op == JSGENOP_CLOSE);
             return JS_TRUE;
         }
     }
 
+    js_LeaveTrace(cx);
+
     arg = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && argc != 0)
           ? vp[2]
           : JSVAL_VOID;
     if (!SendToGenerator(cx, op, obj, gen, arg))
         return JS_FALSE;
     *vp = gen->frame.rval;
     return JS_TRUE;
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -64,19 +64,20 @@
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsparse.h"
 #include "jsscope.h"
 #include "jsscript.h"
+#include "jsstaticcheck.h"
 #include "jsstr.h"
+#include "jstracer.h"
 #include "jsdbgapi.h"   /* whether or not JS_HAS_OBJ_WATCHPOINT */
-#include "jsstaticcheck.h"
 
 #if JS_HAS_GENERATORS
 #include "jsiter.h"
 #endif
 
 #if JS_HAS_XML_SUPPORT
 #include "jsxml.h"
 #endif
@@ -3978,17 +3979,17 @@ js_GetPropertyHelper(JSContext *cx, JSOb
         return OBJ_GET_PROPERTY(cx, obj2, id, vp);
     }
 
     sprop = (JSScopeProperty *) prop;
     if (!js_NativeGet(cx, obj, obj2, sprop, vp))
         return JS_FALSE;
 
     if (entryp) {
-        JS_ASSERT_NOT_EXECUTING_TRACE(cx);
+        JS_ASSERT_NOT_ON_TRACE(cx);
         js_FillPropertyCache(cx, obj, shape, 0, protoIndex, obj2, sprop, entryp);
     }
     JS_UNLOCK_OBJ(cx, obj2);
     return JS_TRUE;
 }
 
 JSBool
 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
@@ -4184,17 +4185,17 @@ js_SetPropertyHelper(JSContext *cx, JSOb
                             JS_UNLOCK_SCOPE(cx, scope);
                             return JS_FALSE);
     }
 
     if (!js_NativeSet(cx, obj, sprop, vp))
         return JS_FALSE;
 
     if (entryp) {
-        JS_ASSERT_NOT_EXECUTING_TRACE(cx);
+        JS_ASSERT_NOT_ON_TRACE(cx);
         if (!(attrs & JSPROP_SHARED))
             js_FillPropertyCache(cx, obj, shape, 0, 0, obj, sprop, entryp);
         else
             PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
     }
     JS_UNLOCK_SCOPE(cx, scope);
     return JS_TRUE;
 
@@ -5125,18 +5126,19 @@ js_TryMethod(JSContext *cx, JSObject *ob
 #endif
     {
         ok = OBJ_GET_PROPERTY(cx, obj, id, &fval);
     }
     if (!ok)
         JS_ClearPendingException(cx);
     JS_SetErrorReporter(cx, older);
 
-    return JSVAL_IS_PRIMITIVE(fval) ||
-           js_InternalCall(cx, obj, fval, argc, argv, rval);
+    if (JSVAL_IS_PRIMITIVE(fval))
+        return JS_TRUE;
+    return js_InternalCall(cx, obj, fval, argc, argv, rval);
 }
 
 #if JS_HAS_XDR
 
 JSBool
 js_XDRObject(JSXDRState *xdr, JSObject **objp)
 {
     JSContext *cx;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -4105,16 +4105,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                             table[j].order = j;
                             j++;
                         }
                         pc2 += jmplen;
                     }
                     tmp = (TableEntry *)
                           JS_malloc(cx, (size_t)j * sizeof *table);
                     if (tmp) {
+                        VOUCH_DOES_NOT_REQUIRE_STACK();
                         ok = js_MergeSort(table, (size_t)j, sizeof(TableEntry),
                                           CompareOffsets, NULL, tmp);
                         JS_free(cx, tmp);
                     } else {
                         ok = JS_FALSE;
                     }
                 }
 
--- a/js/src/jsstaticcheck.h
+++ b/js/src/jsstaticcheck.h
@@ -48,21 +48,22 @@ inline __attribute__ ((unused)) void MUS
 }
 
 /* avoid unused goto-label warnings */
 #define MUST_FLOW_LABEL(label) goto label; label:
 
 inline JS_FORCES_STACK void VOUCH_DOES_NOT_REQUIRE_STACK() {}
 
 inline JS_FORCES_STACK void
-JS_ASSERT_NOT_EXECUTING_TRACE(JSContext *cx)
+JS_ASSERT_NOT_ON_TRACE(JSContext *cx)
 {
     JS_ASSERT(!JS_ON_TRACE(cx));
 }
 
 #else
 #define MUST_FLOW_THROUGH(label)            ((void) 0)
 #define MUST_FLOW_LABEL(label)
 #define VOUCH_DOES_NOT_REQUIRE_STACK()      ((void) 0)
-#define JS_ASSERT_NOT_EXECUTING_TRACE(cx)   JS_ASSERT(!JS_ON_TRACE(cx))
+#define JS_ASSERT_NOT_ON_TRACE(cx)          JS_ASSERT(!JS_ON_TRACE(cx))
 #endif
+#define VOUCH_HAVE_STACK                    VOUCH_DOES_NOT_REQUIRE_STACK
 
 #endif /* jsstaticcheck_h___ */
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -65,16 +65,17 @@
 #include "jsgc.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsregexp.h"
 #include "jsscope.h"
+#include "jsstaticcheck.h"
 #include "jsstr.h"
 #include "jsbit.h"
 
 #define JSSTRDEP_RECURSION_LIMIT        100
 
 size_t
 js_MinimizeDependentStrings(JSString *str, int level, JSString **basep)
 {
@@ -1575,16 +1576,18 @@ find_replen(JSContext *cx, ReplaceData *
 
     lambda = rdata->lambda;
     if (lambda) {
         uintN argc, i, j, m, n, p;
         jsval *invokevp, *sp;
         void *mark;
         JSBool ok;
 
+        JS_ASSERT_NOT_ON_TRACE(cx);
+
         /*
          * Save the regExpStatics from the current regexp, since they may be
          * clobbered by a RegExp usage in the lambda function.  Note that all
          * members of JSRegExpStatics are JSSubStrings, so not GC roots, save
          * input, which is rooted otherwise via vp[1] in str_replace.
          */
         JSRegExpStatics save = cx->regExpStatics;
         JSBool freeMoreParens = JS_FALSE;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -2948,17 +2948,17 @@ nanojit::LirNameMap::formatGuard(LIns *i
 #endif
 
 void
 nanojit::Fragment::onDestroy()
 {
     delete (TreeInfo *)vmprivate;
 }
 
-bool
+static JS_REQUIRES_STACK bool
 js_DeleteRecorder(JSContext* cx)
 {
     JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
 
     /* Aborting and completing a trace end up here. */
     delete tm->recorder;
     tm->recorder = NULL;
 
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -595,9 +595,16 @@ js_GetBuiltinFunction(JSContext *cx, uin
 #else  /* !JS_TRACER */
 
 #define TRACE_0(x)              ((void)0)
 #define TRACE_1(x,a)            ((void)0)
 #define TRACE_2(x,a,b)          ((void)0)
 
 #endif /* !JS_TRACER */
 
+static JS_INLINE JS_FORCES_STACK void
+js_LeaveTrace(JSContext *cx)
+{
+    if (JS_ON_TRACE(cx))
+        js_GetTopStackFrame(cx);
+}
+
 #endif /* jstracer_h___ */