Merge TM -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 06 Jun 2011 09:48:04 -0700
changeset 75914 4f8b85723213489d3cf8699dddad477347ffc316
parent 75913 a4492fe5a236a99a606c9143d3e37a471b305fea (current diff)
parent 71180 7e6f3b1796441e3cb3a2ae87d43bf793a46580a3 (diff)
child 75915 334428e1d5aac565a771d1bbb1a785797fd89f81
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge TM -> JM
js/src/Makefile.in
js/src/jit-test/tests/basic/testStackQuotaExhausted.js
js/src/jsanalyze.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarena.h
js/src/jsatom.cpp
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jsparse.cpp
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstracer.cpp
js/src/jstracer.h
js/src/jsxml.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/Retcon.cpp
js/src/shell/js.cpp
js/src/tests/e4x/XML/jstests.list
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
js/src/xpconnect/src/nsXPConnect.cpp
js/src/xpconnect/src/xpcjsruntime.cpp
js/src/xpconnect/src/xpcprivate.h
--- a/content/base/src/nsInProcessTabChildGlobal.cpp
+++ b/content/base/src/nsInProcessTabChildGlobal.cpp
@@ -290,17 +290,16 @@ nsInProcessTabChildGlobal::InitTabChildG
   NS_ENSURE_STATE(cx);
 
   mCx = cx;
 
   nsContentUtils::XPConnect()->SetSecurityManagerForJSContext(cx, nsContentUtils::GetSecurityManager(), 0);
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
-  JS_SetScriptStackQuota(cx, 25 * sizeof(size_t) * 1024 * 1024);
 
   JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_JIT | JSOPTION_ANONFUNFIX | JSOPTION_PRIVATE_IS_NSISUPPORTS);
   JS_SetVersion(cx, JSVERSION_LATEST);
   JS_SetErrorReporter(cx, ContentScriptErrorReporter);
 
   xpc_LocalizeContext(cx);
 
   JSAutoRequest ar(cx);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -839,17 +839,16 @@ TabChild::InitTabChildGlobal()
   NS_ENSURE_TRUE(cx, false);
 
   mCx = cx;
 
   nsContentUtils::XPConnect()->SetSecurityManagerForJSContext(cx, nsContentUtils::GetSecurityManager(), 0);
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
   JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
-  JS_SetScriptStackQuota(cx, 25 * sizeof(size_t) * 1024 * 1024);
 
   JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_JIT | JSOPTION_ANONFUNFIX | JSOPTION_PRIVATE_IS_NSISUPPORTS);
   JS_SetVersion(cx, JSVERSION_LATEST);
   JS_SetErrorReporter(cx, ContentScriptErrorReporter);
 
   xpc_LocalizeContext(cx);
 
   JSAutoRequest ar(cx);
--- a/dom/src/threads/nsDOMThreadService.cpp
+++ b/dom/src/threads/nsDOMThreadService.cpp
@@ -668,18 +668,17 @@ DOMWorkerErrorReporter(JSContext* aCx,
                                       "DOM Worker javascript",
                                       worker->Pool()->WindowID());
 
   if (NS_FAILED(rv)) {
     return;
   }
 
   // Don't call the error handler if we're out of stack space.
-  if (errorNumber != JSMSG_SCRIPT_STACK_QUOTA &&
-      errorNumber != JSMSG_OVER_RECURSED) {
+  if (errorNumber != JSMSG_OVER_RECURSED) {
     // Try the onerror handler for the worker's scope.
     nsRefPtr<nsDOMWorkerScope> scope = worker->GetInnerScope();
     NS_ASSERTION(scope, "Null scope!");
 
     PRBool hasListeners = scope->HasListeners(NS_LITERAL_STRING("error"));
     if (hasListeners) {
       nsRefPtr<nsDOMWorkerErrorEvent> event(new nsDOMWorkerErrorEvent());
       if (event) {
@@ -1066,17 +1065,16 @@ nsDOMThreadService::CreateJSContext()
 
   JS_ClearContextDebugHooks(cx);
 
   nsresult rv = nsContentUtils::XPConnect()->
     SetSecurityManagerForJSContext(cx, gWorkerSecurityManager, 0);
   NS_ENSURE_SUCCESS(rv, nsnull);
 
   JS_SetNativeStackQuota(cx, 256*1024);
-  JS_SetScriptStackQuota(cx, 100*1024*1024);
 
   JS_SetOptions(cx,
     JS_GetOptions(cx) | JSOPTION_METHODJIT | JSOPTION_JIT |
     JSOPTION_PROFILING | JSOPTION_ANONFUNFIX);
   JS_SetGCParameterForThread(cx, JSGC_MAX_CODE_CACHE_BYTES, 1 * 1024 * 1024);
 
   return cx.forget();
 }
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -1014,20 +1014,18 @@ jsdScript::jsdScript (JSDContext *aCx, J
         
         mValid = PR_TRUE;
     }
 }
 
 jsdScript::~jsdScript () 
 {
     DEBUG_DESTROY ("jsdScript", gScriptCount);
-    if (mFileName)
-        delete mFileName;
-    if (mFunctionName)
-        delete mFunctionName;
+    delete mFileName;
+    delete mFunctionName;
 
     if (mPPLineMap)
         PR_Free(mPPLineMap);
 
     /* Invalidate() needs to be called to release an owning reference to
      * ourselves, so if we got here without being invalidated, something
      * has gone wrong with our ref count. */
     NS_ASSERTION (!mValid, "Script destroyed without being invalidated.");
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -653,17 +653,17 @@ check-malloc-function-usage: $(filter-ou
 		"in Makefile.in" "cx->calloc_ or rt->calloc_" $^
 	$(srcdir)/config/check_source_count.py "\bjs_realloc\b" 0 \
 		"in Makefile.in" "cx->realloc_ or rt->realloc_" $^
 	$(srcdir)/config/check_source_count.py "\bjs_free\b" 0 \
 		"in Makefile.in" "cx->free_" $^
 
 	# We desire these numbers to go down, not up. See "User guide to memory
 	# management within SpiderMonkey" in jsutil.h.
-	$(srcdir)/config/check_source_count.py OffTheBooks:: 55 \
+	$(srcdir)/config/check_source_count.py OffTheBooks:: 53 \
 		"in Makefile.in" "{cx,rt}->{new_,new_array,malloc_,calloc_,realloc_}" $^
 	# This should go to zero, if possible.
 	$(srcdir)/config/check_source_count.py UnwantedForeground:: 36 \
 		"in Makefile.in" "{cx,rt}->{free_,delete_,array_delete}" $^
 
 ifneq ($(OS_ARCH),WINNT) # FIXME: this should be made work on Windows too.
 #check:: check-malloc-function-usage FIXME: disable on JM until closer to merge time.
 endif
--- a/js/src/jit-test/tests/basic/bug522136.js
+++ b/js/src/jit-test/tests/basic/bug522136.js
@@ -1,10 +1,11 @@
 var Q = 0;
+var thrown = false;
 try {
-   (function f(i) { Q = i; if (i == 100000) return; f(i+1); })(1)
+   (function f(i) { Q = i; if (i == 200000) return; f(i+1); })(1)
 } catch (e) {
+    thrown = true;
 }
 
 // Exact behavior of recursion check depends on which JIT we use.
-var ok = (Q == 3000 || Q == 3001);
-assertEq(ok, true);
+assertEq(thrown && Q > 10000, true);
 
--- a/js/src/jit-test/tests/basic/bug557168-1.js
+++ b/js/src/jit-test/tests/basic/bug557168-1.js
@@ -1,11 +1,11 @@
 x = <x/>
 try {
   Function("\
     (function f() {\
       ({x:{b}}=x);\
-      f()\
+      f.apply(null, new Array(100))\
     })()\
   ")()
 } catch (e) {
   assertEq(e.message, "too much recursion");
 }
--- a/js/src/jit-test/tests/basic/bug557168-2.js
+++ b/js/src/jit-test/tests/basic/bug557168-2.js
@@ -1,11 +1,11 @@
 x = <x/>
 try {
   Function("\
     (function f() {\
       ({x}=x);\
-      f()\
+      f.apply(null, new Array(100))\
     })()\
   ")()
 } catch (e) {
   assertEq(e.message, "too much recursion");
 }
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/test-apply-many-args.js
@@ -0,0 +1,14 @@
+function f(x) {
+    if (x == 0)
+        return;
+    arguments[0]--;
+    f.apply(null, arguments);
+}
+
+// When the apply-optimization isn't on, each recursive call chews up the C
+// stack, so don't push it.
+a = [20];
+
+for (var i = 0; i < 2000; ++i)
+  a.push(i);
+f.apply(null, a);
deleted file mode 100644
--- a/js/src/jit-test/tests/basic/testStackQuotaExhausted.js
+++ /dev/null
@@ -1,29 +0,0 @@
-const numFatArgs = Math.pow(2,19) - 1024;
-
-function fun(x) {
-    if (x <= 0)
-        return 0;
-    return fun(x-1);
-}
-
-function fatStack() {
-    return fun(10000);
-}
-
-function assertRightFailure(e) {
-    assertEq(e.toString() == "InternalError: script stack space quota is exhausted" ||
-             e.toString() == "InternalError: too much recursion",
-	     true);
-}
-
-exception = false;
-try {
-    fatStack.apply(null, new Array(numFatArgs));
-} catch (e) {
-    assertRightFailure(e);
-    exception = true;
-}
-assertEq(exception, true);
-
-// No more trace recursion w/ JM
-checkStats({traceCompleted:0});
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -98,17 +98,17 @@ MSG_DEF(JSMSG_CANT_WATCH,              1
 MSG_DEF(JSMSG_STACK_UNDERFLOW,         16, 2, JSEXN_INTERNALERR, "internal error compiling {0}: stack underflow at pc {1}")
 MSG_DEF(JSMSG_NEED_DIET,               17, 1, JSEXN_INTERNALERR, "{0} too large")
 MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS,    18, 0, JSEXN_ERR, "out of local root space")
 MSG_DEF(JSMSG_READ_ONLY,               19, 1, JSEXN_TYPEERR, "{0} is read-only")
 MSG_DEF(JSMSG_BAD_FORMAL,              20, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
 MSG_DEF(JSMSG_CANT_DELETE,             21, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
 MSG_DEF(JSMSG_NOT_FUNCTION,            22, 1, JSEXN_TYPEERR, "{0} is not a function")
 MSG_DEF(JSMSG_NOT_CONSTRUCTOR,         23, 1, JSEXN_TYPEERR, "{0} is not a constructor")
-MSG_DEF(JSMSG_SCRIPT_STACK_QUOTA,      24, 0, JSEXN_INTERNALERR, "script stack space quota is exhausted")
+MSG_DEF(JSMSG_INVALID_DATE,            24, 0, JSEXN_RANGEERR, "invalid date")
 MSG_DEF(JSMSG_TOO_DEEP,                25, 1, JSEXN_INTERNALERR, "{0} nested too deeply")
 MSG_DEF(JSMSG_OVER_RECURSED,           26, 0, JSEXN_INTERNALERR, "too much recursion")
 MSG_DEF(JSMSG_IN_NOT_OBJECT,           27, 1, JSEXN_TYPEERR, "invalid 'in' operand {0}")
 MSG_DEF(JSMSG_BAD_NEW_RESULT,          28, 1, JSEXN_TYPEERR, "invalid new expression result {0}")
 MSG_DEF(JSMSG_BAD_SHARP_DEF,           29, 1, JSEXN_ERR, "invalid sharp variable definition #{0}=")
 MSG_DEF(JSMSG_BAD_SHARP_USE,           30, 1, JSEXN_ERR, "invalid sharp variable use #{0}#")
 MSG_DEF(JSMSG_BAD_INSTANCEOF_RHS,      31, 1, JSEXN_TYPEERR, "invalid 'instanceof' operand {0}")
 MSG_DEF(JSMSG_BAD_BYTECODE,            32, 1, JSEXN_INTERNALERR, "unimplemented JavaScript bytecode {0}")
@@ -344,9 +344,9 @@ MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE,    26
 MSG_DEF(JSMSG_SC_RECURSION,           262, 0, JSEXN_INTERNALERR, "recursive object")
 MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT,   263, 0, JSEXN_TYPEERR, "can't wrap XML objects")
 MSG_DEF(JSMSG_BAD_CLONE_VERSION,      264, 0, JSEXN_ERR, "unsupported structured clone version")
 MSG_DEF(JSMSG_CANT_CLONE_OBJECT,      265, 0, JSEXN_TYPEERR, "can't clone object")
 MSG_DEF(JSMSG_NON_NATIVE_SCOPE,       266, 0, JSEXN_TYPEERR, "non-native scope object")
 MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
 MSG_DEF(JSMSG_INVALID_FOR_IN_INIT,    268, 0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
 MSG_DEF(JSMSG_CLEARED_SCOPE,          269, 0, JSEXN_TYPEERR, "attempt to run compile-and-go script on a cleared scope")
-MSG_DEF(JSMSG_INVALID_DATE,           270, 0, JSEXN_RANGEERR, "invalid date")
+
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2599,22 +2599,24 @@ JS_CompartmentGC(JSContext *cx, JSCompar
     JS_ASSERT(comp != cx->runtime->atomsCompartment);
 
     LeaveTrace(cx);
 
     /* Don't nuke active arenas if executing or compiling. */
     if (cx->tempPool.current == &cx->tempPool.first)
         JS_FinishArenaPool(&cx->tempPool);
 
+    GCREASON(PUBLIC_API);
     js_GC(cx, comp, GC_NORMAL);
 }
 
 JS_PUBLIC_API(void)
 JS_GC(JSContext *cx)
 {
+    GCREASON(PUBLIC_API);
     JS_CompartmentGC(cx, NULL);
 }
 
 JS_PUBLIC_API(void)
 JS_MaybeGC(JSContext *cx)
 {
     LeaveTrace(cx);
 
@@ -2794,22 +2796,16 @@ JS_SetNativeStackQuota(JSContext *cx, si
     } else {
         jsuword stackBase = reinterpret_cast<jsuword>(JS_THREAD_DATA(cx)->nativeStackBase);
         JS_ASSERT(stackBase >= stackSize);
         cx->stackLimit = stackBase - (stackSize - 1);
     }
 #endif
 }
 
-JS_PUBLIC_API(void)
-JS_SetScriptStackQuota(JSContext *cx, size_t quota)
-{
-    cx->scriptStackQuota = quota;
-}
-
 /************************************************************************/
 
 JS_PUBLIC_API(void)
 JS_DestroyIdArray(JSContext *cx, JSIdArray *ida)
 {
     cx->free_(ida);
 }
 
@@ -6245,17 +6241,17 @@ JS_ClearContextThread(JSContext *cx)
 }
 
 #ifdef JS_GC_ZEAL
 JS_PUBLIC_API(void)
 JS_SetGCZeal(JSContext *cx, uint8 zeal, uint32 frequency, JSBool compartment)
 {
     cx->runtime->gcZeal_ = zeal;
     cx->runtime->gcZealFrequency = frequency;
-    cx->runtime->gcNextScheduled = frequency;
+    cx->runtime->gcNextScheduled = zeal >= 2 ? frequency : 0;
     cx->runtime->gcDebugCompartmentGC = !!compartment;
 }
 
 JS_PUBLIC_API(void)
 JS_ScheduleGC(JSContext *cx, uint32 count, JSBool compartment)
 {
     cx->runtime->gcNextScheduled = count;
     cx->runtime->gcDebugCompartmentGC = !!compartment;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1914,32 +1914,16 @@ JS_SetThreadStackLimit(JSContext *cx, js
 
 /*
  * Set the size of the native stack that should not be exceed. To disable
  * stack size checking pass 0.
  */
 extern JS_PUBLIC_API(void)
 JS_SetNativeStackQuota(JSContext *cx, size_t stackSize);
 
-
-/*
- * Set the quota on the number of bytes that stack-like data structures can
- * use when the runtime compiles and executes scripts. These structures
- * consume heap space, so JS_SetThreadStackLimit does not bound their size.
- * The default quota is 128MB which is very generous.
- *
- * The function must be called before any script compilation or execution API
- * calls, i.e. either immediately after JS_NewContext or from JSCONTEXT_NEW
- * context callback.
- */
-extern JS_PUBLIC_API(void)
-JS_SetScriptStackQuota(JSContext *cx, size_t quota);
-
-#define JS_DEFAULT_SCRIPT_STACK_QUOTA   ((size_t) 0x8000000)
-
 /************************************************************************/
 
 /*
  * Classes, objects, and properties.
  */
 typedef void (*JSClassInternal)();
 
 /* For detailed comments on the function pointer types, see jspubtd.h. */
--- a/js/src/jsarena.cpp
+++ b/js/src/jsarena.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  *
  * ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -60,27 +60,26 @@ static JSArenaStats *arena_stats_list;
 #else
 #define COUNT(pool,what)  /* nothing */
 #endif
 
 #define JS_ARENA_DEFAULT_ALIGN  sizeof(double)
 
 JS_PUBLIC_API(void)
 JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size,
-                 size_t align, size_t *quotap)
+                 size_t align)
 {
     if (align == 0)
         align = JS_ARENA_DEFAULT_ALIGN;
     pool->mask = JS_BITMASK(JS_CeilingLog2(align));
     pool->first.next = NULL;
     pool->first.base = pool->first.avail = pool->first.limit =
         JS_ARENA_ALIGN(pool, &pool->first + 1);
     pool->current = &pool->first;
     pool->arenasize = size;
-    pool->quotap = quotap;
 #ifdef JS_ARENAMETER
     memset(&pool->stats, 0, sizeof pool->stats);
     pool->stats.name = strdup(name);
     pool->stats.next = arena_stats_list;
     arena_stats_list = &pool->stats;
 #endif
 }
 
@@ -155,28 +154,19 @@ JS_ArenaAllocate(JSArenaPool *pool, size
         ap = &a->next;
         if (!*ap) {
             /* Not enough space in pool, so we must malloc. */
             extra = (nb > pool->arenasize) ? HEADER_SIZE(pool) : 0;
             hdrsz = sizeof *a + extra + pool->mask;
             gross = hdrsz + JS_MAX(nb, pool->arenasize);
             if (gross < nb)
                 return NULL;
-            if (pool->quotap) {
-                if (gross > *pool->quotap)
-                    return NULL;
-                b = (JSArena *) OffTheBooks::malloc_(gross);
-                if (!b)
-                    return NULL;
-                *pool->quotap -= gross;
-            } else {
-                b = (JSArena *) OffTheBooks::malloc_(gross);
-                if (!b)
-                    return NULL;
-            }
+            b = (JSArena *) OffTheBooks::malloc_(gross);
+            if (!b)
+                return NULL;
 
             b->next = NULL;
             b->limit = (jsuword)b + gross;
             JS_COUNT_ARENA(pool,++);
             COUNT(pool, nmallocs);
 
             /* If oversized, store ap in the header, just before a->base. */
             *ap = a = b;
@@ -198,17 +188,17 @@ JS_ArenaAllocate(JSArenaPool *pool, size
     JS_ASSERT(a->base <= a->avail && a->avail <= a->limit);
     return p;
 }
 
 JS_PUBLIC_API(void *)
 JS_ArenaRealloc(JSArenaPool *pool, void *p, size_t size, size_t incr)
 {
     JSArena **ap, *a, *b;
-    jsuword boff, aoff, extra, hdrsz, gross, growth;
+    jsuword boff, aoff, extra, hdrsz, gross;
 
     /*
      * Use the oversized-single-allocation header to avoid searching for ap.
      * See JS_ArenaAllocate, the SET_HEADER call.
      */
     if (size > pool->arenasize) {
         ap = *PTR_TO_HEADER(pool, p);
         a = *ap;
@@ -221,29 +211,19 @@ JS_ArenaRealloc(JSArenaPool *pool, void 
     JS_ASSERT(a->base == (jsuword)p);
     boff = JS_UPTRDIFF(a->base, a);
     aoff = JS_ARENA_ALIGN(pool, size + incr);
     JS_ASSERT(aoff > pool->arenasize);
     extra = HEADER_SIZE(pool);                  /* oversized header holds ap */
     hdrsz = sizeof *a + extra + pool->mask;     /* header and alignment slop */
     gross = hdrsz + aoff;
     JS_ASSERT(gross > aoff);
-    if (pool->quotap) {
-        growth = gross - (a->limit - (jsuword) a);
-        if (growth > *pool->quotap)
-            return NULL;
-        a = (JSArena *) OffTheBooks::realloc_(a, gross);
-        if (!a)
-            return NULL;
-        *pool->quotap -= growth;
-    } else {
-        a = (JSArena *) OffTheBooks::realloc_(a, gross);
-        if (!a)
-            return NULL;
-    }
+    a = (JSArena *) OffTheBooks::realloc_(a, gross);
+    if (!a)
+        return NULL;
 #ifdef JS_ARENAMETER
     pool->stats.nreallocs++;
 #endif
 
     if (a != *ap) {
         /* Oops, realloc moved the allocation: update other pointers to a. */
         if (pool->current == *ap)
             pool->current = a;
@@ -309,18 +289,16 @@ FreeArenaList(JSArenaPool *pool, JSArena
         a->avail = a->base;
         JS_CLEAR_UNUSED(a);
     } while ((a = a->next) != NULL);
     a = *ap;
 #endif
 
     do {
         *ap = a->next;
-        if (pool->quotap)
-            *pool->quotap += a->limit - (jsuword) a;
         JS_CLEAR_ARENA(a);
         JS_COUNT_ARENA(pool,--);
         UnwantedForeground::free_(a);
     } while ((a = *ap) != NULL);
 
     pool->current = head;
 }
 
--- a/js/src/jsarena.h
+++ b/js/src/jsarena.h
@@ -84,18 +84,16 @@ struct JSArenaStats {
 };
 #endif
 
 struct JSArenaPool {
     JSArena     first;          /* first arena in pool list */
     JSArena     *current;       /* arena from which to allocate space */
     size_t      arenasize;      /* net exact size of a new arena */
     jsuword     mask;           /* alignment mask (power-of-2 - 1) */
-    size_t      *quotap;        /* pointer to the quota on pool allocation
-                                   size or null if pool is unlimited */
 #ifdef JS_ARENAMETER
     JSArenaStats stats;
 #endif
 };
 
 #define JS_ARENA_ALIGN(pool, n) (((jsuword)(n) + (pool)->mask) & ~(pool)->mask)
 
 #define JS_ARENA_ALLOCATE(p, pool, nb)                                        \
@@ -206,17 +204,17 @@ struct JSArenaPool {
         (a) = NULL;                                                           \
     JS_END_MACRO
 
 /*
  * Initialize an arena pool with a minimum size per arena of size bytes.
  */
 extern JS_PUBLIC_API(void)
 JS_InitArenaPool(JSArenaPool *pool, const char *name, size_t size,
-                 size_t align, size_t *quotap);
+                 size_t align);
 
 /*
  * Free the arenas in pool.  The user may continue to allocate from pool
  * after calling this function.  There is no need to call JS_InitArenaPool()
  * again unless JS_FinishArenaPool(pool) has been called.
  */
 extern JS_PUBLIC_API(void)
 JS_FreeArenaPool(JSArenaPool *pool);
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -454,19 +454,20 @@ enum OwnCharsBehavior
     TakeCharOwnership
 };
 
 /*
  * Callers passing OwnChars have freshly allocated *pchars and thus this
  * memory can be used as a new JSAtom's buffer without copying. When this flag
  * is set, the contract is that callers will free *pchars iff *pchars == NULL.
  */
+JS_ALWAYS_INLINE
 static JSAtom *
-Atomize(JSContext *cx, const jschar **pchars, size_t length,
-        InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
+AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
+              InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
 {
     const jschar *chars = *pchars;
 
     if (JSAtom *s = JSAtom::lookupStatic(chars, length))
         return s;
 
     AutoLockAtomsCompartment lock(cx);
 
@@ -507,16 +508,23 @@ Atomize(JSContext *cx, const jschar **pc
     if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(key, ib))) {
         JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
         return NULL;
     }
 
     return key->morphAtomizedStringIntoAtom();
 }
 
+static JSAtom *
+Atomize(JSContext *cx, const jschar **pchars, size_t length,
+        InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
+{
+    return AtomizeInline(cx, pchars, length, ib, ocb);
+}
+
 JSAtom *
 js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
 {
     if (str->isAtom()) {
         JSAtom &atom = str->asAtom();
         /* N.B. static atoms are effectively always interned. */
         if (ib != InternAtom || atom.isStaticAtom())
             return &atom;
@@ -590,17 +598,17 @@ js_Atomize(JSContext *cx, const char *by
 JSAtom *
 js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib)
 {
     CHECK_REQUEST(cx);
 
     if (!CheckStringLength(cx, length))
         return NULL;
 
-    return Atomize(cx, &chars, length, ib);
+    return AtomizeInline(cx, &chars, length, ib);
 }
 
 JSAtom *
 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
 {
     if (JSAtom *atom = JSAtom::lookupStatic(chars, length))
         return atom;
     AutoLockAtomsCompartment lock(cx);
@@ -662,17 +670,17 @@ js_alloc_temp_space(void *priv, size_t s
         if (space) {
             parser->tempFreeList[bin] = *(void **)space;
             return space;
         }
     }
 
     JS_ARENA_ALLOCATE(space, &parser->context->tempPool, size);
     if (!space)
-        js_ReportOutOfScriptQuota(parser->context);
+        js_ReportOutOfMemory(parser->context);
     return space;
 }
 
 static void
 js_free_temp_space(void *priv, void *item, size_t size)
 {
     if (size >= TEMP_SIZE_LIMIT)
         return;
@@ -694,17 +702,17 @@ js_alloc_temp_entry(void *priv, const vo
     ale = parser->aleFreeList;
     if (ale) {
         parser->aleFreeList = ALE_NEXT(ale);
         return &ale->entry;
     }
 
     JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &parser->context->tempPool);
     if (!ale) {
-        js_ReportOutOfScriptQuota(parser->context);
+        js_ReportOutOfMemory(parser->context);
         return NULL;
     }
     return &ale->entry;
 }
 
 static void
 js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag)
 {
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -312,25 +312,22 @@ js_NewContext(JSRuntime *rt, size_t stac
     if (!mem)
         return NULL;
 
     cx = new (mem) JSContext(rt);
     cx->debugHooks = &rt->globalDebugHooks;
 #if JS_STACK_GROWTH_DIRECTION > 0
     cx->stackLimit = (jsuword) -1;
 #endif
-    cx->scriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
     JS_STATIC_ASSERT(JSVERSION_DEFAULT == 0);
     JS_ASSERT(cx->findVersion() == JSVERSION_DEFAULT);
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
-    JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble),
-                     &cx->scriptStackQuota);
-    JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int),
-                     &cx->scriptStackQuota);
+    JS_InitArenaPool(&cx->tempPool, "temp", TEMP_POOL_CHUNK_SIZE, sizeof(jsdouble));
+    JS_InitArenaPool(&cx->regExpPool, "regExp", TEMP_POOL_CHUNK_SIZE, sizeof(int));
 
     JS_ASSERT(cx->resolveFlags == 0);
 
     if (!cx->busyArrays.init()) {
         Foreground::delete_(cx);
         return NULL;
     }
 
@@ -649,29 +646,33 @@ js_DestroyContext(JSContext *cx, JSDestr
          * request to end.  We'll let it run below, just before we do the truly
          * final GC and then free atom state.
          */
         while (cx->outstandingRequests != 0)
             JS_EndRequest(cx);
 #endif
 
         if (last) {
+            GCREASON(LASTCONTEXT);
             js_GC(cx, NULL, GC_LAST_CONTEXT);
             DUMP_EVAL_CACHE_METER(cx);
             DUMP_FUNCTION_METER(cx);
 
             /* Take the runtime down, now that it has no contexts or atoms. */
             JS_LOCK_GC(rt);
             rt->state = JSRTS_DOWN;
             JS_NOTIFY_ALL_CONDVAR(rt->stateChange);
         } else {
-            if (mode == JSDCM_FORCE_GC)
+            if (mode == JSDCM_FORCE_GC) {
+                GCREASON(DESTROYCONTEXT);
                 js_GC(cx, NULL, GC_NORMAL);
-            else if (mode == JSDCM_MAYBE_GC)
+            } else if (mode == JSDCM_MAYBE_GC) {
+                GCREASON(DESTROYCONTEXT);
                 JS_MaybeGC(cx);
+            }
             JS_LOCK_GC(rt);
             js_WaitForGC(rt);
         }
     }
 #ifdef JS_THREADSAFE
 #ifdef DEBUG
     JSThread *t = cx->thread();
 #endif
@@ -772,20 +773,20 @@ ReportError(JSContext *cx, const char *m
 /* The report must be initially zeroed. */
 static void
 PopulateReportBlame(JSContext *cx, JSErrorReport *report)
 {
     /*
      * Walk stack until we find a frame that is associated with some script
      * rather than a native frame.
      */
-    for (StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_TOP); fp; fp = fp->prev()) {
-        if (fp->pc(cx)) {
-            report->filename = fp->script()->filename;
-            report->lineno = js_FramePCToLineNumber(cx, fp);
+    for (FrameRegsIter iter(cx, FRAME_EXPAND_TOP); !iter.done(); ++iter) {
+        if (iter.fp()->isScriptFrame()) {
+            report->filename = iter.fp()->script()->filename;
+            report->lineno = js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
             break;
         }
     }
 }
 
 /*
  * We don't post an exception in this case, since doing so runs into
  * complications of pre-allocating an exception object which required
@@ -837,23 +838,16 @@ js_ReportOutOfMemory(JSContext *cx)
     }
 
     if (onError) {
         AutoAtomicIncrement incr(&cx->runtime->inOOMReport);
         onError(cx, msg, &report);
     }
 }
 
-void
-js_ReportOutOfScriptQuota(JSContext *maybecx)
-{
-    if (maybecx)
-        JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_SCRIPT_STACK_QUOTA);
-}
-
 JS_FRIEND_API(void)
 js_ReportOverRecursed(JSContext *maybecx)
 {
     if (maybecx)
         JS_ReportErrorNumber(maybecx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
 }
 
 void
@@ -1586,16 +1580,17 @@ JSRuntime::onTooMuchMalloc()
     AutoLockGC lock(this);
 
     /*
      * We can be called outside a request and can race against a GC that
      * mutates the JSThread set during the sweeping phase.
      */
     js_WaitForGC(this);
 #endif
+    GCREASON(TOOMUCHMALLOC);
     TriggerGC(this);
 }
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     gcHelperThread.waitBackgroundSweepEnd(this);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -1079,19 +1079,16 @@ struct JSContext
      * True if generating an error, to prevent runaway recursion.
      * NB: generatingError packs with throwing below.
      */
     JSPackedBool        generatingError;
 
     /* Limit pointer for checking native stack consumption during recursion. */
     jsuword             stackLimit;
 
-    /* Quota on the size of arenas used to compile and execute scripts. */
-    size_t              scriptStackQuota;
-
     /* Data shared by threads in an address space. */
     JSRuntime *const    runtime;
 
     /* GC heap compartment. */
     JSCompartment       *compartment;
 
     inline void setCompartment(JSCompartment *compartment);
 
@@ -1463,23 +1460,16 @@ class AutoCheckRequestDepth {
     JS_ASSERT((cx)->thread()->data.requestDepth || (cx)->thread() == (cx)->runtime->gcThread); \
     AutoCheckRequestDepth _autoCheckRequestDepth(cx);
 
 #else
 # define CHECK_REQUEST(cx)          ((void) 0)
 # define CHECK_REQUEST_THREAD(cx)   ((void) 0)
 #endif
 
-static inline uintN
-FramePCOffset(JSContext *cx, js::StackFrame* fp)
-{
-    jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx);
-    return uintN(pc - fp->script()->code);
-}
-
 static inline JSAtom **
 FrameAtomBase(JSContext *cx, js::StackFrame *fp)
 {
     return fp->hasImacropc()
            ? cx->runtime->atomState.commonAtomsStart()
            : fp->script()->atomMap.vector;
 }
 
@@ -2344,22 +2334,16 @@ js_ExpandErrorArguments(JSContext *cx, J
                         void *userRef, const uintN errorNumber,
                         char **message, JSErrorReport *reportp,
                         bool charArgs, va_list ap);
 #endif
 
 extern void
 js_ReportOutOfMemory(JSContext *cx);
 
-/*
- * Report that cx->scriptStackQuota is exhausted.
- */
-void
-js_ReportOutOfScriptQuota(JSContext *maybecx);
-
 /* JS_CHECK_RECURSION is used outside JS, so JS_FRIEND_API. */
 JS_FRIEND_API(void)
 js_ReportOverRecursed(JSContext *maybecx);
 
 extern JS_FRIEND_API(void)
 js_ReportAllocationOverflow(JSContext *cx);
 
 #define JS_CHECK_RECURSION(cx, onerror)                                       \
@@ -2470,22 +2454,16 @@ js_CurrentPCIsInImacro(JSContext *cx);
 
 namespace js {
 
 class RegExpStatics;
 
 extern JS_FORCES_STACK JS_FRIEND_API(void)
 LeaveTrace(JSContext *cx);
 
-enum FrameExpandKind {
-    FRAME_EXPAND_NONE,
-    FRAME_EXPAND_TOP,
-    FRAME_EXPAND_ALL
-};
-
 #ifdef JS_METHODJIT
 namespace mjit {
     void ExpandInlineFrames(JSContext *cx, bool all);
 }
 #endif
 
 } /* namespace js */
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -117,17 +117,17 @@ JSCompartment::init(JSContext *cx)
 {
     chunk = NULL;
     for (unsigned i = 0; i < FINALIZE_LIMIT; i++)
         arenas[i].init();
 
     activeAnalysis = activeInference = false;
     types.init(cx);
 
-    JS_InitArenaPool(&pool, "analysis", 4096, 8, NULL);
+    JS_InitArenaPool(&pool, "analysis", 4096, 8);
 
     freeLists.init();
     if (!crossCompartmentWrappers.init())
         return false;
 
 #ifdef DEBUG
     if (rt->meterEmptyShapes()) {
         if (!emptyShapes.init())
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -97,17 +97,16 @@ struct TracerState
     FrameInfo**    rp;                  // call stack pointer
     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
     VMSideExit*    outermostTreeExitGuard; // the last side exit returned by js_CallTree
     TreeFragment*  outermostTree;       // the outermost tree we initially invoked
-    uintN*         inlineCallCountp;    // inline call count counter
     VMSideExit**   innermostNestedGuardp;
     VMSideExit*    innermost;
     uint64         startTime;
     TracerState*   prev;
 
     // 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.
@@ -116,17 +115,17 @@ struct TracerState
     // Used to communicate the location of the return value in case of a deep bail.
     double*        deepBailSp;
 
     // Used when calling natives from trace to root the vp vector.
     uintN          nativeVpLen;
     js::Value*     nativeVp;
 
     TracerState(JSContext *cx, TraceMonitor *tm, TreeFragment *ti,
-                uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp);
+                VMSideExit** innermostNestedGuardp);
     ~TracerState();
 };
 
 /*
  * Storage for the execution state and store during trace execution. Generated
  * code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS|
  * doubles after the stack begins. Thus, on trace, |TracerState::eos| holds a
  * pointer to the first global.
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1435,17 +1435,17 @@ JS_PUBLIC_API(JSScript *)
 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
 {
     return Valueify(fp)->maybeScript();
 }
 
 JS_PUBLIC_API(jsbytecode *)
 JS_GetFramePC(JSContext *cx, JSStackFrame *fp)
 {
-    return Valueify(fp)->pc(cx);
+    return Valueify(fp)->pcQuadratic(cx);
 }
 
 JS_PUBLIC_API(JSStackFrame *)
 JS_GetScriptedCaller(JSContext *cx, JSStackFrame *fp)
 {
     return Jsvalify(js_GetScriptedCaller(cx, Valueify(fp)));
 }
 
@@ -2490,19 +2490,19 @@ jstv_Filename(JSStackFrame *fp)
         fp = fp->prev();
     return (fp && fp->maybeScript() && fp->script()->filename)
            ? (char *)fp->script()->filename
            : jstv_empty;
 }
 inline uintN
 jstv_Lineno(JSContext *cx, JSStackFrame *fp)
 {
-    while (fp && fp->pc(cx) == NULL)
+    while (fp && fp->pcQuadratic(cx) == NULL)
         fp = fp->prev();
-    return (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
+    return (fp && fp->pcQuadratic(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
 }
 
 /* Collect states here and distribute to a matching buffer, if any */
 JS_FRIEND_API(void)
 js::StoreTraceVisState(JSContext *cx, TraceVisState s, TraceVisExitReason r)
 {
     StackFrame *fp = cx->fp();
 
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -170,17 +170,17 @@ EmitCheck(JSContext *cx, JSCodeGenerator
         if (!base) {
             JS_ARENA_ALLOCATE_CAST(base, jsbytecode *, cg->codePool, incr);
         } else {
             size = BYTECODE_SIZE(limit - base);
             incr -= size;
             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
         }
         if (!base) {
-            js_ReportOutOfScriptQuota(cx);
+            js_ReportOutOfMemory(cx);
             return -1;
         }
         CG_BASE(cg) = base;
         CG_LIMIT(cg) = base + length;
         CG_NEXT(cg) = base + offset;
     }
     return offset;
 }
@@ -520,17 +520,17 @@ AddJumpTarget(AddJumpTargetArgs *args, J
 
         jt = cg->jtFreeList;
         if (jt) {
             cg->jtFreeList = jt->kids[JT_LEFT];
         } else {
             JS_ARENA_ALLOCATE_CAST(jt, JSJumpTarget *, &args->cx->tempPool,
                                    sizeof *jt);
             if (!jt) {
-                js_ReportOutOfScriptQuota(args->cx);
+                js_ReportOutOfMemory(args->cx);
                 return 0;
             }
         }
         jt->offset = args->offset;
         jt->balance = 0;
         jt->kids[JT_LEFT] = jt->kids[JT_RIGHT] = NULL;
         cg->numJumpTargets++;
         args->node = jt;
@@ -976,17 +976,17 @@ OptimizeSpanDeps(JSContext *cx, JSCodeGe
         length = offset + growth;
         next = base + length;
         if (next > limit) {
             JS_ASSERT(length > BYTECODE_CHUNK);
             size = BYTECODE_SIZE(limit - base);
             incr = BYTECODE_SIZE(length) - size;
             JS_ARENA_GROW_CAST(base, jsbytecode *, cg->codePool, size, incr);
             if (!base) {
-                js_ReportOutOfScriptQuota(cx);
+                js_ReportOutOfMemory(cx);
                 return JS_FALSE;
             }
             CG_BASE(cg) = base;
             CG_LIMIT(cg) = next = base + length;
         }
         CG_NEXT(cg) = next;
 
         /*
@@ -4604,17 +4604,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         JS_ASSERT_IF(pn->pn_funbox->tcflags & TCF_FUN_HEAVYWEIGHT,
                      FUN_KIND(fun) == JSFUN_INTERPRETED);
 
         /* Generate code for the function's body. */
         void *cg2mark = JS_ARENA_MARK(cg->codePool);
         void *cg2space;
         JS_ARENA_ALLOCATE_TYPE(cg2space, JSCodeGenerator, cg->codePool);
         if (!cg2space) {
-            js_ReportOutOfScriptQuota(cx);
+            js_ReportOutOfMemory(cx);
             return JS_FALSE;
         }
         JSCodeGenerator *cg2 =
             new (cg2space) JSCodeGenerator(cg->parser,
                                            cg->codePool, cg->notePool,
                                            pn->pn_pos.begin.lineno);
 
         if (!cg2->init())
@@ -7316,17 +7316,17 @@ AllocSrcNote(JSContext *cx, JSCodeGenera
             JS_ARENA_ALLOCATE_CAST(CG_NOTES(cg), jssrcnote *, pool, size);
         } else {
             /* Grow by doubling note array size; update noteMask on success. */
             JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
             if (CG_NOTES(cg))
                 CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
         }
         if (!CG_NOTES(cg)) {
-            js_ReportOutOfScriptQuota(cx);
+            js_ReportOutOfMemory(cx);
             return -1;
         }
     }
 
     CG_NOTE_COUNT(cg) = index + 1;
     return index;
 }
 
@@ -7414,17 +7414,17 @@ GrowSrcNotes(JSContext *cx, JSCodeGenera
     JSArenaPool *pool;
     size_t size;
 
     /* Grow by doubling note array size; update noteMask on success. */
     pool = cg->notePool;
     size = SRCNOTE_SIZE(CG_NOTE_MASK(cg) + 1);
     JS_ARENA_GROW_CAST(CG_NOTES(cg), jssrcnote *, pool, size, size);
     if (!CG_NOTES(cg)) {
-        js_ReportOutOfScriptQuota(cx);
+        js_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
     CG_NOTE_MASK(cg) = (CG_NOTE_MASK(cg) << 1) | 1;
     return JS_TRUE;
 }
 
 jssrcnote *
 js_AddToSrcNoteDelta(JSContext *cx, JSCodeGenerator *cg, jssrcnote *sn,
@@ -7652,17 +7652,17 @@ NewTryNote(JSContext *cx, JSCodeGenerato
 
     JS_ASSERT((uintN)(uint16)stackDepth == stackDepth);
     JS_ASSERT(start <= end);
     JS_ASSERT((size_t)(uint32)start == start);
     JS_ASSERT((size_t)(uint32)end == end);
 
     JS_ARENA_ALLOCATE_TYPE(tryNode, JSTryNode, &cx->tempPool);
     if (!tryNode) {
-        js_ReportOutOfScriptQuota(cx);
+        js_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
 
     tryNode->note.kind = kind;
     tryNode->note.stackDepth = (uint16)stackDepth;
     tryNode->note.start = (uint32)start;
     tryNode->note.length = (uint32)(end - start);
     tryNode->prev = cg->lastTryNode;
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -264,17 +264,16 @@ static JSBool
 InitExnPrivate(JSContext *cx, JSObject *exnObject, JSString *message,
                JSString *filename, uintN lineno, JSErrorReport *report)
 {
     JSSecurityCallbacks *callbacks;
     CheckAccessOp checkAccess;
     JSErrorReporter older;
     JSExceptionState *state;
     jsid callerid;
-    StackFrame *fp, *fpstop;
     size_t stackDepth, valueCount, size;
     JSBool overflow;
     JSExnPrivate *priv;
     JSStackTraceElem *elem;
     jsval *values;
 
     JS_ASSERT(exnObject->getClass() == &js_ErrorClass);
 
@@ -290,32 +289,33 @@ InitExnPrivate(JSContext *cx, JSObject *
                   ? Valueify(callbacks->checkObjectAccess)
                   : NULL;
     older = JS_SetErrorReporter(cx, NULL);
     state = JS_SaveExceptionState(cx);
 
     callerid = ATOM_TO_JSID(cx->runtime->atomState.callerAtom);
     stackDepth = 0;
     valueCount = 0;
-    for (fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL); fp; fp = fp->prev()) {
+    FrameRegsIter firstPass(cx, FRAME_EXPAND_ALL);
+    for (; !firstPass.done(); ++firstPass) {
+        StackFrame *fp = firstPass.fp();
         if (fp->compartment() != cx->compartment)
             break;
         if (fp->isNonEvalFunctionFrame()) {
             Value v = NullValue();
             if (checkAccess &&
                 !checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) {
                 break;
             }
             valueCount += fp->numActualArgs();
         }
         ++stackDepth;
     }
     JS_RestoreExceptionState(cx, state);
     JS_SetErrorReporter(cx, older);
-    fpstop = fp;
 
     size = offsetof(JSExnPrivate, stackElems);
     overflow = (stackDepth > ((size_t)-1 - size) / sizeof(JSStackTraceElem));
     size += stackDepth * sizeof(JSStackTraceElem);
     overflow |= (valueCount > ((size_t)-1 - size) / sizeof(jsval));
     size += valueCount * sizeof(jsval);
     if (overflow) {
         js_ReportAllocationOverflow(cx);
@@ -335,36 +335,37 @@ InitExnPrivate(JSContext *cx, JSObject *
     priv->filename = filename;
     priv->lineno = lineno;
     priv->stackDepth = stackDepth;
 
     values = GetStackTraceValueBuffer(priv);
     elem = priv->stackElems;
 
     /* N.B. frames do not need to be expanded again. */
-    for (fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE); fp != fpstop; fp = fp->prev()) {
+    for (FrameRegsIter iter(cx, FRAME_EXPAND_NONE); iter != firstPass; ++iter) {
+        StackFrame *fp = iter.fp();
         if (fp->compartment() != cx->compartment)
             break;
-        if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
+        if (!fp->isNonEvalFunctionFrame()) {
             elem->funName = NULL;
             elem->argc = 0;
         } else {
             elem->funName = fp->fun()->atom
                             ? fp->fun()->atom
                             : cx->runtime->emptyString;
             elem->argc = fp->numActualArgs();
             fp->forEachCanonicalActualArg(CopyTo(Valueify(values)));
             values += elem->argc;
         }
         elem->ulineno = 0;
         elem->filename = NULL;
         if (fp->isScriptFrame()) {
             elem->filename = fp->script()->filename;
-            if (fp->pc(cx))
-                elem->ulineno = js_FramePCToLineNumber(cx, fp);
+            if (fp->isScriptFrame())
+                elem->ulineno = js_FramePCToLineNumber(cx, fp, iter.pc());
         }
         ++elem;
     }
     JS_ASSERT(priv->stackElems + stackDepth == elem);
     JS_ASSERT(GetStackTraceValueBuffer(priv) + valueCount == values);
 
     exnObject->setPrivate(priv);
 
@@ -691,19 +692,16 @@ static JSString *
 FilenameToString(JSContext *cx, const char *filename)
 {
     return JS_NewStringCopyZ(cx, filename);
 }
 
 static JSBool
 Exception(JSContext *cx, uintN argc, Value *vp)
 {
-    JSString *message, *filename;
-    StackFrame *fp;
-
     /*
      * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
      * called as functions, without operator new.  But as we do not give
      * each constructor a distinct JSClass, whose .name member is used by
      * NewNativeClassInstance to find the class prototype, we must get the
      * class prototype ourselves.
      */
     JSObject &callee = vp[0].toObject();
@@ -725,52 +723,55 @@ Exception(JSContext *cx, uintN argc, Val
      * If it's a new object of class Exception, then null out the private
      * data so that the finalizer doesn't attempt to free it.
      */
     if (obj->getClass() == &js_ErrorClass)
         obj->setPrivate(NULL);
 
     /* Set the 'message' property. */
     Value *argv = vp + 2;
+    JSString *message;
     if (argc != 0 && !argv[0].isUndefined()) {
         message = js_ValueToString(cx, argv[0]);
         if (!message)
             return JS_FALSE;
         argv[0].setString(message);
     } else {
         message = NULL;
     }
 
+    /* Find the scripted caller. */
+    FrameRegsIter iter(cx, FRAME_EXPAND_TOP);
+    while (!iter.done() && !iter.fp()->isScriptFrame())
+        ++iter;
+
     /* Set the 'fileName' property. */
+    JSString *filename;
     if (argc > 1) {
         filename = js_ValueToString(cx, argv[1]);
         if (!filename)
             return JS_FALSE;
         argv[1].setString(filename);
-        fp = NULL;
     } else {
-        fp = js_GetScriptedCaller(cx, NULL);
-        if (fp) {
-            filename = FilenameToString(cx, fp->script()->filename);
+        if (!iter.done()) {
+            filename = FilenameToString(cx, iter.fp()->script()->filename);
             if (!filename)
                 return JS_FALSE;
         } else {
             filename = cx->runtime->emptyString;
         }
     }
 
     /* Set the 'lineNumber' property. */
     uint32_t lineno;
     if (argc > 2) {
         if (!ValueToECMAUint32(cx, argv[2], &lineno))
             return JS_FALSE;
     } else {
-        if (!fp)
-            fp = js_GetScriptedCaller(cx, NULL);
-        lineno = (fp && fp->pc(cx)) ? js_FramePCToLineNumber(cx, fp) : 0;
+        lineno = iter.done() ? 0 : js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
     }
 
     if (obj->getClass() == &js_ErrorClass &&
         !InitExnPrivate(cx, obj, message, filename, lineno, NULL)) {
         return JS_FALSE;
     }
 
     vp->setObject(*obj);
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1637,17 +1637,17 @@ fun_getProperty(JSContext *cx, JSObject 
 #ifdef JS_METHODJIT
     if (slot == FUN_CALLER && fp && fp->prev()) {
         /*
          * If the frame was called from within an inlined frame, mark the
          * innermost function as uninlineable to expand its frame and allow us
          * to recover its callee object.
          */
         JSInlinedSite *inlined;
-        fp->prev()->pc(cx, fp, &inlined);
+        fp->prev()->pcQuadratic(cx, fp, &inlined);
         if (inlined) {
             JSFunction *fun = fp->prev()->jit()->inlineFrames()[inlined->inlineIndex].fun;
             MarkTypeObjectFlags(cx, fun->getType(), OBJECT_FLAG_UNINLINEABLE);
         }
     }
 #endif
 
     switch (slot) {
@@ -2592,17 +2592,17 @@ Function(JSContext *cx, uintN argc, Valu
         /*
          * Allocate a string to hold the concatenated arguments, including room
          * for a terminating 0.  Mark cx->tempPool for later release, to free
          * collected_args and its tokenstream in one swoop.
          */
         AutoArenaAllocator aaa(&cx->tempPool);
         jschar *cp = aaa.alloc<jschar>(args_length + 1);
         if (!cp) {
-            js_ReportOutOfScriptQuota(cx);
+            js_ReportOutOfMemory(cx);
             return false;
         }
         jschar *collected_args = cp;
 
         /*
          * Concatenate the arguments into the new string, separated by commas.
          */
         for (uintN i = 0; i < n; i++) {
@@ -3145,17 +3145,17 @@ js_ReportIsNotFunction(JSContext *cx, co
      *
      * Conversely, values may have been popped from the stack in preparation
      * for a call (e.g., by SplatApplyArgs). Since we must pass an offset from
      * the top of the simulated stack to js_ReportValueError3, we do bounds
      * checking using the minimum of both the simulated and actual stack depth.
      */
     ptrdiff_t spindex = 0;
 
-    FrameRegsIter i(cx);
+    FrameRegsIter i(cx, FRAME_EXPAND_TOP);
     while (!i.done() && !i.pc())
         ++i;
 
     if (!i.done()) {
         uintN depth = js_ReconstructStackDepth(cx, i.fp()->script(), i.pc());
         Value *simsp = i.fp()->base() + depth;
         if (i.fp()->base() <= vp && vp < Min(simsp, i.sp()))
             spindex = vp - simsp;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1182,16 +1182,17 @@ ArenaList::getArenaWithFreeList(JSContex
 
     if (ArenaHeader *aheader = searchForFreeArena())
         return aheader;
     chunk = PickChunk(cx);
 
 #endif /* !JS_THREADSAFE */
 
     if (!chunk) {
+        GCREASON(CHUNK);
         TriggerGC(cx->runtime);
         return NULL;
     }
 
     /*
      * While we still hold the GC lock get the arena from the chunk and add it
      * to the head of the list before the cursor to prevent checking the arena
      * for the free things.
@@ -1344,16 +1345,17 @@ RunLastDitchGC(JSContext *cx)
     METER(rt->gcStats.lastditch++);
 #ifdef JS_THREADSAFE
     Maybe<AutoUnlockAtomsCompartment> maybeUnlockAtomsCompartment;
     if (cx->compartment == rt->atomsCompartment && rt->atomsCompartmentIsLocked)
         maybeUnlockAtomsCompartment.construct(cx);
 #endif
     /* The last ditch GC preserves all atoms. */
     AutoKeepAtoms keep(rt);
+    GCREASON(LASTDITCH);
     js_GC(cx, rt->gcTriggerCompartment, GC_NORMAL);
 
 #ifdef JS_THREADSAFE
     if (rt->gcBytes >= rt->gcMaxBytes)
         cx->runtime->gcHelperThread.waitBackgroundSweepEnd(cx->runtime);
 #endif
 
     return rt->gcBytes < rt->gcMaxBytes;
@@ -1894,16 +1896,17 @@ TriggerGC(JSRuntime *rt)
     TriggerAllOperationCallbacks(rt);
 }
 
 void
 TriggerCompartmentGC(JSCompartment *comp)
 {
     JSRuntime *rt = comp->rt;
     JS_ASSERT(!rt->gcRunning);
+    GCREASON(COMPARTMENT);
 
     if (rt->gcZeal()) {
         TriggerGC(rt);
         return;
     }
 
     if (rt->gcMode != JSGC_MODE_COMPARTMENT || comp == rt->atomsCompartment) {
         /* We can't do a compartmental GC of the default compartment. */
@@ -1934,28 +1937,32 @@ TriggerCompartmentGC(JSCompartment *comp
 }
 
 void
 MaybeGC(JSContext *cx)
 {
     JSRuntime *rt = cx->runtime;
 
     if (rt->gcZeal()) {
+        GCREASON(MAYBEGC);
         js_GC(cx, NULL, GC_NORMAL);
         return;
     }
 
     JSCompartment *comp = cx->compartment;
     if (rt->gcIsNeeded) {
+        GCREASON(MAYBEGC);
         js_GC(cx, (comp == rt->gcTriggerCompartment) ? comp : NULL, GC_NORMAL);
         return;
     }
 
-    if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4))
+    if (comp->gcBytes > 8192 && comp->gcBytes >= 3 * (comp->gcTriggerBytes / 4)) {
+        GCREASON(MAYBEGC);
         js_GC(cx, (rt->gcMode == JSGC_MODE_COMPARTMENT) ? comp : NULL, GC_NORMAL);
+    }
 }
 
 } /* namespace js */
 
 void
 js_DestroyScriptsToGC(JSContext *cx, JSCompartment *comp)
 {
     JSScript **listp, *script;
--- a/js/src/jsgcstats.cpp
+++ b/js/src/jsgcstats.cpp
@@ -367,16 +367,20 @@ GCMarker::dumpConservativeRoots()
 
     if (fp != stdout && fp != stderr)
         fclose(fp);
 }
 #endif /* JS_DUMP_CONSERVATIVE_GC_ROOTS */
 
 #if defined(MOZ_GCTIMER) || defined(JSGC_TESTPILOT)
 
+volatile GCTimer::JSGCReason gcReason = GCTimer::NOREASON;
+const char *gcReasons[] = {"  API", "Maybe", "LastC", "DestC", "Compa", "LastD",
+                          "Malloc", "Alloc", "Chunk", "Shape", "  None"};
+
 jsrefcount newChunkCount = 0;
 jsrefcount destroyChunkCount = 0;
 
 GCTimer::GCTimer(JSRuntime *rt, JSCompartment *comp)
   : rt(rt), isCompartmental(comp),
     enabled(rt->gcData.isTimerEnabled())
 {
     clearTimestamps();
@@ -447,35 +451,37 @@ GCTimer::finish(bool lastGC)
                     TIMEDIFF(startSweep, sweepDestroyEnd));
         } else {
             static FILE *gcFile;
 
             if (!gcFile) {
                 gcFile = fopen("gcTimer.dat", "a");
 
                 fprintf(gcFile, "     AppTime,  Total,   Wait,   Mark,  Sweep, FinObj,"
-                                " FinStr, SwShapes, Destroy,    End, +Chu, -Chu\n");
+                                " FinStr, SwShapes, Destroy,    End, +Chu, -Chu, T, Reason\n");
             }
             JS_ASSERT(gcFile);
             /*               App   , Tot  , Wai  , Mar  , Swe  , FiO  , FiS  , SwS  , Des   , End */
             fprintf(gcFile, "%12.0f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %6.1f, %8.1f,  %6.1f, %6.1f, ",
                     appTime, gcTime, waitTime, markTime, sweepTime, sweepObjTime, sweepStringTime,
                     sweepShapeTime, destroyTime, endTime);
-            fprintf(gcFile, "%4d, %4d\n", newChunkCount, destroyChunkCount);
+            fprintf(gcFile, "%4d, %4d,", newChunkCount, destroyChunkCount);
+            fprintf(gcFile, " %s, %s\n", isCompartmental ? "C" : "G", gcReasons[gcReason]);
             fflush(gcFile);
 
             if (lastGC) {
                 fclose(gcFile);
                 gcFile = NULL;
             }
         }
 #endif
     }
     newChunkCount = 0;
     destroyChunkCount = 0;
+    gcReason = NOREASON;
 }
 
 #undef TIMEDIFF
 
 #endif
 
 } //js
 
--- a/js/src/jsgcstats.h
+++ b/js/src/jsgcstats.h
@@ -206,28 +206,48 @@ struct GCTimer
 
     uint64 getFirstEnter();
 
     void clearTimestamps() {
         memset(&enter, 0, &end - &enter + sizeof(end));
     }
 
     void finish(bool lastGC);
+
+    enum JSGCReason {
+        PUBLIC_API,
+        MAYBEGC,
+        LASTCONTEXT,
+        DESTROYCONTEXT,
+        COMPARTMENT,
+        LASTDITCH,
+        TOOMUCHMALLOC,
+        ALLOCTRIGGER,
+        CHUNK,
+        SHAPE,
+        NOREASON
+    };
 };
 
+/* We accept the possiblility of races for this variable. */
+extern volatile GCTimer::JSGCReason gcReason;
+
+#define GCREASON(x) ((gcReason == GCTimer::NOREASON) ? gcReason = GCTimer::x : gcReason = gcReason)
+
 # define GCTIMER_PARAM              , GCTimer &gcTimer
 # define GCTIMER_ARG                , gcTimer
 # define GCTIMESTAMP(stamp_name_) \
     JS_BEGIN_MACRO \
         if (gcTimer.enabled) \
             gcTimer.stamp_name_ = PRMJ_Now(); \
     JS_END_MACRO
 # define GCTIMER_BEGIN(rt, comp)    GCTimer gcTimer(rt, comp)
 # define GCTIMER_END(last)          (gcTimer.finish(last))
 #else
+# define GCREASON(x)                ((void) 0)
 # define GCTIMER_PARAM
 # define GCTIMER_ARG
 # define GCTIMESTAMP(x)             ((void) 0)
 # define GCTIMER_BEGIN(rt, comp)    ((void) 0)
 # define GCTIMER_END(last)          ((void) 0)
 #endif
 
 } //js
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1672,28 +1672,24 @@ FixLazyArguments(JSContext *cx, JSScript
 #endif
 
     ScriptAnalysis *analysis = script->analysis(cx);
     if (analysis && !analysis->ranBytecode())
         analysis->analyzeBytecode(cx);
     if (!analysis || analysis->OOM())
         return;
 
-    for (AllFramesIter iter(cx); !iter.done(); ++iter) {
+    for (FrameRegsIter iter(cx, FRAME_EXPAND_NONE); !iter.done(); ++iter) {
         StackFrame *fp = iter.fp();
         if (fp->isScriptFrame() && fp->script() == script) {
-            JSInlinedSite *inline_;
-            jsbytecode *pc = fp->pc(cx, NULL, &inline_);
-            JS_ASSERT(!inline_);
-
             /*
              * Check locals and stack slots, assignment to individual arguments
              * is treated as an escape on the arguments.
              */
-            Value *sp = fp->base() + analysis->getCode(pc).stackDepth;
+            Value *sp = fp->base() + analysis->getCode(iter.pc()).stackDepth;
             for (Value *vp = fp->slots(); vp < sp; vp++) {
                 if (vp->isMagicCheck(JS_LAZY_ARGUMENTS)) {
                     if (!js_GetArgsValue(cx, fp, vp))
                         vp->setNull();
                 }
             }
         }
     }
@@ -2925,37 +2921,31 @@ TypeObject::clearNewScript(JSContext *cx
     for (unsigned i = 0; i < getPropertyCount(); i++) {
         Property *prop = getProperty(i);
         if (!prop)
             continue;
         if (prop->types.isDefiniteProperty())
             prop->types.setOwnProperty(cx, true);
     }
 
-#ifdef JS_METHODJIT
-    mjit::ExpandInlineFrames(cx, true);
-#endif
-
     /*
      * If we cleared the new script while in the middle of initializing an
      * object, it will still have the new script's shape and reflect the no
      * longer correct state of the object once its initialization is completed.
      * We can't really detect the possibility of this statically, but the new
      * script keeps track of where each property is initialized so we can walk
      * the stack and fix up any such objects.
      */
-    for (AllFramesIter iter(cx); !iter.done(); ++iter) {
+    for (FrameRegsIter iter(cx, FRAME_EXPAND_ALL); !iter.done(); ++iter) {
         StackFrame *fp = iter.fp();
         if (fp->isScriptFrame() && fp->isConstructing() &&
             fp->script() == newScript->script && fp->thisValue().isObject() &&
             fp->thisValue().toObject().type == this) {
             JSObject *obj = &fp->thisValue().toObject();
-            JSInlinedSite *inline_;
-            jsbytecode *pc = fp->pc(cx, NULL, &inline_);
-            JS_ASSERT(!inline_);
+            jsbytecode *pc = iter.pc();
 
             /* Whether all identified 'new' properties have been initialized. */
             bool finished = false;
 
             /* If not finished, number of properties that have been added. */
             uint32 numProperties = 0;
 
             /*
@@ -2978,18 +2968,17 @@ TypeObject::clearNewScript(JSContext *cx
                     } else if (init->offset > offset) {
                         /* Advanced past all properties which have been initialized. */
                         break;
                     } else if (init->offset == offset) {
                         StackSegment &seg = cx->stack.space().containingSegment(fp);
                         if (seg.currentFrame() == fp)
                             break;
                         fp = seg.computeNextFrame(fp);
-                        pc = fp->pc(cx, NULL, &inline_);
-                        JS_ASSERT(!inline_);
+                        pc = fp->pcQuadratic(cx);
                     } else {
                         /* This call has already finished. */
                         depth = 1;
                     }
                 } else if (init->kind == TypeNewScript::Initializer::FRAME_POP) {
                     if (depth) {
                         depth--;
                     } else {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -150,17 +150,17 @@ js::GetScopeChain(JSContext *cx)
  */
 JSObject *
 js::GetBlockChain(JSContext *cx, StackFrame *fp)
 {
     if (!fp->isScriptFrame())
         return NULL;
 
     /* Assume that imacros don't affect blockChain */
-    jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pc(cx);
+    jsbytecode *target = fp->hasImacropc() ? fp->imacropc() : fp->pcQuadratic(cx);
 
     JSScript *script = fp->script();
     jsbytecode *start = script->code;
     JS_ASSERT(target >= start && target < start + script->length);
 
     JSObject *blockChain = NULL;
     uintN indexBase = 0;
     ptrdiff_t oplen;
@@ -196,17 +196,17 @@ js::GetBlockChain(JSContext *cx, StackFr
  * instruction appears immediately after the current PC.
  * We ensure this happens for a few important ops like DEFFUN.
  * |oplen| is the length of opcode at the current PC.
  */
 JSObject *
 js::GetBlockChainFast(JSContext *cx, StackFrame *fp, JSOp op, size_t oplen)
 {
     /* Assume that we're in a script frame. */
-    jsbytecode *pc = fp->pc(cx);
+    jsbytecode *pc = fp->pcQuadratic(cx);
     JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == op);
 
     pc += oplen;
     op = JSOp(*pc);
 
     /* The fast paths assume no JSOP_RESETBASE/INDEXBASE or JSOP_TRAP noise. */
     if (op == JSOP_NULLBLOCKCHAIN)
         return NULL;
@@ -2188,17 +2188,17 @@ TypeCheckNextBytecode(JSContext *cx, JSS
         script->types.checkBytecode(cx, regs.pc, regs.sp);
     }
 #endif
 }
 
 namespace js {
 
 JS_REQUIRES_STACK JS_NEVER_INLINE bool
-Interpret(JSContext *cx, StackFrame *entryFrame, uintN inlineCallCount, InterpMode interpMode)
+Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_INTERP);
 #endif
     JSAutoResolveFlags rf(cx, RESOLVE_INFER);
 
     JS_ASSERT(!cx->compartment->activeAnalysis);
 
@@ -2362,17 +2362,17 @@ Interpret(JSContext *cx, StackFrame *ent
         mjit::CompileStatus status =                                          \
             mjit::CanMethodJITAtBranch(cx, script, regs.fp(), regs.pc);       \
         if (status == mjit::Compile_Error)                                    \
             goto error;                                                       \
         if (status == mjit::Compile_Okay) {                                   \
             void *ncode =                                                     \
                 script->nativeCodeForPC(regs.fp()->isConstructing(), regs.pc);\
             interpReturnOK = mjit::JaegerShotAtSafePoint(cx, ncode);          \
-            if (inlineCallCount)                                              \
+            if (entryFrame != regs.fp())                                      \
                 goto jit_return;                                              \
             regs.fp()->setFinishedInInterpreter();                            \
             goto leave_on_safe_point;                                         \
         }                                                                     \
         if (status == mjit::Compile_Abort) {                                  \
             useMethodJIT = false;                                             \
         }                                                                     \
     JS_END_MACRO
@@ -2415,17 +2415,17 @@ Interpret(JSContext *cx, StackFrame *ent
     JS_END_MACRO
 
 #define MONITOR_BRANCH()                                                      \
     JS_BEGIN_MACRO                                                            \
         if (TRACING_ENABLED(cx)) {                                            \
             if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && useMethodJIT) { \
                 MONITOR_BRANCH_METHODJIT();                                   \
             } else {                                                          \
-                MonitorResult r = MonitorLoopEdge(cx, inlineCallCount, interpMode); \
+                MonitorResult r = MonitorLoopEdge(cx, interpMode);            \
                 if (r == MONITOR_RECORDING) {                                 \
                     JS_ASSERT(TRACE_RECORDER(cx));                            \
                     JS_ASSERT(!TRACE_PROFILER(cx));                           \
                     MONITOR_BRANCH_TRACEVIS;                                  \
                     ENABLE_INTERRUPTS();                                      \
                     CLEAR_LEAVE_ON_TRACE_POINT();                             \
                 }                                                             \
                 RESTORE_INTERP_VARS();                                        \
@@ -2569,17 +2569,17 @@ Interpret(JSContext *cx, StackFrame *ent
         goto error;
 
     /* Don't call the script prologue if executing between Method and Trace JIT. */
     if (interpMode == JSINTERP_NORMAL) {
         StackFrame *fp = regs.fp();
         JS_ASSERT_IF(!fp->isGeneratorFrame(), regs.pc == script->code);
         bool newType = fp->isConstructing() && cx->typeInferenceEnabled() &&
             fp->prev() && fp->prev()->isScriptFrame() &&
-            UseNewType(cx, fp->prev()->script(), fp->prev()->pc(cx));
+            UseNewType(cx, fp->prev()->script(), fp->prev()->pcQuadratic(cx, fp));
         if (!ScriptPrologueOrGeneratorResume(cx, fp, newType))
             goto error;
     }
 
     /* The REJOIN mode acts like the normal mode, except the prologue is skipped. */
     if (interpMode == JSINTERP_REJOIN)
         interpMode = JSINTERP_NORMAL;
 
@@ -2906,18 +2906,16 @@ BEGIN_CASE(JSOP_STOP)
 
         /* Sync interpreter locals. */
         script = regs.fp()->script();
         argv = regs.fp()->maybeFormalArgs();
         atoms = FrameAtomBase(cx, regs.fp());
 
         /* Resume execution in the calling frame. */
         RESET_USE_METHODJIT();
-        JS_ASSERT(inlineCallCount);
-        inlineCallCount--;
         if (JS_LIKELY(interpReturnOK)) {
             JS_ASSERT(js_CodeSpec[js_GetOpcode(cx, script, regs.pc)].length
                       == JSOP_CALL_LENGTH);
             TRACE_0(LeaveFrame);
             len = JSOP_CALL_LENGTH;
             DO_NEXT_OP(len);
         }
         goto error;
@@ -4699,22 +4697,16 @@ BEGIN_CASE(JSOP_FUNCALL)
         {
             JSScript *newscript = newfun->script();
             if (JS_UNLIKELY(newscript->isEmpty())) {
                 vp->setUndefined();
                 regs.sp = vp + 1;
                 goto end_call;
             }
 
-            /* Restrict recursion of lightweight functions. */
-            if (JS_UNLIKELY(inlineCallCount >= StackSpace::MAX_INLINE_CALLS)) {
-                js_ReportOverRecursed(cx);
-                goto error;
-            }
-
             /* This will construct the type sets for the callee, if necessary. */
             TypeMonitorCall(cx, CallArgsFromVp(argc, vp), flags & StackFrame::CONSTRUCTING);
 
             bool newType = (flags & StackFrame::CONSTRUCTING) &&
                 cx->typeInferenceEnabled() && UseNewType(cx, script, regs.pc);
 
             /* Get pointer to new frame/slots, prepare arguments. */
             ContextStack &stack = cx->stack;
@@ -4736,32 +4728,32 @@ BEGIN_CASE(JSOP_FUNCALL)
             argv = regs.fp()->formalArgsEnd() - newfun->nargs;
             atoms = script->atomMap.vector;
 
             /* Now that the new frame is rooted, maybe create a call object. */
             if (newfun->isHeavyweight() && !CreateFunCallObject(cx, regs.fp()))
                 goto error;
 
             RESET_USE_METHODJIT();
-            inlineCallCount++;
             JS_RUNTIME_METER(rt, inlineCalls);
 
             TRACE_0(EnterFrame);
 
             CHECK_INTERRUPT_HANDLER();
 
 #ifdef JS_METHODJIT
             /* Try to ensure methods are method JIT'd.  */
             mjit::CompileRequest request = (interpMode == JSINTERP_NORMAL)
                                            ? mjit::CompileRequest_Interpreter
                                            : mjit::CompileRequest_JIT;
             mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp(), request);
             if (status == mjit::Compile_Error)
                 goto error;
-            if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) && status == mjit::Compile_Okay && !newType) {
+            if (!TRACE_RECORDER(cx) && !TRACE_PROFILER(cx) &&
+                status == mjit::Compile_Okay && !newType) {
                 interpReturnOK = mjit::JaegerShot(cx);
                 CHECK_INTERRUPT_HANDLER();
                 goto jit_return;
             }
 #endif
 
             if (!ScriptPrologue(cx, regs.fp(), newType))
                 goto error;
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -254,17 +254,17 @@ enum InterpMode
     JSINTERP_SKIP_TRAP = 5  /* as REJOIN, but skip trap at first opcode */
 };
 
 /*
  * Execute the caller-initialized frame for a user-defined script or function
  * pointed to by cx->fp until completion or error.
  */
 extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
-Interpret(JSContext *cx, StackFrame *stopFp, uintN inlineCallCount = 0, InterpMode mode = JSINTERP_NORMAL);
+Interpret(JSContext *cx, StackFrame *stopFp, InterpMode mode = JSINTERP_NORMAL);
 
 extern JS_REQUIRES_STACK bool
 RunScript(JSContext *cx, JSScript *script, StackFrame *fp);
 
 extern bool
 CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs);
 
 extern bool
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1162,17 +1162,17 @@ EvalKernel(JSContext *cx, const CallArgs
      * way so that the compiler can make assumptions about what bindings may or
      * may not exist in the current frame if it doesn't see 'eval'.)
      */
     uintN staticLevel;
     if (evalType == DIRECT_EVAL) {
         staticLevel = caller->script()->staticLevel + 1;
 
 #ifdef DEBUG
-        jsbytecode *callerPC = caller->pc(cx);
+        jsbytecode *callerPC = caller->pcQuadratic(cx);
         JS_ASSERT_IF(caller->isFunctionFrame(), caller->fun()->isHeavyweight());
         JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
 #endif
     } else {
         JS_ASSERT(call.callee().getGlobal() == &scopeobj);
         staticLevel = 0;
     }
 
@@ -1333,29 +1333,17 @@ PrincipalsForCompiledCode(const CallArgs
      *
      * In the converse situation, where the callee has lower privileges than the
      * caller, we might initially guess that the caller would want to retain
      * their higher privileges in the generated code. However, since the
      * compiled code will be run with the callee's scope chain, this would make
      * fp->script()->compartment() != fp->compartment().
      */
 
-    JSPrincipals *calleePrincipals = call.callee().principals(cx);
-
-#ifdef DEBUG
-    if (calleePrincipals) {
-        if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) {
-            if (JSPrincipals *callerPrincipals = scopeChain->principals(cx)) {
-                JS_ASSERT(callerPrincipals->subsume(callerPrincipals, calleePrincipals));
-            }
-        }
-    }
-#endif
-
-    return calleePrincipals;
+    return call.callee().principals(cx);
 }
 
 }  /* namespace js */
 
 #if JS_HAS_OBJ_WATCHPOINT
 
 static JSBool
 obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
@@ -7319,17 +7307,17 @@ MaybeDumpValue(const char *name, const V
 JS_FRIEND_API(void)
 js_DumpStackFrame(JSContext *cx, StackFrame *start)
 {
     /* This should only called during live debugging. */
     VOUCH_DOES_NOT_REQUIRE_STACK();
 
     if (!start)
         start = cx->maybefp();
-    FrameRegsIter i(cx);
+    FrameRegsIter i(cx, FRAME_EXPAND_NONE);
     while (!i.done() && i.fp() != start)
         ++i;
 
     if (i.done()) {
         fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx);
         return;
     }
 
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -627,17 +627,17 @@ SprintEnsureBuffer(Sprinter *sp, size_t 
         return JS_TRUE;
     base = sp->base;
     if (!base) {
         JS_ARENA_ALLOCATE_CAST(base, char *, sp->pool, nb);
     } else {
         JS_ARENA_GROW_CAST(base, char *, sp->pool, sp->size, nb);
     }
     if (!base) {
-        js_ReportOutOfScriptQuota(sp->context);
+        js_ReportOutOfMemory(sp->context);
         return JS_FALSE;
     }
     sp->base = base;
     sp->size += nb;
     return JS_TRUE;
 }
 
 namespace js {
@@ -850,17 +850,17 @@ js_NewPrinter(JSContext *cx, const char 
               uintN indent, JSBool pretty, JSBool grouped, JSBool strict)
 {
     JSPrinter *jp;
 
     jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter));
     if (!jp)
         return NULL;
     INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0);
-    JS_InitArenaPool(&jp->pool, name, 256, 1, &cx->scriptStackQuota);
+    JS_InitArenaPool(&jp->pool, name, 256, 1);
     jp->indent = indent;
     jp->pretty = !!pretty;
     jp->grouped = !!grouped;
     jp->strict = !!strict;
     jp->script = NULL;
     jp->dvgfence = NULL;
     jp->pcstack = NULL;
     jp->fun = fun;
@@ -1299,16 +1299,18 @@ DecompileSwitch(SprintStack *ss, TableEn
                 todo = -1;
                 if (table[i].label) {
                     str = table[i].label;
                     key = JSVAL_VOID;
                 } else if (JSVAL_IS_DOUBLE(key)) {
                     JSOp junk;
 
                     todo = SprintDoubleValue(&ss->sprinter, key, &junk);
+                    if (todo < 0)
+                        return JS_FALSE;
                     str = NULL;
                 } else {
                     str = js_ValueToString(cx, Valueify(key));
                     if (!str)
                         return JS_FALSE;
                 }
                 if (todo >= 0) {
                     rval = OFF2STR(&ss->sprinter, todo);
@@ -1851,17 +1853,17 @@ InitSprintStack(JSContext *cx, SprintSta
 
     INIT_SPRINTER(cx, &ss->sprinter, &cx->tempPool, PAREN_SLOP);
 
     /* Allocate the parallel (to avoid padding) offset and opcode stacks. */
     offsetsz = depth * sizeof(ptrdiff_t);
     opcodesz = depth * sizeof(jsbytecode);
     JS_ARENA_ALLOCATE(space, &cx->tempPool, offsetsz + opcodesz);
     if (!space) {
-        js_ReportOutOfScriptQuota(cx);
+        js_ReportOutOfMemory(cx);
         return JS_FALSE;
     }
     ss->offsets = (ptrdiff_t *) space;
     ss->opcodes = (jsbytecode *) ((char *)space + offsetsz);
 
     ss->top = ss->inArrayInit = 0;
     ss->inGenExp = JS_FALSE;
     ss->printer = jp;
@@ -2067,38 +2069,37 @@ Decompile(SprintStack *ss, jsbytecode *p
 
         /*
          * Save source literal associated with JS now before the following
          * rewrite changes op. See bug 380197.
          */
         token = CodeToken[op];
 
         if (pc + oplen == jp->dvgfence) {
-            StackFrame *fp;
-            uint32 format, mode, type;
-
             /*
              * Rewrite non-get ops to their "get" format if the error is in
              * the bytecode at pc, so we don't decompile more than the error
              * expression.
              */
-            fp = js_GetScriptedCaller(cx, NULL);
-            format = cs->format;
-            if (((fp && pc == fp->pc(cx)) ||
+            FrameRegsIter iter(cx, FRAME_EXPAND_TOP);
+            while (!iter.done() && !iter.fp()->isScriptFrame())
+                ++iter;
+            uint32 format = cs->format;
+            if (((!iter.done() && pc == iter.pc()) ||
                  (pc == startpc && nuses != 0)) &&
                 format & (JOF_SET|JOF_DEL|JOF_INCDEC|JOF_FOR|JOF_VARPROP)) {
-                mode = JOF_MODE(format);
+                uint32 mode = JOF_MODE(format);
                 if (mode == JOF_NAME) {
                     /*
                      * JOF_NAME does not imply JOF_ATOM, so we must check for
                      * the QARG and QVAR format types, and translate those to
                      * JSOP_GETARG or JSOP_GETLOCAL appropriately, instead of
                      * to JSOP_NAME.
                      */
-                    type = JOF_TYPE(format);
+                    uint32 type = JOF_TYPE(format);
                     op = (type == JOF_QARG)
                          ? JSOP_GETARG
                          : (type == JOF_LOCAL)
                          ? JSOP_GETLOCAL
                          : JSOP_NAME;
 
                     JS_ASSERT(js_CodeSpec[op].nuses >= 0);
                     i = nuses - js_CodeSpec[op].nuses;
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -244,17 +244,17 @@ Parser::newObjectBox(JSObject *obj)
      * We use JSContext.tempPool to allocate parsed objects and place them on
      * a list in this Parser to ensure GC safety. Thus the tempPool arenas
      * containing the entries must be alive until we are done with scanning,
      * parsing and code generation for the whole script or top-level function.
      */
     JSObjectBox *objbox;
     JS_ARENA_ALLOCATE_TYPE(objbox, JSObjectBox, &context->tempPool);
     if (!objbox) {
-        js_ReportOutOfScriptQuota(context);
+        js_ReportOutOfMemory(context);
         return NULL;
     }
     objbox->traceLink = traceListHead;
     traceListHead = objbox;
     objbox->emitLink = NULL;
     objbox->object = obj;
     objbox->isFunctionBox = false;
     return objbox;
@@ -270,17 +270,17 @@ Parser::newFunctionBox(JSObject *obj, JS
      * We use JSContext.tempPool to allocate parsed objects and place them on
      * a list in this Parser to ensure GC safety. Thus the tempPool arenas
      * containing the entries must be alive until we are done with scanning,
      * parsing and code generation for the whole script or top-level function.
      */
     JSFunctionBox *funbox;
     JS_ARENA_ALLOCATE_TYPE(funbox, JSFunctionBox, &context->tempPool);
     if (!funbox) {
-        js_ReportOutOfScriptQuota(context);
+        js_ReportOutOfMemory(context);
         return NULL;
     }
     funbox->traceLink = traceListHead;
     traceListHead = funbox;
     funbox->emitLink = NULL;
     funbox->object = obj;
     funbox->isFunctionBox = true;
     funbox->node = fn;
@@ -671,17 +671,17 @@ NewOrRecycledNode(JSTreeContext *tc)
     JSParseNode *pn;
 
     pn = tc->parser->nodeList;
     if (!pn) {
         JSContext *cx = tc->parser->context;
 
         JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
         if (!pn)
-            js_ReportOutOfScriptQuota(cx);
+            js_ReportOutOfMemory(cx);
     } else {
         tc->parser->nodeList = pn->pn_next;
     }
 
     if (pn) {
 #ifdef METER_PARSENODES
         parsenodes++;
         if (parsenodes - recyclednodes > maxparsenodes)
@@ -915,20 +915,18 @@ Compiler::compileScript(JSContext *cx, J
      */
     JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
     JS_ASSERT_IF(staticLevel != 0, callerFrame);
 
     Compiler compiler(cx, principals, callerFrame);
     if (!compiler.init(chars, length, filename, lineno, version))
         return NULL;
 
-    JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
-                     &cx->scriptStackQuota);
-    JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
-                     &cx->scriptStackQuota);
+    JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
+    JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
 
     Parser &parser = compiler.parser;
     TokenStream &tokenStream = parser.tokenStream;
 
     JSCodeGenerator cg(&parser, &codePool, &notePool, tokenStream.getLineno());
     if (!cg.init())
         return NULL;
 
@@ -1815,20 +1813,18 @@ Compiler::compileFunctionBody(JSContext 
 {
     Compiler compiler(cx, principals);
 
     if (!compiler.init(chars, length, filename, lineno, version))
         return false;
 
     /* No early return from after here until the js_FinishArenaPool calls. */
     JSArenaPool codePool, notePool;
-    JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode),
-                     &cx->scriptStackQuota);
-    JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote),
-                     &cx->scriptStackQuota);
+    JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode));
+    JS_InitArenaPool(&notePool, "note", 1024, sizeof(jssrcnote));
 
     Parser &parser = compiler.parser;
     TokenStream &tokenStream = parser.tokenStream;
 
     JSCodeGenerator funcg(&parser, &codePool, &notePool, tokenStream.getLineno());
     if (!funcg.init())
         return false;
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -85,16 +85,17 @@ js_GenerateShape(JSRuntime *rt)
          * have a chance to wrap around shapeGen to zero.
          */
         rt->shapeGen = SHAPE_OVERFLOW_BIT;
         shape = SHAPE_OVERFLOW_BIT;
 
 #ifdef JS_THREADSAFE
         AutoLockGC lockIf(rt);
 #endif
+        GCREASON(SHAPE);
         TriggerGC(rt);
     }
     return shape;
 }
 
 uint32
 js_GenerateShape(JSContext *cx)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -177,17 +177,17 @@ Bindings::getLocalNameArray(JSContext *c
    JS_ASSERT(hasLocalNames());
 
     uintN n = countLocalNames();
     jsuword *names;
 
     JS_ASSERT(SIZE_MAX / size_t(n) > sizeof *names);
     JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, size_t(n) * sizeof *names);
     if (!names) {
-        js_ReportOutOfScriptQuota(cx);
+        js_ReportOutOfMemory(cx);
         return NULL;
     }
 
 #ifdef DEBUG
     for (uintN i = 0; i != n; i++)
         names[i] = 0xdeadbeef;
 #endif
 
@@ -385,17 +385,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
          * names (indexes starting from nargs) bitmap's bit is set when the
          * name is declared as const, not as ordinary var.
          * */
         uintN bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32);
         uint32 *bitmap;
         JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &cx->tempPool,
                                bitmapLength * sizeof *bitmap);
         if (!bitmap) {
-            js_ReportOutOfScriptQuota(cx);
+            js_ReportOutOfMemory(cx);
             return false;
         }
 
         jsuword *names;
         if (xdr->mode == JSXDR_ENCODE) {
             names = script->bindings.getLocalNameArray(cx, &cx->tempPool);
             if (!names)
                 return false;
@@ -1706,20 +1706,19 @@ js_GetSrcNoteCached(JSContext *cx, JSScr
             GSN_CACHE_METER(cache, fills);
         }
     }
 
     return result;
 }
 
 uintN
-js_FramePCToLineNumber(JSContext *cx, StackFrame *fp)
+js_FramePCToLineNumber(JSContext *cx, StackFrame *fp, jsbytecode *pc)
 {
-    return js_PCToLineNumber(cx, fp->script(),
-                             fp->hasImacropc() ? fp->imacropc() : fp->pc(cx));
+    return js_PCToLineNumber(cx, fp->script(), fp->hasImacropc() ? fp->imacropc() : pc);
 }
 
 uintN
 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
 {
     JSOp op;
     JSFunction *fun;
     uintN lineno;
@@ -1824,29 +1823,42 @@ js_GetScriptLineExtent(JSScript *script)
             lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
         } else if (type == SRC_NEWLINE) {
             lineno++;
         }
     }
     return 1 + lineno - script->lineno;
 }
 
-const char *
-js::CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop)
+namespace js {
+
+uintN
+CurrentLine(JSContext *cx)
 {
-    StackFrame *fp = js_GetScriptedCaller(cx, NULL);
-    if (!fp) {
+    return js_FramePCToLineNumber(cx, cx->fp(), cx->regs().pc);
+}
+
+const char *
+CurrentScriptFileAndLineSlow(JSContext *cx, uintN *linenop)
+{
+    FrameRegsIter iter(cx, FRAME_EXPAND_TOP);
+    while (!iter.done() && !iter.fp()->isScriptFrame())
+        ++iter;
+
+    if (iter.done()) {
         *linenop = 0;
         return NULL;
     }
 
-    *linenop = js_FramePCToLineNumber(cx, fp);
-    return fp->script()->filename;
+    *linenop = js_FramePCToLineNumber(cx, iter.fp(), iter.pc());
+    return iter.fp()->script()->filename;
 }
 
+}  /* namespace js */
+
 class DisablePrincipalsTranscoding {
     JSSecurityCallbacks *callbacks;
     JSPrincipalsTranscoder temp;
 
   public:
     DisablePrincipalsTranscoding(JSContext *cx)
       : callbacks(JS_GetRuntimeSecurityCallbacks(cx->runtime)),
         temp(NULL)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -802,29 +802,32 @@ extern jssrcnote *
 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 /*
  * NOTE: use js_FramePCToLineNumber(cx, fp) when you have an active fp, in
  * preference to js_PCToLineNumber (cx, fp->script  fp->regs->pc), because
  * fp->imacpc may be non-null, indicating an active imacro.
  */
 extern uintN
-js_FramePCToLineNumber(JSContext *cx, js::StackFrame *fp);
+js_FramePCToLineNumber(JSContext *cx, js::StackFrame *fp, jsbytecode *pc);
 
 extern uintN
 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
 
 extern jsbytecode *
 js_LineNumberToPC(JSScript *script, uintN lineno);
 
 extern JS_FRIEND_API(uintN)
 js_GetScriptLineExtent(JSScript *script);
 
 namespace js {
 
+extern uintN
+CurrentLine(JSContext *cx);
+
 /*
  * This function returns the file and line number of the script currently
  * executing on cx. If there is no current script executing on cx (e.g., a
  * native called directly through JSAPI (e.g., by setTimeout)), NULL and 0 are
  * returned as the file and line. Additionally, this function avoids the full
  * linear scan to compute line number when the caller guarnatees that the
  * script compilation occurs at a JSOP_EVAL.
  */
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -379,16 +379,24 @@ ValueToTypeChar(const Value &v)
     if (v.isBoolean()) return 'B';
     if (v.isNull()) return 'N';
     if (v.isUndefined()) return 'U';
     if (v.isMagic()) return 'M';
     return '?';
 }
 #endif
 
+static inline uintN
+CurrentPCOffset(JSContext *cx)
+{
+    StackFrame *fp = cx->fp();
+    jsbytecode *pc = fp->hasImacropc() ? fp->imacropc() : cx->regs().pc;
+    return uintN(pc - fp->script()->code);
+}
+
 
 /* Blacklist parameters. */
 
 /*
  * Number of iterations of a loop where we start tracing.  That is, we don't
  * start tracing until the beginning of the HOTLOOP-th iteration.
  */
 #define HOTLOOP 8
@@ -1632,18 +1640,18 @@ TreeFragment::initialize(JSContext* cx, 
 
     /* Capture the coerced type of each active slot in the type map. */
     this->typeMap.captureTypes(cx, globalObj, *globalSlots, 0 /* callDepth */, speculate);
     this->nStackTypes = this->typeMap.length() - globalSlots->length();
     this->spOffsetAtEntry = cx->regs().sp - cx->fp()->base();
 
 #ifdef DEBUG
     this->treeFileName = cx->fp()->script()->filename;
-    this->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp());
-    this->treePCOffset = FramePCOffset(cx, cx->fp());
+    this->treeLineNumber = CurrentLine(cx);
+    this->treePCOffset = CurrentPCOffset(cx);
 #endif
     this->script = cx->fp()->script();
     this->gcthings.clear();
     this->shapes.clear();
     this->unstableExits = NULL;
     this->sideExits.clear();
 
     /* Determine the native frame layout at the entry point. */
@@ -2533,18 +2541,18 @@ TraceRecorder::finishAbort(const char* r
     AUDIT(recorderAborted);
 #ifdef DEBUG
     debug_only_printf(LC_TMMinimal | LC_TMAbort,
                       "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n",
                       tree->treeFileName,
                       tree->treeLineNumber,
                       tree->treePCOffset,
                       cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()),
+                      CurrentLine(cx),
+                      CurrentPCOffset(cx),
                       reason);
 #endif
     Backoff(traceMonitor, (jsbytecode*) fragment->root->ip, fragment->root);
 
     /*
      * If this is the primary trace and we didn't succeed compiling, trash the
      * tree. Otherwise, remove the VMSideExits we added while recording, which
      * are about to be invalid.
@@ -4177,17 +4185,17 @@ public:
 
 #if defined JS_JIT_SPEW
 JS_REQUIRES_STACK static void
 TreevisLogExit(JSContext* cx, VMSideExit* exit)
 {
     debug_only_printf(LC_TMTreeVis, "TREEVIS ADDEXIT EXIT=%p TYPE=%s FRAG=%p PC=%p FILE=\"%s\""
                       " LINE=%d OFFS=%d", (void*)exit, getExitName(exit->exitType),
                       (void*)exit->from, (void*)cx->regs().pc, cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()), FramePCOffset(cx, cx->fp()));
+                      CurrentLine(cx), CurrentPCOffset(cx));
     debug_only_print0(LC_TMTreeVis, " STACK=\"");
     for (unsigned i = 0; i < exit->numStackSlots; i++)
         debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->stackTypeMap()[i]));
     debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\"");
     for (unsigned i = 0; i < exit->numGlobalSlots; i++)
         debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(exit->globalTypeMap()[i]));
     debug_only_print0(LC_TMTreeVis, "\"\n");
 }
@@ -4531,18 +4539,17 @@ TraceRecorder::compile()
         return ARECORD_STOP;
 
     /* :TODO: windows support */
 #if defined DEBUG && !defined WIN32
     /* Associate a filename and line number with the fragment. */
     const char* filename = cx->fp()->script()->filename;
     char* label = (char*) cx->malloc_((filename ? strlen(filename) : 7) + 16);
     if (label) {
-        sprintf(label, "%s:%u", filename ? filename : "<stdin>",
-                js_FramePCToLineNumber(cx, cx->fp()));
+        sprintf(label, "%s:%u", filename ? filename : "<stdin>", CurrentLine(cx));
         lirbuf->printer->addrNameMap->addAddrRange(fragment, sizeof(Fragment), 0, label);
         cx->free_(label);
     }
 #endif
 
     Assembler *assm = traceMonitor->assembler;
     JS_ASSERT(!assm->error());
     assm->compile(fragment, tempAlloc(), /*optimize*/true verbose_only(, lirbuf->printer));
@@ -5010,18 +5017,18 @@ TraceRecorder::closeLoop()
      * should try to compile the outer tree again.
      */
     if (outerPC)
         AttemptCompilation(traceMonitor, globalObj, outerScript, outerPC, outerArgc);
 #ifdef JS_JIT_SPEW
     debug_only_printf(LC_TMMinimal,
                       "Recording completed at  %s:%u@%u via closeLoop (FragID=%06u)\n",
                       cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()),
+                      CurrentLine(cx),
+                      CurrentPCOffset(cx),
                       fragment->profFragID);
     debug_only_print0(LC_TMMinimal, "\n");
 #endif
 
     return finishSuccessfully();
 }
 
 static void
@@ -5178,18 +5185,18 @@ TraceRecorder::endLoop(VMSideExit* exit)
      * yet, we should try to compile the outer tree again.
      */
     if (outerPC)
         AttemptCompilation(traceMonitor, globalObj, outerScript, outerPC, outerArgc);
 #ifdef JS_JIT_SPEW
     debug_only_printf(LC_TMMinimal,
                       "Recording completed at  %s:%u@%u via endLoop (FragID=%06u)\n",
                       cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()),
+                      CurrentLine(cx),
+                      CurrentPCOffset(cx),
                       fragment->profFragID);
     debug_only_print0(LC_TMTracer, "\n");
 #endif
 
     return finishSuccessfully();
 }
 
 /* Emit code to adjust the stack to match the inner tree's stack expectations. */
@@ -5743,18 +5750,17 @@ RecordTree(JSContext* cx, TraceMonitor* 
 
     f->initialize(cx, globalSlots, speculate);
 
 #ifdef DEBUG
     AssertTreeIsUnique(tm, f);
 #endif
 #ifdef JS_JIT_SPEW
     debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d",
-                      (void*)f, f->ip, f->treeFileName, f->treeLineNumber,
-                      FramePCOffset(cx, cx->fp()));
+                      (void*)f, f->ip, f->treeFileName, f->treeLineNumber, CurrentPCOffset(cx));
     debug_only_print0(LC_TMTreeVis, " STACK=\"");
     for (unsigned i = 0; i < f->nStackTypes; i++)
         debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[i]));
     debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\"");
     for (unsigned i = 0; i < f->nGlobalTypes(); i++)
         debug_only_printf(LC_TMTreeVis, "%c", TypeToChar(f->typeMap[f->nStackTypes + i]));
     debug_only_print0(LC_TMTreeVis, "\"\n");
 #endif
@@ -5866,18 +5872,17 @@ CreateBranchFragment(JSContext* cx, Trac
                           ? (++(tm->lastFragID)) : 0;
     )
 
     VMFragment* f = new (*tm->dataAlloc) VMFragment(cx->regs().pc verbose_only(, profFragID));
 
     debug_only_printf(LC_TMTreeVis, "TREEVIS CREATEBRANCH ROOT=%p FRAG=%p PC=%p FILE=\"%s\""
                       " LINE=%d ANCHOR=%p OFFS=%d\n",
                       (void*)root, (void*)f, (void*)cx->regs().pc, cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()), (void*)anchor,
-                      FramePCOffset(cx, cx->fp()));
+                      CurrentLine(cx), (void*)anchor, CurrentPCOffset(cx));
     verbose_only( tm->branches = new (*tm->dataAlloc) Seq<Fragment*>(f, tm->branches); )
 
     f->root = root;
     if (anchor)
         anchor->target = f;
     return f;
 }
 
@@ -5980,31 +5985,31 @@ AttemptToExtendTree(JSContext* cx, Trace
     }
 #ifdef MOZ_TRACEVIS
     if (tvso) tvso->r = R_FAIL_EXTEND_COLD;
 #endif
     return false;
 }
 
 static JS_REQUIRES_STACK bool
-ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount,
+ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
             VMSideExit** innermostNestedGuardp, VMSideExit** lrp);
 
 static inline MonitorResult
 RecordingIfTrue(bool b)
 {
     return b ? MONITOR_RECORDING : MONITOR_NOT_RECORDING;
 }
 
 /*
  * A postcondition of recordLoopEdge is that if recordLoopEdge does not return
  * MONITOR_RECORDING, the recording has been aborted.
  */
 JS_REQUIRES_STACK MonitorResult
-TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r, uintN& inlineCallCount)
+TraceRecorder::recordLoopEdge(JSContext* cx, TraceRecorder* r)
 {
     TraceMonitor* tm = r->traceMonitor;
 
     /* Process needFlush and deep abort requests. */
     if (tm->needFlush) {
         ResetJIT(cx, tm, FR_DEEP_BAIL);
         return MONITOR_NOT_RECORDING;
     }
@@ -6024,18 +6029,18 @@ TraceRecorder::recordLoopEdge(JSContext*
     if (!CheckGlobalObjectShape(cx, tm, globalObj, &globalShape, &globalSlots)) {
         JS_ASSERT(!tm->recorder);
         return MONITOR_NOT_RECORDING;
     }
 
     debug_only_printf(LC_TMTracer,
                       "Looking for type-compatible peer (%s:%d@%d)\n",
                       cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()));
+                      CurrentLine(cx),
+                      CurrentPCOffset(cx));
 
     // Find a matching inner tree. If none can be found, compile one.
     TreeFragment* f = r->findNestedCompatiblePeer(first);
     if (!f || !f->code()) {
         AUDIT(noCompatInnerTrees);
 
         TreeFragment* outerFragment = root;
         JSScript* outerScript = outerFragment->script;
@@ -6045,38 +6050,34 @@ TraceRecorder::recordLoopEdge(JSContext*
 
         if (AbortRecording(cx, "No compatible inner tree") == JIT_RESET)
             return MONITOR_NOT_RECORDING;
 
         return RecordingIfTrue(RecordTree(cx, tm, first,
                                           outerScript, outerPC, outerArgc, globalSlots));
     }
 
-    AbortableRecordingStatus status = r->attemptTreeCall(f, inlineCallCount);
+    AbortableRecordingStatus status = r->attemptTreeCall(f);
     if (status == ARECORD_CONTINUE)
         return MONITOR_RECORDING;
     if (status == ARECORD_ERROR) {
         if (tm->recorder)
             AbortRecording(cx, "Error returned while recording loop edge");
         return MONITOR_ERROR;
     }
     JS_ASSERT(status == ARECORD_ABORTED && !tm->recorder);
     return MONITOR_NOT_RECORDING;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount)
+TraceRecorder::attemptTreeCall(TreeFragment* f)
 {
     adjustCallerTypes(f);
     prepareTreeCall(f);
 
-#ifdef DEBUG
-    uintN oldInlineCallCount = inlineCallCount;
-#endif
-
     JSContext *localCx = cx;
     TraceMonitor *localtm = traceMonitor;
 
     // Refresh the import type map so the tracker can reimport values after the
     // call with their correct types. The inner tree must not change the type of
     // any variable in a frame above the current one (i.e., upvars).
     //
     // Note that DetermineTypesVisitor may call determineSlotType, which may
@@ -6085,17 +6086,17 @@ TraceRecorder::attemptTreeCall(TreeFragm
     // if there is not a tracker instruction for that value, which means that
     // value has not been written yet, so that type map entry is up to date.
     importTypeMap.setLength(NativeStackSlots(cx, callDepth));
     DetermineTypesVisitor visitor(*this, importTypeMap.data());
     VisitStackSlots(visitor, cx, callDepth);
 
     VMSideExit* innermostNestedGuard = NULL;
     VMSideExit* lr;
-    bool ok = ExecuteTree(cx, traceMonitor, f, inlineCallCount, &innermostNestedGuard, &lr);
+    bool ok = ExecuteTree(cx, traceMonitor, f, &innermostNestedGuard, &lr);
 
     /*
      * If ExecuteTree reentered the interpreter, it may have killed |this|
      * and/or caused an error, which must be propagated.
      */
     JS_ASSERT_IF(localtm->recorder, localtm->recorder == this);
     if (!ok)
         return ARECORD_ERROR;
@@ -6119,18 +6120,16 @@ TraceRecorder::attemptTreeCall(TreeFragm
                 return ARECORD_ABORTED;
             }
             return AttemptToExtendTree(localCx, localtm,
                                        innermostNestedGuard, lr, outerScript, outerPC)
                    ? ARECORD_CONTINUE
                    : ARECORD_ABORTED;
         }
 
-        JS_ASSERT(oldInlineCallCount == inlineCallCount);
-
         /* Emit a call to the inner tree and continue recording the outer tree trace. */
         emitTreeCall(f, lr);
         return ARECORD_CONTINUE;
 
       case UNSTABLE_LOOP_EXIT:
       {
         /* Abort recording so the inner loop can become type stable. */
         JSObject* _globalObj = globalObj;
@@ -6426,32 +6425,30 @@ FindVMCompatiblePeer(JSContext* cx, JSOb
 /*
  * For the native stacks and global frame, reuse the storage in |tm->storage|.
  * This reuse depends on the invariant that only one trace uses |tm->storage|
  * at a time. This is subtly correct in case of deep bail; see the comment
  * about "clobbering deep bails" in DeepBail.
  */
 JS_ALWAYS_INLINE
 TracerState::TracerState(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
-                         uintN& inlineCallCount, VMSideExit** innermostNestedGuardp)
+                         VMSideExit** innermostNestedGuardp)
   : cx(cx),
     traceMonitor(tm),
     stackBase(tm->storage->stack()),
     sp(stackBase + f->nativeStackBase / sizeof(double)),
     eos(tm->storage->global()),
     callstackBase(tm->storage->callstack()),
     sor(callstackBase),
     rp(callstackBase),
-    eor(callstackBase + JS_MIN(TraceNativeStorage::MAX_CALL_STACK_ENTRIES,
-                               StackSpace::MAX_INLINE_CALLS - inlineCallCount)),
+    eor(callstackBase + TraceNativeStorage::MAX_CALL_STACK_ENTRIES),
     lastTreeExitGuard(NULL),
     lastTreeCallGuard(NULL),
     rpAtLastTreeCall(NULL),
     outermostTree(f),
-    inlineCallCountp(&inlineCallCount),
     innermostNestedGuardp(innermostNestedGuardp),
 #ifdef EXECUTE_TREE_TIMER
     startTime(rdtsc()),
 #endif
     builtinStatus(0),
     nativeVp(NULL)
 {
     JS_ASSERT(!tm->tracecx);
@@ -6468,23 +6465,16 @@ TracerState::TracerState(JSContext* cx, 
     JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL ||
               JS_THREAD_DATA(cx)->recordingCompartment == cx->compartment);
     JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL);
     JS_THREAD_DATA(cx)->onTraceCompartment = cx->compartment;
 
     JS_ASSERT(eos == stackBase + TraceNativeStorage::MAX_NATIVE_STACK_SLOTS);
     JS_ASSERT(sp < eos);
 
-    /*
-     * inlineCallCount has already been incremented, if being invoked from
-     * EnterFrame. It is okay to have a 0-frame restriction since the JIT
-     * might not need any frames.
-     */
-    JS_ASSERT(inlineCallCount <= StackSpace::MAX_INLINE_CALLS);
-
 #ifdef DEBUG
     /*
      * Cannot 0xCD-fill global frame since it may overwrite a bailed outer
      * ExecuteTree's 0xdeadbeefdeadbeef marker.
      */
     memset(tm->storage->stack(), 0xCD, TraceNativeStorage::MAX_NATIVE_STACK_SLOTS * sizeof(double));
     memset(tm->storage->callstack(), 0xCD, TraceNativeStorage::MAX_CALL_STACK_ENTRIES * sizeof(FrameInfo*));
 #endif
@@ -6575,52 +6565,51 @@ enum LEAVE_TREE_STATUS {
   DEEP_BAILED = 1
 };
 
 static LEAVE_TREE_STATUS
 LeaveTree(TraceMonitor *tm, TracerState&, VMSideExit *lr);
 
 /* Return false if the interpreter should goto error. */
 static JS_REQUIRES_STACK bool
-ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount,
+ExecuteTree(JSContext* cx, TraceMonitor* tm, TreeFragment* f,
             VMSideExit** innermostNestedGuardp, VMSideExit **lrp)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_EXECUTE);
 #endif
     JS_ASSERT(f->root == f && f->code());
 
-    if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace() ||
-        inlineCallCount + f->maxCallDepth > StackSpace::MAX_INLINE_CALLS) {
+    if (!ScopeChainCheck(cx, f) || !cx->stack.space().ensureEnoughSpaceToEnterTrace()) {
         *lrp = NULL;
         return true;
     }
 
     /* Make sure the global object is sane. */
     JS_ASSERT(f->globalObj->numSlots() <= MAX_GLOBAL_SLOTS);
     JS_ASSERT(f->nGlobalTypes() == f->globalSlots->length());
     JS_ASSERT_IF(f->globalSlots->length() != 0,
                  f->globalObj->shape() == f->globalShape);
 
     /* Initialize trace state. */
-    TracerState state(cx, tm, f, inlineCallCount, innermostNestedGuardp);
+    TracerState state(cx, tm, f, innermostNestedGuardp);
     double* stack = tm->storage->stack();
     double* global = tm->storage->global();
     JSObject* globalObj = f->globalObj;
     unsigned ngslots = f->globalSlots->length();
     uint16* gslots = f->globalSlots->data();
 
     BuildNativeFrame(cx, globalObj, 0 /* callDepth */, ngslots, gslots,
                      f->typeMap.data(), global, stack);
 
     AUDIT(traceTriggered);
     debug_only_printf(LC_TMTracer, "entering trace at %s:%u@%u, execs: %u code: %p\n",
                       cx->fp()->script()->filename,
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()),
+                      CurrentLine(cx),
+                      CurrentPCOffset(cx),
            f->execs,
            (void *) f->code());
 
     debug_only_stmt(uint32 globalSlots = globalObj->numSlots();)
     debug_only_stmt(*(uint64*)&tm->storage->global()[globalSlots] = 0xdeadbeefdeadbeefLL;)
 
     /* Execute trace. */
     tm->iterationCounter = 0;
@@ -6654,17 +6643,17 @@ ExecuteTree(JSContext* cx, TraceMonitor*
     if (iters == LOOP_COUNT_MAX)
         prefix = ">";
     debug_only_printf(LC_TMMinimal, "  [%.3f ms] Tree at line %u executed for %s%u iterations;"
                       " executed %u times; leave for %s at %s:%u (%s)\n",
                       double(t1-t0) / PRMJ_USEC_PER_MSEC,
                       f->treeLineNumber, prefix, (uintN)iters, f->execs,
                       getExitName(lr->exitType),
                       fp->script()->filename,
-                      js_FramePCToLineNumber(cx, fp),
+                      CurrentLine(cx),
                       js_CodeName[fp->hasImacropc() ? *fp->imacropc() : *cx->regs().pc]);
 #endif
     
 #ifdef JS_METHODJIT
     if (cx->methodJitEnabled) {
         if (lr->exitType == LOOP_EXIT && f->iters < MIN_LOOP_ITERS
             && f->execs >= LOOP_CHECK_ITERS)
         {
@@ -6845,30 +6834,28 @@ LeaveTree(TraceMonitor *tm, TracerState&
          * to its correct value.
          */
         cx->regs().sp = cx->fp()->slots() + (fi->spdist - (2 + fi->get_argc()));
         int slots = FlushNativeStackFrame(cx, 0 /* callDepth */, fi->get_typemap(), stack);
 
         /* Finish initializing cx->fp() and push a new cx->fp(). */
         SynthesizeFrame(cx, *fi, callee);
 #ifdef DEBUG
-        StackFrame* fp = cx->fp();
         debug_only_printf(LC_TMTracer,
                           "synthesized deep frame for %s:%u@%u, slots=%d, fi=%p\n",
-                          fp->script()->filename,
-                          js_FramePCToLineNumber(cx, fp),
-                          FramePCOffset(cx, fp),
+                          cx->fp()->script()->filename,
+                          CurrentLine(cx),
+                          CurrentPCOffset(cx),
                           slots,
                           (void*)*callstack);
 #endif
         /*
          * Keep track of the additional frames we put on the interpreter stack
          * and the native stack slots we consumed.
          */
-        ++*state.inlineCallCountp;
         ++callstack;
         stack += slots;
     }
 
     /*
      * We already synthesized the frames around the innermost guard. Here we
      * just deal with additional frames inside the tree we are bailing out
      * from.
@@ -6878,23 +6865,22 @@ LeaveTree(TraceMonitor *tm, TracerState&
     unsigned calleeOffset = 0;
     for (unsigned n = 0; n < calldepth; ++n) {
         /* Peek at the callee native slot in the not-yet-synthesized prev frame. */
         calleeOffset += callstack[n]->callerHeight;
         JSObject* callee = *(JSObject**)&stack[calleeOffset];
 
         /* Reconstruct the frame. */
         SynthesizeFrame(cx, *callstack[n], callee);
-        ++*state.inlineCallCountp;
 #ifdef DEBUG
-        StackFrame* fp = cx->fp();
         debug_only_printf(LC_TMTracer,
                           "synthesized shallow frame for %s:%u@%u\n",
-                          fp->script()->filename, js_FramePCToLineNumber(cx, fp),
-                          FramePCOffset(cx, fp));
+                          cx->fp()->script()->filename,
+                          CurrentLine(cx),
+                          CurrentPCOffset(cx));
 #endif
     }
 
     /*
      * Adjust sp and pc relative to the tree we exited from (not the tree we
      * entered into).  These are our final values for sp and pc since
      * SynthesizeFrame has already taken care of all frames in between.
      */
@@ -6937,18 +6923,18 @@ LeaveTree(TraceMonitor *tm, TracerState&
     uint64 cycles = rdtsc() - state.startTime;
 #elif defined(JS_JIT_SPEW)
     uint64 cycles = 0;
 #endif
     debug_only_printf(LC_TMTracer,
                       "leaving trace at %s:%u@%u, op=%s, lr=%p, exitType=%s, sp=%lld, "
                       "calldepth=%d, cycles=%llu\n",
                       fp->script()->filename,
-                      js_FramePCToLineNumber(cx, fp),
-                      FramePCOffset(cx, fp),
+                      CurrentLine(cx),
+                      CurrentPCOffset(cx),
                       js_CodeName[fp->hasImacropc() ? *fp->imacropc() : *cx->regs().pc],
                       (void*)lr,
                       getExitName(lr->exitType),
                       (long long int)(cx->regs().sp - fp->base()),
                       calldepth,
                       (unsigned long long int)cycles);
 
     DebugOnly<int> slots = FlushNativeStackFrame(cx, innermost->calldepth, innermost->stackTypeMap(), stack);
@@ -7035,17 +7021,17 @@ TraceRecorder::assertInsideLoop()
      * immediately preceeding a loop (the one that jumps to the loop
      * condition).
      */
     JS_ASSERT(pc >= beg - JSOP_GOTO_LENGTH && pc <= end);
 #endif
 }
 
 JS_REQUIRES_STACK MonitorResult
-RecordLoopEdge(JSContext* cx, TraceMonitor* tm, uintN& inlineCallCount)
+RecordLoopEdge(JSContext* cx, TraceMonitor* tm)
 {
 #ifdef MOZ_TRACEVIS
     TraceVisStateObj tvso(cx, S_MONITOR);
 #endif
 
     /* Is the recorder currently active? */
     if (tm->recorder) {
         tm->recorder->assertInsideLoop();
@@ -7053,17 +7039,17 @@ RecordLoopEdge(JSContext* cx, TraceMonit
         if (pc == tm->recorder->tree->ip) {
             AbortableRecordingStatus status = tm->recorder->closeLoop();
             if (status != ARECORD_COMPLETED) {
                 if (tm->recorder)
                     AbortRecording(cx, "closeLoop failed");
                 return MONITOR_NOT_RECORDING;
             }
         } else {
-            MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount);
+            MonitorResult r = TraceRecorder::recordLoopEdge(cx, tm->recorder);
             JS_ASSERT((r == MONITOR_RECORDING) == (tm->recorder != NULL));
             if (r == MONITOR_RECORDING || r == MONITOR_ERROR)
                 return r;
 
             /*
              * recordLoopEdge will invoke an inner tree if we have a matching
              * one. If we arrive here, that tree didn't run to completion and
              * instead we mis-matched or the inner tree took a side exit other than
@@ -7140,18 +7126,17 @@ RecordLoopEdge(JSContext* cx, TraceMonit
         if (!rv)
             tvso.r = R_FAIL_RECORD_TREE;
 #endif
         return RecordingIfTrue(rv);
     }
 
     debug_only_printf(LC_TMTracer,
                       "Looking for compat peer %d@%d, from %p (ip: %p)\n",
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()), (void*)f, f->ip);
+                      CurrentLine(cx), CurrentPCOffset(cx), (void*)f, f->ip);
 
     uintN count;
     TreeFragment* match = FindVMCompatiblePeer(cx, globalObj, f, count);
     if (!match) {
         if (count < MAXPEERS)
             goto record;
 
         /*
@@ -7164,17 +7149,17 @@ RecordLoopEdge(JSContext* cx, TraceMonit
         tvso.r = R_MAX_PEERS;
 #endif
         return MONITOR_NOT_RECORDING;
     }
 
     VMSideExit* lr = NULL;
     VMSideExit* innermostNestedGuard = NULL;
 
-    if (!ExecuteTree(cx, tm, match, inlineCallCount, &innermostNestedGuard, &lr))
+    if (!ExecuteTree(cx, tm, match, &innermostNestedGuard, &lr))
         return MONITOR_ERROR;
 
     if (!lr) {
 #ifdef MOZ_TRACEVIS
         tvso.r = R_FAIL_EXECUTE_TREE;
 #endif
         return MONITOR_NOT_RECORDING;
     }
@@ -16673,18 +16658,17 @@ class AutoRetBlacklist
 
     ~AutoRetBlacklist()
     {
         *blacklist = IsBlacklisted(pc);
     }
 };
 
 JS_REQUIRES_STACK TracePointAction
-RecordTracePoint(JSContext* cx, TraceMonitor* tm,
-                 uintN& inlineCallCount, bool* blacklist, bool execAllowed)
+RecordTracePoint(JSContext* cx, TraceMonitor* tm, bool* blacklist, bool execAllowed)
 {
     StackFrame* fp = cx->fp();
     jsbytecode* pc = cx->regs().pc;
 
     JS_ASSERT(!tm->recorder);
     JS_ASSERT(!tm->profile);
 
     JSObject* globalObj = cx->fp()->scopeChain().getGlobal();
@@ -16698,34 +16682,33 @@ RecordTracePoint(JSContext* cx, TraceMon
         return TPA_Nothing;
     }
 
     uint32 argc = entryFrameArgc(cx);
     TreeFragment* tree = LookupOrAddLoop(tm, pc, globalObj, globalShape, argc);
 
     debug_only_printf(LC_TMTracer,
                       "Looking for compat peer %d@%d, from %p (ip: %p)\n",
-                      js_FramePCToLineNumber(cx, cx->fp()),
-                      FramePCOffset(cx, cx->fp()), (void*)tree, tree->ip);
+                      CurrentLine(cx), CurrentPCOffset(cx), (void*)tree, tree->ip);
 
     if (tree->code() || tree->peer) {
         uintN count;
         TreeFragment* match = FindVMCompatiblePeer(cx, globalObj, tree, count);
         if (match) {
             VMSideExit* lr = NULL;
             VMSideExit* innermostNestedGuard = NULL;
 
             if (!execAllowed) {
                 /* We've already compiled a trace for it, but we don't want to use that trace. */
                 Blacklist((jsbytecode*)tree->root->ip);
                 return TPA_Nothing;
             }
 
             /* Best case - just go and execute. */
-            if (!ExecuteTree(cx, tm, match, inlineCallCount, &innermostNestedGuard, &lr))
+            if (!ExecuteTree(cx, tm, match, &innermostNestedGuard, &lr))
                 return TPA_Error;
 
             if (!lr)
                 return TPA_Nothing;
 
             switch (lr->exitType) {
               case UNSTABLE_LOOP_EXIT:
                 if (!AttemptToStabilizeTree(cx, tm, globalObj, lr, NULL, NULL, 0))
@@ -16773,17 +16756,17 @@ RecordTracePoint(JSContext* cx, TraceMon
         return TPA_Nothing;
     if (!RecordTree(cx, tm, tree->first, NULL, NULL, 0, globalSlots))
         return TPA_Nothing;
 
   interpret:
     JS_ASSERT(tm->recorder);
 
     /* Locked and loaded with a recorder. Ask the interperter to go run some code. */
-    if (!Interpret(cx, fp, inlineCallCount, JSINTERP_RECORD))
+    if (!Interpret(cx, fp, JSINTERP_RECORD))
         return TPA_Error;
 
     JS_ASSERT(!cx->isExceptionPending());
     
     return TPA_RanStuff;
 }
 
 LoopProfile::LoopProfile(TraceMonitor *tm, StackFrame *entryfp,
@@ -16815,17 +16798,17 @@ LoopProfile::reset()
     loopStackDepth = 0;
     sp = 0;
 
     PodArrayZero(allOps);
     PodArrayZero(selfOps);
 }
 
 MonitorResult
-LoopProfile::profileLoopEdge(JSContext* cx, uintN& inlineCallCount)
+LoopProfile::profileLoopEdge(JSContext* cx)
 {
     if (cx->regs().pc == top) {
         debug_only_print0(LC_TMProfiler, "Profiling complete (edge)\n");
         decide(cx);
     } else {
         /* Record an inner loop invocation. */
         StackFrame *fp = cx->fp();
         jsbytecode *pc = cx->regs().pc;
@@ -16924,23 +16907,23 @@ LoopProfile::stopProfiling(JSContext *cx
 {
     JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL);
     JS_THREAD_DATA(cx)->profilingCompartment = NULL;
 
     traceMonitor->profile = NULL;
 }
 
 JS_REQUIRES_STACK TracePointAction
-MonitorTracePoint(JSContext *cx, uintN& inlineCallCount, bool* blacklist,
+MonitorTracePoint(JSContext *cx, bool* blacklist,
                   void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits)
 {
     TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
 
     if (!cx->profilingEnabled)
-        return RecordTracePoint(cx, tm, inlineCallCount, blacklist, true);
+        return RecordTracePoint(cx, tm, blacklist, true);
 
     *blacklist = false;
 
     /*
      * This is the only place where we check for re-entering the profiler.
      * The assumption is that MonitorTracePoint is the only place where we
      * start profiling. When we do so, we enter an interpreter frame with
      * JSINTERP_PROFILE mode. All other entry points to the profiler check
@@ -16960,32 +16943,31 @@ MonitorTracePoint(JSContext *cx, uintN& 
     prof->hits += hits;
     if (prof->hits < PROFILE_HOTLOOP)
         return TPA_Nothing;
 
     AutoRetBlacklist autoRetBlacklist(cx->regs().pc, blacklist);
 
     if (prof->profiled) {
         if (prof->traceOK) {
-            return RecordTracePoint(cx, tm, inlineCallCount, blacklist, prof->execOK);
+            return RecordTracePoint(cx, tm, blacklist, prof->execOK);
         } else {
             return TPA_Nothing;
         }
     }
 
-    debug_only_printf(LC_TMProfiler, "Profiling at line %d\n",
-                      js_FramePCToLineNumber(cx, cx->fp()));
+    debug_only_printf(LC_TMProfiler, "Profiling at line %d\n", CurrentLine(cx));
 
     tm->profile = prof;
 
     JS_ASSERT(JS_THREAD_DATA(cx)->profilingCompartment == NULL);
     JS_ASSERT(JS_THREAD_DATA(cx)->recordingCompartment == NULL);
     JS_THREAD_DATA(cx)->profilingCompartment = cx->compartment;
 
-    if (!Interpret(cx, cx->fp(), inlineCallCount, JSINTERP_PROFILE))
+    if (!Interpret(cx, cx->fp(), JSINTERP_PROFILE))
         return TPA_Error;
 
     JS_ASSERT(!cx->isExceptionPending());
 
     /* Look it up again since a reset may have happened during Interpret. */
     prof = LookupLoopProfile(tm, pc);
     if (prof && prof->undecided) {
         *loopCounter = 3000;
@@ -17022,17 +17004,17 @@ LoopProfile::profileOperation(JSContext*
     }
 
     jsbytecode *pc = cx->regs().pc;
     StackFrame *fp = cx->fp();
     JSScript *script = fp->script();
 
     if (!PCWithinLoop(fp, pc, *this)) {
         debug_only_printf(LC_TMProfiler, "Profiling complete (loop exit) at line %u\n",
-                          js_FramePCToLineNumber(cx, cx->fp()));
+                          CurrentLine(cx));
         tm->profile->decide(cx);
         stopProfiling(cx);
         return ProfComplete;
     }
 
     while (loopStackDepth > 0 && !PCWithinLoop(fp, pc, loopStack[loopStackDepth-1])) {
         debug_only_print0(LC_TMProfiler, "Profiler: Exiting inner loop\n");
         loopStackDepth--;
@@ -17043,17 +17025,17 @@ LoopProfile::profileOperation(JSContext*
             if (loopStackDepth == PROFILE_MAX_INNER_LOOPS) {
                 debug_only_print0(LC_TMProfiler, "Profiling complete (maxnest)\n");
                 tm->profile->decide(cx);
                 stopProfiling(cx);
                 return ProfComplete;
             }
 
             debug_only_printf(LC_TMProfiler, "Profiler: Entering inner loop at line %d\n",
-                              js_FramePCToLineNumber(cx, cx->fp()));
+                              CurrentLine(cx));
             loopStack[loopStackDepth++] = InnerLoop(fp, pc, GetLoopBottom(cx));
         }
     }
 
     numAllOps++;
     if (loopStackDepth == 0) {
         numSelfOps++;
         numSelfOpsMult += branchMultiplier;
@@ -17389,23 +17371,23 @@ LoopProfile::decide(JSContext *cx)
         debug_only_printf(LC_TMProfiler, "Blacklisting at %d\n", line);
         Blacklist(top);
     }
 
     debug_only_print0(LC_TMProfiler, "\n");
 }
 
 JS_REQUIRES_STACK MonitorResult
-MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode)
+MonitorLoopEdge(JSContext* cx, InterpMode interpMode)
 {
     TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
     if (interpMode == JSINTERP_PROFILE && tm->profile)
-        return tm->profile->profileLoopEdge(cx, inlineCallCount);
+        return tm->profile->profileLoopEdge(cx);
     else
-        return RecordLoopEdge(cx, tm, inlineCallCount);
+        return RecordLoopEdge(cx, tm);
 }
 
 void
 AbortProfiling(JSContext *cx)
 {
     JS_ASSERT(TRACE_PROFILER(cx));
     LoopProfile *prof = TRACE_PROFILER(cx);
     
@@ -17414,20 +17396,20 @@ AbortProfiling(JSContext *cx)
     prof->traceOK = false;
     prof->execOK = false;
     prof->stopProfiling(cx);
 }
 
 #else /* JS_METHODJIT */
 
 JS_REQUIRES_STACK MonitorResult
-MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode)
+MonitorLoopEdge(JSContext* cx, InterpMode interpMode)
 {
     TraceMonitor *tm = JS_TRACE_MONITOR_FROM_CONTEXT(cx);
-    return RecordLoopEdge(cx, tm, inlineCallCount);
+    return RecordLoopEdge(cx, tm);
 }
 
 #endif /* JS_METHODJIT */
 
 uint32
 GetHotloop(JSContext *cx)
 {
 #ifdef JS_METHODJIT
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -789,17 +789,17 @@ public:
         allOps[kind]++;
         if (loopStackDepth == 0)
             selfOps[kind]++;
     }
 
     inline uintN count(OpKind kind) { return allOps[kind]; }
 
     /* Called for every back edge being profiled. */
-    MonitorResult profileLoopEdge(JSContext* cx, uintN& inlineCallCount);
+    MonitorResult profileLoopEdge(JSContext* cx);
     
     /* Called for every instruction being profiled. */
     ProfileAction profileOperation(JSContext *cx, JSOp op);
 
     /* Once a loop's profile is done, these decide whether it should be traced. */
     bool isCompilationExpensive(JSContext *cx, uintN depth);
     bool isCompilationUnprofitable(JSContext *cx, uintN goodOps);
     void decide(JSContext *cx);
@@ -1534,21 +1534,19 @@ class TraceRecorder
     JS_REQUIRES_STACK AbortableRecordingStatus endLoop(VMSideExit* exit);
     JS_REQUIRES_STACK bool joinEdgesToEntry(TreeFragment* peer_root);
     JS_REQUIRES_STACK void adjustCallerTypes(TreeFragment* f);
     JS_REQUIRES_STACK void prepareTreeCall(TreeFragment* inner);
     JS_REQUIRES_STACK void emitTreeCall(TreeFragment* inner, VMSideExit* exit);
     JS_REQUIRES_STACK void determineGlobalTypes(JSValueType* typeMap);
     JS_REQUIRES_STACK VMSideExit* downSnapshot(FrameInfo* downFrame);
     JS_REQUIRES_STACK TreeFragment* findNestedCompatiblePeer(TreeFragment* f);
-    JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner,
-                                                               uintN& inlineCallCount);
+    JS_REQUIRES_STACK AbortableRecordingStatus attemptTreeCall(TreeFragment* inner);
 
-    static JS_REQUIRES_STACK MonitorResult recordLoopEdge(JSContext* cx, TraceRecorder* r,
-                                                          uintN& inlineCallCount);
+    static JS_REQUIRES_STACK MonitorResult recordLoopEdge(JSContext* cx, TraceRecorder* r);
 
     /* Allocators associated with this recording session. */
     VMAllocator& tempAlloc() const { return *traceMonitor->tempAlloc; }
     VMAllocator& traceAlloc() const { return *traceMonitor->traceAlloc; }
     VMAllocator& dataAlloc() const { return *traceMonitor->dataAlloc; }
 
     /* Member declarations for each opcode, to be called before interpreting the opcode. */
 #define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format)               \
@@ -1592,19 +1590,18 @@ class TraceRecorder
 
     friend class ImportBoxedStackSlotVisitor;
     friend class AdjustCallerGlobalTypesVisitor;
     friend class AdjustCallerStackTypesVisitor;
     friend class TypeCompatibilityVisitor;
     friend class SlotMap;
     friend class DefaultSlotMap;
     friend class DetermineTypesVisitor;
-    friend MonitorResult RecordLoopEdge(JSContext*, TraceMonitor*, uintN&);
-    friend TracePointAction RecordTracePoint(JSContext*, TraceMonitor*, uintN &inlineCallCount,
-                                             bool *blacklist);
+    friend MonitorResult RecordLoopEdge(JSContext*, TraceMonitor*);
+    friend TracePointAction RecordTracePoint(JSContext*, TraceMonitor*, bool *blacklist);
     friend AbortResult AbortRecording(JSContext*, const char*);
     friend class BoxArg;
     friend void TraceMonitor::sweep(JSContext *cx);
 
   public:
     static bool JS_REQUIRES_STACK
     startRecorder(JSContext*, TraceMonitor *, VMSideExit*, VMFragment*,
                   unsigned stackSlots, unsigned ngslots, JSValueType* typeMap,
@@ -1681,24 +1678,24 @@ class TraceRecorder
     JS_END_MACRO
 
 #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 MonitorResult
-MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, InterpMode interpMode);
+MonitorLoopEdge(JSContext* cx, InterpMode interpMode);
 
 extern JS_REQUIRES_STACK TracePointAction
-RecordTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist);
+RecordTracePoint(JSContext*, bool* blacklist);
 
 extern JS_REQUIRES_STACK TracePointAction
-MonitorTracePoint(JSContext*, uintN& inlineCallCount, bool* blacklist,
-                  void** traceData, uintN *traceEpoch, uint32 *loopCounter, uint32 hits);
+MonitorTracePoint(JSContext*, bool* blacklist, void** traceData, uintN *traceEpoch,
+                  uint32 *loopCounter, uint32 hits);
 
 extern JS_REQUIRES_STACK TraceRecorder::AbortResult
 AbortRecording(JSContext* cx, const char* reason);
 
 extern void
 InitJIT();
 
 extern void
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1733,27 +1733,26 @@ ParseXMLSource(JSContext *cx, JSString *
     js_strncpy(chars + offset, srcp, srclen);
     offset += srclen;
     dstlen = length - offset + 1;
     InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset, &dstlen);
     chars [offset + dstlen] = 0;
 
     LeaveTrace(cx);
     xml = NULL;
-    FrameRegsIter i(cx);
+    FrameRegsIter i(cx, FRAME_EXPAND_TOP);
     for (; !i.done() && !i.pc(); ++i)
         JS_ASSERT(!i.fp()->isScriptFrame());
     filename = NULL;
     lineno = 1;
     if (!i.done()) {
-        StackFrame *fp = i.fp();
         op = (JSOp) *i.pc();
         if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
-            filename = fp->script()->filename;
-            lineno = js_FramePCToLineNumber(cx, fp);
+            filename = i.fp()->script()->filename;
+            lineno = js_FramePCToLineNumber(cx, i.fp(), i.pc());
             for (endp = srcp + srclen; srcp < endp; srcp++) {
                 if (*srcp == '\n')
                     --lineno;
             }
         }
     }
 
     {
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -208,27 +208,32 @@ static inline void
 RemovePartialFrame(JSContext *cx, StackFrame *fp)
 {
     cx->stack.popInlineFrame();
 }
 
 static inline bool
 CheckStackQuota(VMFrame &f)
 {
-    uint32 nvals = VALUES_PER_STACK_FRAME + f.fp()->script()->nslots + StackSpace::STACK_JIT_EXTRA;
-    if ((Value *)f.fp() + nvals >= f.stackLimit) {
-        StackSpace &space = f.cx->stack.space();
-        if (!space.bumpLimitWithinQuota(NULL, f.entryfp, f.regs.sp, nvals, &f.stackLimit)) {
-            /* Remove the current partially-constructed frame before throwing. */
-            RemovePartialFrame(f.cx, f.fp());
-            js_ReportOverRecursed(f.cx);
-            return false;
-        }
-    }
-    return true;
+    JS_ASSERT(f.regs.sp == f.fp()->base());
+
+    /* Include extra space for inlined frames, loop temporaries and any pushed callee frame. */
+    uintN nvals = f.fp()->script()->nslots + StackSpace::STACK_JIT_EXTRA;
+
+    if ((Value *)f.regs.sp + nvals < f.stackLimit)
+        return true;
+
+    if (f.cx->stack.space().tryBumpLimit(NULL, f.regs.sp, nvals, &f.stackLimit))
+        return true;
+
+    /* Remove the current partially-constructed frame before throwing. */
+    RemovePartialFrame(f.cx, f.fp());
+    js_ReportOverRecursed(f.cx);
+
+    return false;
 }
 
 /*
  * HitStackQuota is called after the early prologue pushing the new frame would
  * overflow f.stackLimit.
  */
 void JS_FASTCALL
 stubs::HitStackQuota(VMFrame &f)
@@ -260,17 +265,17 @@ stubs::FixupArity(VMFrame &f, uint32 nac
     void *ncode          = oldfp->nativeReturnAddress();
 
     /* Pop the inline frame. */
     f.regs.popPartialFrame((Value *)oldfp);
 
     /* Reserve enough space for a callee frame. */
     StackFrame *newfp = cx->stack.getInlineFrameWithinLimit(cx, (Value*) oldfp, nactual,
                                                             fun, fun->script(), &flags,
-                                                            f.entryfp, &f.stackLimit, ncode);
+                                                            &f.stackLimit, ncode);
 
     /*
      * Note: this function is called without f.regs intact, but if the previous
      * call failed it will use ncode to set f.regs to reflect the state at the
      * call site. We can't use the value for ncode now as generating the
      * exception may have caused us to discard the caller's code.
      */
     if (!newfp)
@@ -322,17 +327,17 @@ UncachedInlineCall(VMFrame &f, uint32 fl
         types::UseNewType(cx, f.script(), f.pc());
 
     CallArgs args = CallArgsFromVp(argc, vp);
     types::TypeMonitorCall(cx, args, flags & StackFrame::CONSTRUCTING);
 
     /* Get pointer to new frame/slots, prepare arguments. */
     StackFrame *newfp = cx->stack.getInlineFrameWithinLimit(cx, f.regs.sp, argc,
                                                             newfun, newscript, &flags,
-                                                            f.entryfp, &f.stackLimit, NULL);
+                                                            &f.stackLimit, NULL);
     if (JS_UNLIKELY(!newfp))
         return false;
 
     /* Initialize frame, locals. */
     newfp->initCallFrame(cx, callee, newfun, argc, flags);
     SetValueRangeToUndefined(newfp->slots(), newscript->nfixed);
 
     /*
@@ -735,17 +740,17 @@ PartialInterpret(VMFrame &f)
 #ifdef DEBUG
     JSScript *script = fp->script();
     JS_ASSERT(!fp->finishedInInterpreter());
     JS_ASSERT(fp->hasImacropc() ||
               !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs().pc));
 #endif
 
     JSBool ok = JS_TRUE;
-    ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT);
+    ok = Interpret(cx, fp, JSINTERP_SAFEPOINT);
 
     return ok;
 }
 
 JS_STATIC_ASSERT(JSOP_NOP == 0);
 
 /*
  * Returns whether the current PC would return, or if the frame has already
@@ -985,17 +990,16 @@ RunTracer(VMFrame &f)
      * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the
      * correct return value stored if we trace/interpret through to the end
      * of the frame.
      */
     entryFrame->scopeChain();
     entryFrame->returnValue();
 
     bool blacklist;
-    uintN inlineCallCount = 0;
     void **traceData;
     uintN *traceEpoch;
     uint32 *loopCounter;
     uint32 hits;
 #if JS_MONOIC
     traceData = &ic.traceData;
     traceEpoch = &ic.traceEpoch;
     loopCounter = &ic.loopCounter;
@@ -1015,17 +1019,17 @@ RunTracer(VMFrame &f)
          * frames, point f.regs.fp at them and then enter the interpreter. If the
          * interpreter pops the frames it will not be reflected here as a local
          * set of regs is used by the interpreter, and f->regs end up pointing at
          * garbage, confusing the recompiler.
          */
         FrameRegs regs = f.regs;
         PreserveRegsGuard regsGuard(cx, regs);
 
-        tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch,
+        tpa = MonitorTracePoint(f.cx, &blacklist, traceData, traceEpoch,
                                 loopCounter, hits);
         JS_ASSERT(!TRACE_RECORDER(cx));
     }
 
 #if JS_MONOIC
     ic.loopCounterStart = *loopCounter;
     if (blacklist)
         DisableTraceHint(entryFrame->jit(), ic);
@@ -1589,17 +1593,17 @@ js_InternalInterpret(void *returnData, v
     f.regs.sp = fp->base() + nextDepth;
 
     /* Reinsert any trap before resuming in the interpreter. */
     untrap.retrap();
 
     /* Release lock on analysis data before resuming. */
     enter.leave();
 
-    if (!Interpret(cx, NULL, 0, interpMode))
+    if (!Interpret(cx, NULL, interpMode))
         return js_InternalThrow(f);
 
     /* The interpreter should have finished its entry frame. */
     JS_ASSERT(f.regs.fp() == fp);
 
     /* Force construction of the frame's return value, if it was not set. */
     fp->returnValue();
 
--- a/js/src/methodjit/MonoIC.cpp
+++ b/js/src/methodjit/MonoIC.cpp
@@ -1118,56 +1118,22 @@ void * JS_FASTCALL
 ic::NativeNew(VMFrame &f, CallICInfo *ic)
 {
     CallCompiler cc(f, *ic, true);
     if (!cc.generateNativeStub())
         stubs::SlowNew(f, ic->frameSize.staticArgc());
     return NULL;
 }
 
-static const unsigned MANY_ARGS = 1024;
-
-static bool
-BumpStackFull(VMFrame &f, uintN inc)
-{
-    /* If we are not passing many args, treat this as a normal call. */
-    if (inc < MANY_ARGS) {
-        if (f.regs.sp + inc < f.stackLimit)
-            return true;
-        StackSpace &space = f.cx->stack.space();
-        return space.bumpLimitWithinQuota(f.cx, f.entryfp, f.regs.sp, inc, &f.stackLimit);
-    }
-
-    /*
-     * The purpose of f.stackLimit is to catch over-recursion based on
-     * assumptions about the average frame size. 'apply' with a large number of
-     * arguments breaks these assumptions and can result in premature "out of
-     * script quota" errors. Normally, apply will go through js::Invoke, which
-     * effectively starts a fresh stackLimit. Here, we bump f.stackLimit,
-     * if necessary, to allow for this 'apply' call, and a reasonable number of
-     * subsequent calls, to succeed without hitting the stackLimit. In theory,
-     * this a recursive chain containing apply to circumvent the stackLimit.
-     * However, since each apply call must consume at least MANY_ARGS slots,
-     * this sequence will quickly reach the end of the stack and OOM.
-     */
-    StackSpace &space = f.cx->stack.space();
-    if (!space.bumpLimit(f.cx, f.entryfp, f.regs.sp, inc, &f.stackLimit)) {
-        js_ReportOutOfScriptQuota(f.cx);
-        return false;
-    }
-    return true;
-}
-
 static JS_ALWAYS_INLINE bool
 BumpStack(VMFrame &f, uintN inc)
 {
-    /* Fast path BumpStackFull. */
-    if (inc < MANY_ARGS && f.regs.sp + inc < f.stackLimit)
+    if (f.regs.sp + inc < f.stackLimit)
         return true;
-    return BumpStackFull(f, inc);
+    return f.cx->stack.space().tryBumpLimit(f.cx, f.regs.sp, inc, &f.stackLimit);
 }
 
 /*
  * SplatApplyArgs is only called for expressions of the form |f.apply(x, y)|.
  * Additionally, the callee has already been checked to be the native apply.
  * All successful paths through SplatApplyArgs must set f.u.call.dynamicArgc
  * and f.regs.sp.
  */
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -177,18 +177,17 @@ class PICStubCompiler : public BaseCompi
     LookupStatus disable(JSContext *cx, const char *reason) {
         return pic.disable(cx, reason, stub);
     }
 
   protected:
     void spew(const char *event, const char *op) {
 #ifdef JS_METHODJIT_SPEW
         JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
-                   type, event, op, script->filename,
-                   js_FramePCToLineNumber(cx, f.fp()));
+                   type, event, op, script->filename, CurrentLine(cx));
 #endif
     }
 };
 
 class SetPropCompiler : public PICStubCompiler
 {
     JSObject *obj;
     JSAtom *atom;
@@ -2186,18 +2185,17 @@ BaseIC::isCallOp()
     return !!(js_CodeSpec[op].format & JOF_CALLOP);
 }
 
 void
 BaseIC::spew(JSContext *cx, const char *event, const char *message)
 {
 #ifdef JS_METHODJIT_SPEW
     JaegerSpew(JSpew_PICs, "%s %s: %s (%s: %d)\n",
-               js_CodeName[op], event, message, cx->fp()->script()->filename,
-               js_FramePCToLineNumber(cx, cx->fp()));
+               js_CodeName[op], event, message, cx->fp()->script()->filename, CurrentLine(cx));
 #endif
 }
 
 LookupStatus
 BaseIC::disable(JSContext *cx, const char *reason, void *stub)
 {
     spew(cx, "disabled", reason);
     Repatcher repatcher(cx->fp()->jit());
@@ -2371,17 +2369,17 @@ GetElementIC::attachGetProp(VMFrame &f, 
     buffer.maybeLink(protoGuard, slowPathStart);
     buffer.link(done, fastPathRejoin);
 
     CodeLocationLabel cs = buffer.finalize();
 #if DEBUG
     char *chars = DeflateString(cx, v.toString()->getChars(cx), v.toString()->length());
     JaegerSpew(JSpew_PICs, "generated %s stub at %p for atom 0x%x (\"%s\") shape 0x%x (%s: %d)\n",
                js_CodeName[op], cs.executableAddress(), id, chars, holder->shape(),
-               cx->fp()->script()->filename, js_FramePCToLineNumber(cx, cx->fp()));
+               cx->fp()->script()->filename, CurrentLine(cx));
     cx->free_(chars);
 #endif
 
     // Update the inline guards, if needed.
     if (shouldPatchInlineTypeGuard() || shouldPatchUnconditionalClaspGuard()) {
         Repatcher repatcher(cx->fp()->jit());
 
         if (shouldPatchInlineTypeGuard()) {
--- a/js/src/methodjit/Retcon.cpp
+++ b/js/src/methodjit/Retcon.cpp
@@ -316,17 +316,17 @@ ExpandInlineFrames(JSContext *cx, bool a
                 mjit::Recompiler::expandInlineFrames(cx, f->fp(), f->regs.inlined(), nnext, f);
             }
         }
 
         StackFrame *end = f->entryfp->prev();
         StackFrame *next = NULL;
         for (StackFrame *fp = f->fp(); fp != end; fp = fp->prev()) {
             mjit::CallSite *inlined;
-            fp->pc(cx, next, &inlined);
+            fp->pcQuadratic(cx, next, &inlined);
             if (next && inlined) {
                 mjit::Recompiler::expandInlineFrames(cx, fp, inlined, next, f);
                 fp = next;
                 next = NULL;
             } else {
                 next = fp;
             }
         }
@@ -420,17 +420,17 @@ Recompiler::recompile(bool resetUses)
          */
         StackFrame *fp = f->fp();
         void **addr = f->returnAddressLocation();
         RejoinState rejoin = (RejoinState) f->stubRejoin;
         if (rejoin == REJOIN_NATIVE ||
             rejoin == REJOIN_NATIVE_LOWERED) {
             /* Native call. */
             if (fp->script() == script) {
-                patchNative(cx, fp->jit(), fp, fp->pc(cx, NULL), NULL, rejoin);
+                patchNative(cx, fp->jit(), fp, fp->pcQuadratic(cx, NULL), NULL, rejoin);
                 f->stubRejoin = REJOIN_NATIVE_PATCHED;
             }
         } else if (rejoin == REJOIN_NATIVE_PATCHED) {
             /* Already patched, don't do anything. */
         } else if (rejoin) {
             /* Recompilation triggered by CompileFunction. */
             if (fp->script() == script) {
                 fp->setRejoin(StubRejoin(rejoin));
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -137,18 +137,16 @@ size_t gMaxStackSize = DEFAULT_MAX_STACK
 
 
 #ifdef JS_THREADSAFE
 static PRUintn gStackBaseThreadIndex;
 #else
 static jsuword gStackBase;
 #endif
 
-static size_t gScriptStackQuota = JS_DEFAULT_SCRIPT_STACK_QUOTA;
-
 /*
  * Limit the timeout to 30 minutes to prevent an overflow on platfoms
  * that represent the time internally in microseconds using 32-bit int.
  */
 static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
 static jsdouble gTimeoutInterval = -1.0;
 static volatile bool gCanceled = false;
 
@@ -387,17 +385,16 @@ ShellOperationCallback(JSContext *cx)
     JS_ClearPendingException(cx);
     return JS_FALSE;
 }
 
 static void
 SetContextOptions(JSContext *cx)
 {
     JS_SetNativeStackQuota(cx, gMaxStackSize);
-    JS_SetScriptStackQuota(cx, gScriptStackQuota);
     JS_SetOperationCallback(cx, ShellOperationCallback);
 }
 
 static void
 Process(JSContext *cx, JSObject *obj, char *filename, JSBool forceTTY, JSBool last)
 {
     JSBool ok, hitEOF;
     JSObject *scriptObj;
@@ -3122,31 +3119,16 @@ ToInt32(JSContext *cx, uintN argc, jsval
 
 static JSBool
 StringsAreUTF8(JSContext *cx, uintN argc, jsval *vp)
 {
     *vp = JS_CStringsAreUTF8() ? JSVAL_TRUE : JSVAL_FALSE;
     return JS_TRUE;
 }
 
-static JSBool
-StackQuota(JSContext *cx, uintN argc, jsval *vp)
-{
-    uint32 n;
-
-    if (argc == 0)
-        return JS_NewNumberValue(cx, (double) gScriptStackQuota, vp);
-    if (!JS_ValueToECMAUint32(cx, JS_ARGV(cx, vp)[0], &n))
-        return JS_FALSE;
-    gScriptStackQuota = n;
-    JS_SetScriptStackQuota(cx, gScriptStackQuota);
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
-}
-
 static const char* badUTF8 = "...\xC0...";
 static const char* bigUTF8 = "...\xFB\xBF\xBF\xBF\xBF...";
 static const jschar badSurrogate[] = { 'A', 'B', 'C', 0xDEEE, 'D', 'E', 0 };
 
 static JSBool
 TestUTF8(JSContext *cx, uintN argc, jsval *vp)
 {
     int32 mode = 1;
@@ -3759,17 +3741,17 @@ EvalInFrame(JSContext *cx, uintN argc, j
     JSString *str = JSVAL_TO_STRING(argv[1]);
 
     bool saveCurrent = (argc >= 3 && JSVAL_IS_BOOLEAN(argv[2]))
                         ? !!(JSVAL_TO_BOOLEAN(argv[2]))
                         : false;
 
     JS_ASSERT(cx->running());
 
-    FrameRegsIter fi(cx);
+    FrameRegsIter fi(cx, FRAME_EXPAND_ALL);
     for (uint32 i = 0; i < upCount; ++i, ++fi) {
         if (!fi.fp()->prev())
             break;
     }
 
     StackFrame *const fp = fi.fp();
     if (!fp->isScriptFrame()) {
         JS_ReportError(cx, "cannot eval in non-script frame");
@@ -4561,24 +4543,40 @@ Compile(JSContext *cx, uintN argc, jsval
     }
     jsval arg0 = JS_ARGV(cx, vp)[0];
     if (!JSVAL_IS_STRING(arg0)) {
         const char *typeName = JS_GetTypeName(cx, JS_TypeOfValue(cx, arg0));
         JS_ReportError(cx, "expected string to compile, got %s", typeName);
         return JS_FALSE;
     }
 
+    static JSClass dummy_class = {
+        "jdummy",
+        JSCLASS_GLOBAL_FLAGS,
+        JS_PropertyStub,  JS_PropertyStub,
+        JS_PropertyStub,  JS_StrictPropertyStub,
+        JS_EnumerateStub, JS_ResolveStub,
+        JS_ConvertStub,   NULL,
+        JSCLASS_NO_OPTIONAL_MEMBERS
+    };
+
+    JSObject *fakeGlobal = JS_NewGlobalObject(cx, &dummy_class);
+    if (!fakeGlobal)
+        return JS_FALSE;
+
     JSString *scriptContents = JSVAL_TO_STRING(arg0);
-    if (!JS_CompileUCScript(cx, NULL, JS_GetStringCharsZ(cx, scriptContents),
-                            JS_GetStringLength(scriptContents), "<string>", 0)) {
-        return false;
-    }
+
+    uintN oldopts = JS_GetOptions(cx);
+    JS_SetOptions(cx, oldopts | JSOPTION_COMPILE_N_GO | JSOPTION_NO_SCRIPT_RVAL);
+    bool ok = JS_CompileUCScript(cx, fakeGlobal, JS_GetStringCharsZ(cx, scriptContents),
+                                 JS_GetStringLength(scriptContents), "<string>", 0);
+    JS_SetOptions(cx, oldopts);
 
     JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    return JS_TRUE;
+    return ok;
 }
 
 static JSBool
 Parse(JSContext *cx, uintN argc, jsval *vp)
 {
     if (argc < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "compile", "0", "s");
@@ -4852,17 +4850,16 @@ static JSFunctionSpec shell_functions[] 
     JS_FN_TYPE("internalConst",  InternalConst,  1,0, JS_TypeHandlerDynamic),
     JS_FN_TYPE("setDebug",       SetDebug,       1,0, JS_TypeHandlerBool),
     JS_FN_TYPE("setDebuggerHandler", SetDebuggerHandler, 1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("setThrowHook",   SetThrowHook,   1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("trap",           Trap,           3,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("untrap",         Untrap,         2,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("line2pc",        LineToPC,       0,0, JS_TypeHandlerInt),
     JS_FN_TYPE("pc2line",        PCToLine,       0,0, JS_TypeHandlerInt),
-    JS_FN_TYPE("stackQuota",     StackQuota,     0,0, JS_TypeHandlerDynamic),
     JS_FN_TYPE("stringsAreUTF8", StringsAreUTF8, 0,0, JS_TypeHandlerBool),
     JS_FN_TYPE("testUTF8",       TestUTF8,       1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("throwError",     ThrowError,     0,0, JS_TypeHandlerVoid),
 #ifdef DEBUG
     JS_FN_TYPE("disassemble",    DisassembleToString, 1,0, JS_TypeHandlerString),
     JS_FN_TYPE("dis",            Disassemble,    1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("disfile",        DisassFile,     1,0, JS_TypeHandlerVoid),
     JS_FN_TYPE("dissrc",         DisassWithSrc,  1,0, JS_TypeHandlerVoid),
@@ -4981,17 +4978,16 @@ static const char *const shell_help_mess
 "  list of constant names",
 "setDebug(debug)          Set debug mode",
 "setDebuggerHandler(f)    Set handler for debugger keyword to f",
 "setThrowHook(f)          Set throw hook to f",
 "trap([fun, [pc,]] exp)   Trap bytecode execution",
 "untrap(fun[, pc])        Remove a trap",
 "line2pc([fun,] line)     Map line number to PC",
 "pc2line(fun[, pc])       Map PC to line number",
-"stackQuota([number])     Query/set script stack quota",
 "stringsAreUTF8()         Check if strings are UTF-8 encoded",
 "testUTF8(mode)           Perform UTF-8 tests (modes are 1 to 4)",
 "throwError()             Throw an error from JS_ReportError",
 #ifdef DEBUG
 "disassemble([fun])       Return the disassembly for the given function",
 "dis([fun])               Disassemble functions into bytecodes",
 "disfile('foo.js')        Disassemble script file into bytecodes\n"
 "  dis and disfile take these options as preceeding string arguments\n"
--- a/js/src/tests/e4x/Regress/jstests.list
+++ b/js/src/tests/e4x/Regress/jstests.list
@@ -75,22 +75,22 @@ script regress-373082.js
 script regress-374106.js
 script regress-374112.js
 script regress-374116.js
 script regress-374160.js
 script regress-375406.js
 script regress-378492.js
 script regress-380833.js
 script regress-383255.js
-script regress-394941.js
+silentfail script regress-394941.js
 script regress-407323.js
 script regress-426520.js
 script regress-453915.js
-script regress-458679-01.js
-script regress-458679-02.js
+silentfail script regress-458679-01.js
+silentfail script regress-458679-02.js
 script regress-460180.js
 script regress-465063.js
 script regress-470619.js
 script regress-473709.js
 script regress-474319.js
 script regress-477053.js
 script regress-561031.js
 script regress-587434.js
--- a/js/src/tests/e4x/Regress/regress-319872.js
+++ b/js/src/tests/e4x/Regress/regress-319872.js
@@ -35,24 +35,22 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 319872;
 var summary = 'Do not Crash in jsxml.c';
 var actual = 'No Crash';
-var expect = /(No Crash|InternalError: script stack space quota is exhausted|InternalError: allocation size overflow)/;
+var expect = /(No Crash|InternalError: allocation size overflow)/;
 
 printBugNumber(BUGNUMBER);
 START(summary);
-printStatus ("Expect either no error, out of memory or catchable script stack " + 
-             "space quota is exhausted error");
+printStatus ("Expect either no error or out of memory");
 expectExitCode(0);
-expectExitCode(3);
 expectExitCode(5);
 
 try
 {
   var i,m,str;
   str="<a xmlns:v=\"";
   m="";
 
--- a/js/src/tests/e4x/Regress/regress-394941.js
+++ b/js/src/tests/e4x/Regress/regress-394941.js
@@ -35,17 +35,20 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 
 var summary = 'Infinite recursion should throw catchable exception';
 var BUGNUMBER = 394941;
 var actual = '';
-var expect = /InternalError: (script stack space quota is exhausted|too much recursion)/;
+var expect = /InternalError: too much recursion/;
+
+expectExitCode(0);
+expectExitCode(5);
 
 /*
  * use the reportMatch so that the test will pass on 1.8 
  * where the error message is "too much recursion" and on 1.9.0
  * where the error message is "script stack space quota is exhausted".
  */
 
 printBugNumber(BUGNUMBER);
--- a/js/src/tests/e4x/Regress/regress-458679-01.js
+++ b/js/src/tests/e4x/Regress/regress-458679-01.js
@@ -40,16 +40,19 @@
 var summary = 'GetXMLEntity should not assume FastAppendChar is infallible';
 var BUGNUMBER = 458679;
 var actual = '';
 var expect = '';
 
 printBugNumber(BUGNUMBER);
 START(summary);
 
+expectExitCode(0);
+expectExitCode(5);
+
 try
 {
     var x = "<";
 
     while (x.length < 12000000)
         x += x;
 
     <e4x>{x}</e4x>;
--- a/js/src/tests/e4x/Regress/regress-458679-02.js
+++ b/js/src/tests/e4x/Regress/regress-458679-02.js
@@ -40,16 +40,19 @@
 var summary = 'GetXMLEntity should not assume FastAppendChar is infallible';
 var BUGNUMBER = 458679;
 var actual = '';
 var expect = '';
 
 printBugNumber(BUGNUMBER);
 START(summary);
 
+expectExitCode(0);
+expectExitCode(5);
+
 function stringOfLength(n)
 {
     if (n == 0) {
         return "";
     } else if (n == 1) {
         return "<";
     } else {
         var r = n % 2;
--- a/js/src/tests/e4x/XML/jstests.list
+++ b/js/src/tests/e4x/XML/jstests.list
@@ -43,15 +43,15 @@ script 13.4.4.39.js
 script 13.4.4.4.js
 script 13.4.4.40.js
 script 13.4.4.5.js
 script 13.4.4.6.js
 script 13.4.4.7.js
 script 13.4.4.8.js
 script 13.4.4.9.js
 script regress-291930.js
-script regress-324422-1.js
+silentfail script regress-324422-1.js
 skip script regress-324422-2.js # slow
 skip script regress-324688.js # bug 528404 - disable due to random timeouts
 script regress-336921.js
 script regress-376773.js
 script regress-621464.js
 script regress-638982.js
--- a/js/src/tests/e4x/XML/regress-324422-1.js
+++ b/js/src/tests/e4x/XML/regress-324422-1.js
@@ -41,37 +41,31 @@ var summary = "Do not crash creating XML
 
 var BUGNUMBER = 324422;
 var actual = 'No Crash';
 var expect = 'No Crash';
 
 printBugNumber(BUGNUMBER);
 START(summary);
 
+expectExitCode(0);
+expectExitCode(5);
+
 if (typeof document == 'undefined')
 {
     printStatus ("Expect possible out of memory error");
     expectExitCode(0);
     expectExitCode(5);
 }
 var str = '<fu>x</fu>';
 
 for (var icount = 0; icount < 20; icount++)
 {
     str = str + str;
 }
 
 printStatus(str.length);
 
-try
-{
-    var x = new XML('<root>' + str + '</root>');
-}
-catch(ex)
-{
-    expect = 'InternalError: script stack space quota is exhausted';
-    actual = ex + '';
-    print('Caught ' + ex);
-}
+var x = new XML('<root>' + str + '</root>');
 
 TEST(1, expect, actual);
 
 END();
--- a/js/src/tests/e4x/XML/regress-324422-2.js
+++ b/js/src/tests/e4x/XML/regress-324422-2.js
@@ -39,20 +39,20 @@
 
 var summary = "Do not crash creating XML object with long initialiser";
 var BUGNUMBER = 324422;
 var actual = 'No Crash';
 var expect = 'No Crash';
 
 printBugNumber(BUGNUMBER);
 START(summary);
-printStatus ("Expect out of memory or script stack space quota is exhausted error");
+printStatus ("Expect out of memory error");
 
 expectExitCode(0);
-expectExitCode(3);
+expectExitCode(5);
 
 try
 {
     var str = '0123456789';
 
     for (var icount = 0; icount < 24; icount++)
     {
         str = str + str;
--- a/js/src/tests/ecma_5/RegExp/jstests.list
+++ b/js/src/tests/ecma_5/RegExp/jstests.list
@@ -1,9 +1,10 @@
 url-prefix ../../jsreftest.html?test=ecma_5/RegExp/
 script 7.8.5-01.js
 script 15.10.5-01.js
 script 15.10.7.5-01.js
 script empty-lookahead.js
 script exec.js
 script exec-lastIndex-ToInteger.js
-skip-if(!xulRuntime.shell&&(Android||xulRuntime.OS=="WINNT")) script regress-617935.js
+script regress-576828.js
+silentfail skip-if(!xulRuntime.shell&&(Android||xulRuntime.OS=="WINNT")) script regress-617935.js
 script instance-property-storage-introspection.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/RegExp/regress-576828.js
@@ -0,0 +1,8 @@
+var re = /(z\1){3}/;
+var str = 'zzz';
+var actual = re.exec(str);
+var expected = makeExpectedMatch(['zzz', 'z'], 0, str);
+checkRegExpMatch(actual, expected);
+
+if (typeof reportCompare == 'function')
+    reportCompare(true, true);
--- a/js/src/tests/ecma_5/RegExp/regress-617935.js
+++ b/js/src/tests/ecma_5/RegExp/regress-617935.js
@@ -1,15 +1,18 @@
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  *
  * Author: Christian Holler <decoder@own-hero.net>
  */
 
+expectExitCode(0);
+expectExitCode(5);
+
 /* Length of 32 */
 var foo = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
 
 /* Make len(foo) 32768 */
 for (i = 0; i < 10; ++i) {
     foo += foo;
 }
 
--- a/js/src/tests/ecma_5/RegExp/shell.js
+++ b/js/src/tests/ecma_5/RegExp/shell.js
@@ -0,0 +1,21 @@
+function makeExpectedMatch(arr, index, input) {
+    var expectedMatch = {
+        index: index,
+        input: input,
+        length: arr.length,
+    };
+
+    for (var i = 0; i < arr.length; ++i)
+        expectedMatch[i] = arr[i];
+
+    return expectedMatch;
+}
+
+function checkRegExpMatch(actual, expected) {
+    assertEq(actual.length, expected.length);
+    for (var i = 0; i < actual.length; ++i)
+        assertEq(actual[i], expected[i]);
+
+    assertEq(actual.index, expected.index);
+    assertEq(actual.input, expected.input);
+}
--- a/js/src/tests/js1_5/Array/jstests.list
+++ b/js/src/tests/js1_5/Array/jstests.list
@@ -14,17 +14,17 @@ script regress-310351.js
 script regress-311515.js
 script regress-313153.js
 script regress-315509-01.js
 skip-if(xulRuntime.XPCOMABI.match(/x86_64/)) script regress-330812.js # No test results
 script regress-345961.js
 script regress-348810.js
 script regress-350256-01.js
 script regress-350256-02.js
-script regress-350256-03.js
+silentfail script regress-350256-03.js
 script regress-360681-01.js
 script regress-360681-02.js
 script regress-364104.js
 script regress-422286.js
 script regress-424954.js
 script regress-451483.js
 script regress-451906.js
 script regress-456845.js
--- a/js/src/tests/js1_5/Array/regress-350256-03.js
+++ b/js/src/tests/js1_5/Array/regress-350256-03.js
@@ -36,52 +36,43 @@
  * ***** END LICENSE BLOCK ***** */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 350256;
 var summary = 'Array.apply maximum arguments: 2^19-1024';
 var actual = '';
 var expect = '';
 
+expectExitCode(0);
+expectExitCode(5);
 
 //-----------------------------------------------------------------------------
 test(Math.pow(2, 19)-1024);
 //-----------------------------------------------------------------------------
 
 function test(length)
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
-  try
-  {
+  var a = new Array();
+  a[length - 2] = 'length-2';
+  a[length - 1] = 'length-1';
 
-    var a = new Array();
-    a[length - 2] = 'length-2';
-    a[length - 1] = 'length-1';
-
-    var b = Array.apply(null, a);
-
-    expect = length + ',length-2,length-1';
-    actual = b.length + "," + b[length - 2] + "," + b[length - 1];
-    reportCompare(expect, actual, summary);
+  var b = Array.apply(null, a);
 
-    function f() {
-      return arguments.length + "," + arguments[length - 2] + "," +
-        arguments[length - 1];
-    }
-
-    expect = length + ',length-2,length-1';
-    actual = f.apply(null, a);
+  expect = length + ',length-2,length-1';
+  actual = b.length + "," + b[length - 2] + "," + b[length - 1];
+  reportCompare(expect, actual, summary);
 
+  function f() {
+    return arguments.length + "," + arguments[length - 2] + "," +
+      arguments[length - 1];
   }
-  catch(ex)
-  {
-    expect = 'InternalError: script stack space quota is exhausted';
-    actual = ex + '';
-    print(actual);
-  }
+
+  expect = length + ',length-2,length-1';
+  actual = f.apply(null, a);
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
 }
--- a/js/src/tests/js1_5/Function/jstests.list
+++ b/js/src/tests/js1_5/Function/jstests.list
@@ -2,14 +2,14 @@ url-prefix ../../jsreftest.html?test=js1
 script 10.1.6-01.js
 script 10.1.6.js
 script 15.3.4.4.js
 script regress-123371.js
 script regress-178389.js
 script regress-222029-001.js
 script regress-222029-002.js
 script regress-292215.js
-script regress-338001.js
-script regress-338121-01.js
-script regress-338121-02.js
-script regress-338121-03.js
+#silentfail script regress-338001.js # disabled pending bug 657444
+#silentfail script regress-338121-01.js # disabled pending bug 657444
+#silentfail script regress-338121-02.js # disabled pending bug 657444
+#silentfail script regress-338121-03.js # disabled pending bug 657444
 script regress-344052.js
 script regress-364023.js
--- a/js/src/tests/js1_5/Function/regress-338001.js
+++ b/js/src/tests/js1_5/Function/regress-338001.js
@@ -34,17 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 338001;
 var summary = 'integer overflow in jsfun.c:Function';
 var actual = 'No Crash';
-var expect = /No Crash|InternalError: allocation size overflow|InternalError: script stack space quota is exhausted/;
+var expect = /No Crash|InternalError: allocation size overflow/;
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
 expectExitCode(0);
 expectExitCode(5);
 
 var fe="f";
--- a/js/src/tests/js1_5/Function/regress-338121-01.js
+++ b/js/src/tests/js1_5/Function/regress-338121-01.js
@@ -39,31 +39,25 @@
 var BUGNUMBER = 338121;
 var summary = 'Issues with JS_ARENA_ALLOCATE_CAST';
 var actual = 'No Crash';
 var expect = 'No Crash';
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
-try
-{
-  var fe="v";
+expectExitCode(0);
+expectExitCode(5);
 
-  for (i=0; i<25; i++)
-    fe += fe;
+var fe="v";
+
+for (i=0; i<25; i++)
+  fe += fe;
 
-  var fu=new Function(
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    "done"
-    );
+var fu=new Function(
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  "done"
+  );
 
-  print('Done');
-}
-catch(ex)
-{
-  expect = 'InternalError: script stack space quota is exhausted';
-  actual = ex + '';
-  print(actual);
-}
+print('Done');
 
 reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_5/Function/regress-338121-02.js
+++ b/js/src/tests/js1_5/Function/regress-338121-02.js
@@ -39,35 +39,29 @@
 var BUGNUMBER = 338121;
 var summary = 'Issues with JS_ARENA_ALLOCATE_CAST';
 var actual = 'No Crash';
 var expect = 'No Crash';
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
-try
-{
-  var fe="vv";
+expectExitCode(0);
+expectExitCode(5);
+
+var fe="vv";
 
-  for (i=0; i<24; i++)
-    fe += fe;
+for (i=0; i<24; i++)
+  fe += fe;
 
-  var fu=new Function(
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    "done"
-    );
+var fu=new Function(
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  "done"
+  );
 
 //alert("fu="+fu);
 //print("fu="+fu);
-  var fuout = 'fu=' + fu;
+var fuout = 'fu=' + fu;
 
-  print('Done');
-}
-catch(ex)
-{
-  expect = 'InternalError: script stack space quota is exhausted';
-  actual = ex + '';
-  print(actual);
-}
+print('Done');
 
 reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_5/Function/regress-338121-03.js
+++ b/js/src/tests/js1_5/Function/regress-338121-03.js
@@ -39,36 +39,31 @@
 var BUGNUMBER = 338121;
 var summary = 'Issues with JS_ARENA_ALLOCATE_CAST';
 var actual = 'No Crash';
 var expect = 'No Crash';
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
-try
-{
-  var fe="vv";
+expectExitCode(0);
+expectExitCode(5);
 
-  for (i=0; i<24; i++)
-    fe += fe;
+var fe="vv";
+
+for (i=0; i<24; i++)
+  fe += fe;
 
-  var fu=new Function(
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
-    fe, fe, fe,
-    "done"
-    );
+var fu=new Function(
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe, fe,
+  fe, fe, fe,
+  "done"
+  );
 
 //alert("fu="+fu);
 //print("fu="+fu);
-  var fuout = 'fu=' + fu;
-}
-catch(ex)
-{
-  expect = 'InternalError: script stack space quota is exhausted';
-  actual = ex + '';
-  print('Caught ' + ex);
-}
+var fuout = 'fu=' + fu;
+
 print('Done');
 
 reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_5/Regress/jstests.list
+++ b/js/src/tests/js1_5/Regress/jstests.list
@@ -86,17 +86,17 @@ script regress-261886.js
 skip script regress-261887.js # we violate the spec here with our new iterators
 skip script regress-271716-n.js # never terminates
 script regress-274035.js
 script regress-274888.js
 script regress-275378.js
 script regress-276103.js
 script regress-278873.js
 script regress-280769-1.js
-script regress-280769-2.js
+silentfail script regress-280769-2.js
 script regress-280769-3.js
 script regress-280769-4.js
 script regress-280769-5.js
 script regress-280769.js
 script regress-281487.js
 script regress-281606.js
 script regress-281930.js
 script regress-283477.js
--- a/js/src/tests/js1_5/Regress/regress-280769-2.js
+++ b/js/src/tests/js1_5/Regress/regress-280769-2.js
@@ -35,23 +35,26 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 280769;
 var summary = 'Do not overflow 64K boundary in treeDepth';
 var actual = 'No Crash';
-var expect = /No Crash|InternalError: allocation size overflow|InternalError: script stack space quota is exhausted/;
+var expect = /No Crash|InternalError: allocation size overflow/;
 var status;
 var result;
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
+expectExitCode(0);
+expectExitCode(5);
+
 status = summary + ' ' + inSection(1) + ' (new RegExp("0|...|99999") ';
 
 try
 {
   var N = 100 * 1000;
   var a = new Array(N);
   for (var i = 0; i != N; ++i) {
     a[i] = i;
--- a/js/src/tests/js1_5/extensions/regress-336409-2.js
+++ b/js/src/tests/js1_5/extensions/regress-336409-2.js
@@ -34,17 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 336409;
 var summary = 'Integer overflow in js_obj_toSource';
 var actual = 'No Crash';
-var expect = /(No Crash|InternalError: script stack space quota is exhausted|InternalError: allocation size overflow)/;
+var expect = /(No Crash|InternalError: allocation size overflow)/;
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
 expectExitCode(0);
 expectExitCode(5);
 
 function createString(n)
--- a/js/src/tests/js1_5/extensions/regress-336410-2.js
+++ b/js/src/tests/js1_5/extensions/regress-336410-2.js
@@ -34,17 +34,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 //-----------------------------------------------------------------------------
 var BUGNUMBER = 336410;
 var summary = 'Integer overflow in array_toSource';
 var actual = 'No Crash';
-var expect = /(No Crash|InternalError: script stack space quota is exhausted|InternalError: allocation size overflow)/;
+var expect = /(No Crash|InternalError: allocation size overflow)/;
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
 
 expectExitCode(0);
 expectExitCode(5);
 
 function createString(n)
--- a/js/src/tests/js1_5/extensions/regress-342960.js
+++ b/js/src/tests/js1_5/extensions/regress-342960.js
@@ -50,37 +50,28 @@ function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
   expectExitCode(0);
   expectExitCode(5);
 
-  try
+  function v()
   {
-    function v()
-    {
-      var meg="";
-      var r="";
-      var i;
-      print("don't interrupt the script. let it go.");
-      for(i=0;i<1024*1024;i++) meg += "v";
-      for(i=0;i<1024/8;i++) r += meg;
-      var o={f1: r, f2: r, f3: r,f4: r,f5: r, f6: r, f7: r, f8: r,f9: r};
-      print('done obj');
-      var rr=r.toSource();
-      print('done toSource()');
-    }
+    var meg="";
+    var r="";
+    var i;
+    print("don't interrupt the script. let it go.");
+    for(i=0;i<1024*1024;i++) meg += "v";
+    for(i=0;i<1024/8;i++) r += meg;
+    var o={f1: r, f2: r, f3: r,f4: r,f5: r, f6: r, f7: r, f8: r,f9: r};
+    print('done obj');
+    var rr=r.toSource();
+    print('done toSource()');
+  }
 
-    v();
-  }
-  catch(ex)
-  {
-    expect = 'InternalError: script stack space quota is exhausted';
-    actual = ex + '';
-    print(actual);
-  }
+  v();
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
 }
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -446,17 +446,17 @@ StackFrame::initEvalFrame(JSContext *cx,
         exec = prev->exec;
         args.script = script;
     } else {
         exec.script = script;
     }
 
     scopeChain_ = &prev->scopeChain();
     prev_ = prev;
-    prevpc_ = prev->pc(cx, NULL, &prevInline_);
+    prevpc_ = prev->pcQuadratic(cx, NULL, &prevInline_);
     JS_ASSERT(!hasImacropc());
     JS_ASSERT(!hasHookData());
     setAnnotation(prev->annotation());
 }
 
 inline void
 StackFrame::initGlobalFrame(JSScript *script, JSObject &chain, StackFrame *prev, uint32 flagsArg)
 {
@@ -788,44 +788,42 @@ StackSpace::ensureSpace(JSContext *maybe
     JS_ASSERT(from >= firstUnused());
 #ifdef XP_WIN
     JS_ASSERT(from <= commitEnd_);
     if (commitEnd_ - from < nvals)
         return bumpCommit(maybecx, from, nvals);
     return true;
 #else
     if (end_ - from < nvals) {
-        js_ReportOutOfScriptQuota(maybecx);
+        js_ReportOverRecursed(maybecx);
         return false;
     }
     return true;
 #endif
 }
 
 inline Value *
 StackSpace::getStackLimit(JSContext *cx)
 {
+    Value *limit;
+#ifdef XP_WIN
+    limit = commitEnd_;
+#else
+    limit = end_;
+#endif
+
+    /* See getStackLimit comment in Stack.h. */
     FrameRegs &regs = cx->regs();
     uintN minSpace = regs.fp()->numSlots() + STACK_JIT_EXTRA;
-    Value *sp = regs.sp;
-    Value *required = sp + minSpace;
-    Value *desired = sp + STACK_QUOTA;
-#ifdef XP_WIN
-    if (required <= commitEnd_)
-        return Min(commitEnd_, desired);
-    if (!bumpCommit(cx, sp, minSpace))
+    if (regs.sp + minSpace > limit) {
+        js_ReportOverRecursed(cx);
         return NULL;
-    JS_ASSERT(commitEnd_ >= required);
-    return commitEnd_;
-#else
-    if (required <= end_)
-        return Min(end_, desired);
-    js_ReportOutOfScriptQuota(cx);
-    return NULL;
-#endif
+    }
+
+    return limit;
 }
 
 /*****************************************************************************/
 
 JS_ALWAYS_INLINE bool
 ContextStack::isCurrentAndActive() const
 {
     assertSegmentsInSync();
@@ -840,22 +838,21 @@ struct OOMCheck
     operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
     {
         return space.ensureSpace(cx, from, nvals);
     }
 };
 
 struct LimitCheck
 {
-    StackFrame *base;
     Value **limit;
     void *topncode;
 
-    LimitCheck(StackFrame *base, Value **limit, void *topncode)
-        : base(base), limit(limit), topncode(topncode)
+    LimitCheck(Value **limit, void *topncode)
+        : limit(limit), topncode(topncode)
     {}
 
     JS_ALWAYS_INLINE bool
     operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
     {
         JS_ASSERT(from < *limit);
         if (*limit - from >= ptrdiff_t(nvals))
             return true;
@@ -863,17 +860,17 @@ struct LimitCheck
         if (topncode) {
             /*
              * The current regs.pc may not be intact, set it in case bumping
              * the limit fails. See FixupArity.
              */
             cx->regs().updateForNcode(cx->fp()->jit(), topncode);
         }
 
-        return space.bumpLimitWithinQuota(cx, base, from, nvals, limit);
+        return space.tryBumpLimit(cx, from, nvals, limit);
     }
 };
 
 }  /* namespace detail */
 
 template <class Check>
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getCallFrame(JSContext *cx, Value *firstUnused, uintN nactual,
@@ -923,22 +920,22 @@ ContextStack::getInlineFrame(JSContext *
     JS_ASSERT(cx->regs().sp == sp);
 
     return getCallFrame(cx, sp, nactual, fun, script, flags, detail::OOMCheck());
 }
 
 JS_ALWAYS_INLINE StackFrame *
 ContextStack::getInlineFrameWithinLimit(JSContext *cx, Value *sp, uintN nactual,
                                         JSFunction *fun, JSScript *script, uint32 *flags,
-                                        StackFrame *fp, Value **limit, void *topncode) const
+                                        Value **limit, void *topncode) const
 {
     JS_ASSERT(isCurrentAndActive());
     JS_ASSERT(cx->regs().sp == sp);
 
-    return getCallFrame(cx, sp, nactual, fun, script, flags, detail::LimitCheck(fp, limit, topncode));
+    return getCallFrame(cx, sp, nactual, fun, script, flags, detail::LimitCheck(limit, topncode));
 }
 
 JS_ALWAYS_INLINE void
 ContextStack::pushInlineFrame(JSScript *script, StackFrame *fp, FrameRegs &regs)
 {
     JS_ASSERT(isCurrentAndActive());
     JS_ASSERT(regs_ == &regs && script == fp->script());
 
@@ -1119,52 +1116,16 @@ ContextStack::currentScript(jsbytecode *
 inline JSObject *
 ContextStack::currentScriptedScopeChain() const
 {
     return &regs_->fp()->scopeChain();
 }
 
 /*****************************************************************************/
 
-inline
-FrameRegsIter::FrameRegsIter(JSContext *cx)
-  : cx_(cx)
-{
-    seg_ = cx->stack.currentSegment();
-    if (JS_UNLIKELY(!seg_ || !seg_->isActive())) {
-        initSlow();
-        return;
-    }
-    fp_ = cx->fp();
-    sp_ = cx->regs().sp;
-    pc_ = cx->regs().pc;
-    return;
-}
-
-inline FrameRegsIter &
-FrameRegsIter::operator++()
-{
-    StackFrame *oldfp = fp_;
-    fp_ = fp_->prev();
-    if (!fp_)
-        return *this;
-
-    if (JS_UNLIKELY(oldfp == seg_->initialFrame())) {
-        incSlow(oldfp);
-        return *this;
-    }
-
-    JSInlinedSite *inline_;
-    pc_ = oldfp->prevpc(&inline_);
-    sp_ = oldfp->formalArgsEnd();
-    JS_ASSERT(!inline_);
-
-    return *this;
-}
-
 namespace detail {
 
 struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
 {
     CopyNonHoleArgsTo(ArgumentsObject *argsobj, Value *dst) : argsobj(*argsobj), dst(dst) {}
     ArgumentsObject &argsobj;
     Value *dst;
     bool operator()(uint32 argi, Value *src) {
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -87,27 +87,33 @@ StackFrame::prevpcSlow(JSInlinedSite **p
     return prevpc_;
 #else
     JS_NOT_REACHED("Unknown PC for frame");
     return NULL;
 #endif
 }
 
 jsbytecode *
-StackFrame::pc(JSContext *cx, StackFrame *next, JSInlinedSite **pinlined)
+StackFrame::pcQuadratic(JSContext *cx, StackFrame *next, JSInlinedSite **pinlined)
 {
     JS_ASSERT_IF(next, next->prev() == this);
 
     StackSegment &seg = cx->stack.space().containingSegment(this);
     FrameRegs &regs = seg.currentRegs();
+
+    /*
+     * This isn't just an optimization; seg->computeNextFrame(fp) is only
+     * defined if fp != seg->currentFrame.
+     */
     if (regs.fp() == this) {
         if (pinlined)
             *pinlined = regs.inlined();
         return regs.pc;
     }
+
     if (!next)
         next = seg.computeNextFrame(this);
     return next->prevpc(pinlined);
 }
 
 /*****************************************************************************/
 
 JS_REQUIRES_STACK bool
@@ -154,19 +160,17 @@ StackSegment::computeNextFrame(StackFram
         next = prev;
     return next;
 }
 
 /*****************************************************************************/
 
 StackSpace::StackSpace()
   : base_(NULL),
-#ifdef XP_WIN
     commitEnd_(NULL),
-#endif
     end_(NULL),
     seg_(NULL)
 {
     override_.top = NULL;
 #ifdef DEBUG
     override_.seg = NULL;
     override_.frame = NULL;
 #endif
@@ -186,24 +190,24 @@ StackSpace::init()
     base_ = reinterpret_cast<Value *>(p);
     commitEnd_ = base_ + COMMIT_VALS;
     end_ = base_ + CAPACITY_VALS;
 #elif defined(XP_OS2)
     if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
         DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
         return false;
     base_ = reinterpret_cast<Value *>(p);
-    end_ = base_ + CAPACITY_VALS;
+    end_ = commitEnd_ = base_ + CAPACITY_VALS;
 #else
     JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
     p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     if (p == MAP_FAILED)
         return false;
     base_ = reinterpret_cast<Value *>(p);
-    end_ = base_ + CAPACITY_VALS;
+    end_ = commitEnd_ = base_ + CAPACITY_VALS;
 #endif
     return true;
 }
 
 StackSpace::~StackSpace()
 {
     JS_ASSERT(!seg_);
     if (!base_)
@@ -299,17 +303,17 @@ StackSpace::mark(JSTracer *trc)
     }
 }
 
 #ifdef XP_WIN
 JS_FRIEND_API(bool)
 StackSpace::bumpCommit(JSContext *maybecx, Value *from, ptrdiff_t nvals) const
 {
     if (end_ - from < nvals) {
-        js_ReportOutOfScriptQuota(maybecx);
+        js_ReportOverRecursed(maybecx);
         return false;
     }
 
     Value *newCommit = commitEnd_;
     Value *request = from + nvals;
 
     /* Use a dumb loop; will probably execute once. */
     JS_ASSERT((end_ - newCommit) % COMMIT_VALS == 0);
@@ -317,64 +321,35 @@ StackSpace::bumpCommit(JSContext *maybec
         newCommit += COMMIT_VALS;
         JS_ASSERT((end_ - newCommit) >= 0);
     } while (newCommit < request);
 
     /* The cast is safe because CAPACITY_BYTES is small. */
     int32 size = static_cast<int32>(newCommit - commitEnd_) * sizeof(Value);
 
     if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
-        js_ReportOutOfScriptQuota(maybecx);
+        js_ReportOverRecursed(maybecx);
         return false;
     }
 
     commitEnd_ = newCommit;
     return true;
 }
 #endif
 
 bool
-StackSpace::bumpLimitWithinQuota(JSContext *maybecx, StackFrame *fp, Value *sp,
-                                 uintN nvals, Value **limit) const
+StackSpace::tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit)
 {
-    JS_ASSERT(sp >= firstUnused());
-    JS_ASSERT(sp + nvals >= *limit);
+    if (!ensureSpace(maybecx, from, nvals))
+        return false;
 #ifdef XP_WIN
-    Value *quotaEnd = (Value *)fp + STACK_QUOTA;
-    if (sp + nvals < quotaEnd) {
-        if (!ensureSpace(NULL, sp, nvals))
-            goto fail;
-        *limit = Min(quotaEnd, commitEnd_);
-        return true;
-    }
-  fail:
+    *limit = commitEnd_;
+#else
+    *limit = end_;
 #endif
-    js_ReportOverRecursed(maybecx);
-    return false;
-}
-
-bool
-StackSpace::bumpLimit(JSContext *cx, StackFrame *fp, Value *sp,
-                      uintN nvals, Value **limit) const
-{
-    JS_ASSERT(*limit > base_);
-    JS_ASSERT(sp < *limit);
-
-    /*
-     * Ideally, we would only ensure space for 'nvals', not 'nvals + remain',
-     * since this is ~500K. However, this whole call should be a rare case: some
-     * script is passing a obscene number of args to 'apply' and we are just
-     * trying to keep the stack limit heuristic from breaking the script.
-     */
-    Value *quota = (Value *)fp + STACK_QUOTA;
-    uintN remain = quota - sp;
-    uintN inc = nvals + remain;
-    if (!ensureSpace(NULL, sp, inc))
-        return false;
-    *limit = sp + inc;
     return true;
 }
 
 void
 StackSpace::popSegment()
 {
     JS_ASSERT(seg_->empty());
     seg_ = seg_->previousInMemory();
@@ -383,16 +358,22 @@ StackSpace::popSegment()
 void
 StackSpace::pushSegment(StackSegment &seg)
 {
     JS_ASSERT(seg.empty());
     seg.setPreviousInMemory(seg_);
     seg_ = &seg;
 }
 
+size_t
+StackSpace::committedSize()
+{
+    return (commitEnd_ - base_) * sizeof(Value);
+}
+
 /*****************************************************************************/
 
 ContextStack::ContextStack(JSContext *cx)
   : regs_(NULL),
     seg_(NULL),
     space_(&JS_THREAD_DATA(cx)->stackSpace),
     cx_(cx)
 {
@@ -664,68 +645,97 @@ ContextStack::notifyIfNoCodeRunning()
         return;
 
     cx_->resetCompartment();
     cx_->maybeMigrateVersionOverride();
 }
 
 /*****************************************************************************/
 
-void
-FrameRegsIter::initSlow()
+FrameRegsIter::FrameRegsIter(JSContext *cx, FrameExpandKind expand)
+  : cx_(cx)
 {
+    LeaveTrace(cx);
+
+#ifdef JS_METHODJIT
+    if (expand != js::FRAME_EXPAND_NONE)
+        js::mjit::ExpandInlineFrames(cx, expand == js::FRAME_EXPAND_ALL);
+#endif
+
+    seg_ = cx->stack.currentSegment();
     if (!seg_) {
         fp_ = NULL;
         sp_ = NULL;
         pc_ = NULL;
+        inlined_ = NULL;
         return;
     }
-
-    JS_ASSERT(seg_->isSuspended());
-    fp_ = seg_->suspendedFrame();
-    sp_ = seg_->suspendedRegs().sp;
-    pc_ = seg_->suspendedRegs().pc;
+    if (!seg_->isActive()) {
+        JS_ASSERT(seg_->isSuspended());
+        fp_ = seg_->suspendedFrame();
+        sp_ = seg_->suspendedRegs().sp;
+        pc_ = seg_->suspendedRegs().pc;
+        inlined_ = seg_->suspendedRegs().inlined();
+        return;
+    }
+    fp_ = cx->fp();
+    sp_ = cx->regs().sp;
+    pc_ = cx->regs().pc;
+    inlined_ = cx->regs().inlined();
+    return;
 }
 
-/*
- * Using the invariant described in the js::StackSegment comment, we know that,
- * when a pair of prev-linked stack frames are in the same segment, the
- * first frame's address is the top of the prev-frame's stack, modulo missing
- * arguments.
- */
-void
-FrameRegsIter::incSlow(StackFrame *oldfp)
+FrameRegsIter &
+FrameRegsIter::operator++()
 {
+    StackFrame *oldfp = fp_;
+    fp_ = fp_->prev();
+    if (!fp_)
+        return *this;
+
+    if (oldfp != seg_->initialFrame()) {
+        pc_ = oldfp->prevpc(&inlined_);
+        sp_ = oldfp->formalArgsEnd();
+        return *this;
+    }
+
     JS_ASSERT(oldfp == seg_->initialFrame());
     JS_ASSERT(fp_ == oldfp->prev());
 
     /*
      * Segments from arbitrary context stacks can interleave so we must do a
      * linear scan over segments in this context stack. Furthermore, 'prev' can
      * be any frame in the segment (not only the suspendedFrame), so we must
      * scan each stack frame in each segment. Fortunately, this is not hot code.
      */
     seg_ = seg_->previousInContext();
     sp_ = seg_->suspendedRegs().sp;
     pc_ = seg_->suspendedRegs().pc;
+    inlined_ = seg_->suspendedRegs().inlined();
     StackFrame *f = seg_->suspendedFrame();
     while (f != fp_) {
         if (f == seg_->initialFrame()) {
             seg_ = seg_->previousInContext();
             sp_ = seg_->suspendedRegs().sp;
             pc_ = seg_->suspendedRegs().pc;
+            inlined_ = seg_->suspendedRegs().inlined();
             f = seg_->suspendedFrame();
         } else {
-            JSInlinedSite *inline_;
             sp_ = f->formalArgsEnd();
-            pc_ = f->prevpc(&inline_);
+            pc_ = f->prevpc(&inlined_);
             f = f->prev();
-            JS_ASSERT(!inline_);
         }
     }
+    return *this;
+}
+
+bool
+FrameRegsIter::operator==(const FrameRegsIter &rhs) const
+{
+    return done() == rhs.done() && (done() || fp_ == rhs.fp_);
 }
 
 /*****************************************************************************/
 
 AllFramesIter::AllFramesIter(JSContext *cx)
   : seg_(cx->stack.currentSegment())
 {
 #ifdef JS_METHODJIT
@@ -744,9 +754,8 @@ AllFramesIter::operator++()
         while (seg_ && seg_->empty())
             seg_ = seg_->previousInMemory();
         fp_ = seg_ ? seg_->currentFrame() : NULL;
     } else {
         fp_ = fp_->prev();
     }
     return *this;
 }
-
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -451,18 +451,32 @@ class StackFrame
      */
 
     /*
      * Get the frame's current bytecode, assuming |this| is in |cx|. next is
      * frame whose prev == this, NULL if not known or if this == cx->fp().
      * If the frame is inside an inline call made within the pc, the pc will
      * be that of the outermost call and the state of any inlined frame(s) is
      * returned through pinlined.
+     *
+     * Beware, as the name implies, pcQuadratic can lead to quadratic behavior
+     * in loops such as:
+     *
+     *   for ( ...; fp; fp = fp->prev())
+     *     ... fp->pcQuadratic(cx);
+     *
+     * Using next can avoid this, but in most cases prefer FrameRegsIter;
+     * it is amortized O(1).
+     *
+     *   When I get to the bottom I go back to the top of the stack
+     *   Where I stop and I turn and I go right back
+     *   Till I get to the bottom and I see you again...
      */
-    jsbytecode *pc(JSContext *cx, StackFrame *next = NULL, JSInlinedSite **pinlined = NULL);
+    jsbytecode *pcQuadratic(JSContext *cx, StackFrame *next = NULL,
+                            JSInlinedSite **pinlined = NULL);
 
     jsbytecode *prevpc(JSInlinedSite **pinlined) {
         if (flags_ & HAS_PREVPC) {
             if (pinlined)
                 *pinlined = prevInline_;
             return prevpc_;
         }
         return prevpcSlow(pinlined);
@@ -1107,20 +1121,18 @@ struct StackOverride
 #endif
 };
 
 /*****************************************************************************/
 
 class StackSpace
 {
     Value         *base_;
-#ifdef XP_WIN
     mutable Value *commitEnd_;
-#endif
-    Value *end_;
+    Value         *end_;
     StackSegment  *seg_;
     StackOverride override_;
 
     static const size_t CAPACITY_VALS  = 512 * 1024;
     static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value);
     static const size_t COMMIT_VALS    = 16 * 1024;
     static const size_t COMMIT_BYTES   = COMMIT_VALS * sizeof(Value);
 
@@ -1167,74 +1179,47 @@ class StackSpace
      * good way to handle an OOM for these allocations, so this function checks
      * that OOM cannot occur using the size of the TraceNativeStorage as a
      * conservative upper bound.
      */
     inline bool ensureEnoughSpaceToEnterTrace();
 #endif
 
     /*
-     * If we let infinite recursion go until it hit the end of the contiguous
-     * stack, it would take a long time. As a heuristic, we kill scripts which
-     * go deeper than MAX_INLINE_CALLS. Note: this heuristic only applies to a
-     * single activation of the VM. If a script reenters, the call count gets
-     * reset. This is ok because we will quickly hit the C recursion limit.
-     */
-    static const size_t MAX_INLINE_CALLS = 3000;
-
-    /*
-     * SunSpider and v8bench have roughly an average of 9 slots per script. Our
-     * heuristic for a quick over-recursion check uses a generous slot count
-     * based on this estimate. We take this frame size and multiply it by the
-     * old recursion limit from the interpreter. Worst case, if an average size
-     * script (<=9 slots) over recurses, it'll effectively be the same as having
-     * increased the old inline call count to <= 5,000.
-     */
-    static const size_t STACK_QUOTA = MAX_INLINE_CALLS * (VALUES_PER_STACK_FRAME + 18);
-
-    /*
      * Extra space to reserve on the stack for method JIT frames, beyond the
      * frame's nslots. This may be used for inlined stack frames, slots storing
-     * loop invariant code, or to reserve space for pushed callee frames.
+     * loop invariant code, or to reserve space for pushed callee frames. Note
+     * that this space should be reserved when pushing interpreter frames as
+     * well, so that we don't need to check the stack when entering the method
+     * JIT at loop heads or safe points.
      */
     static const size_t STACK_JIT_EXTRA = (VALUES_PER_STACK_FRAME + 18) * 10;
 
     /*
-     * In the mjit, we'd like to collapse two "overflow" checks into one:
-     *  - the MAX_INLINE_CALLS check (see above comment)
-     *  - the stack OOM check (or, on Windows, the commit/OOM check) This
-     * function produces a 'limit' pointer that satisfies both these checks.
-     * (The STACK_QUOTA comment explains how this limit simulates checking
-     * MAX_INLINE_CALLS.) This limit is guaranteed to have at least enough space
-     * for cx->fp()->nslots() plus an extra stack frame (which is the min
-     * requirement for entering mjit code) or else an error is reported and NULL
-     * is returned. When the stack grows past the returned limit, the script may
-     * still be within quota, but more memory needs to be committed. This is
-     * handled by bumpLimitWithinQuota.
+     * Return a limit against which jit code can check for. This limit is not
+     * necessarily the end of the stack since we lazily commit stack memory on
+     * some platforms. Thus, when the stack limit is exceeded, the caller should
+     * use tryBumpLimit to attempt to increase the stack limit by committing
+     * more memory. If the stack is truly exhausted, tryBumpLimit will report an
+     * error and return NULL.
+     *
+     * An invariant of the methodjit is that there is always space to push a
+     * frame on top of the current frame's expression stack (which can be at
+     * most script->nslots deep). getStackLimit ensures that the returned limit
+     * does indeed have this required space and reports an error and returns
+     * NULL if this reserve space cannot be allocated.
      */
     inline Value *getStackLimit(JSContext *cx);
-
-    /*
-     * Try to bump the limit, staying within |base + STACK_QUOTA|, by
-     * committing more pages of the contiguous stack.
-     *  base: the frame on which execution started
-     *  from: the current top of the stack
-     *  nvals: requested space above 'from'
-     *  *limit: receives bumped new limit
-     */
-    bool bumpLimitWithinQuota(JSContext *maybecx, StackFrame *base, Value *from, uintN nvals, Value **limit) const;
-
-    /*
-     * Raise the given limit without considering quota.
-     * See comment in BumpStackFull.
-     */
-    bool bumpLimit(JSContext *cx, StackFrame *base, Value *from, uintN nvals, Value **limit) const;
+    bool tryBumpLimit(JSContext *maybecx, Value *from, uintN nvals, Value **limit);
 
     /* Called during GC: mark segments, frames, and slots under firstUnused. */
     void mark(JSTracer *trc);
+
+    /* We only report the committed size;  uncommitted size is uninteresting. */
+    JS_FRIEND_API(size_t) committedSize();
 };
 
 /*****************************************************************************/
 
 class ContextStack
 {
     FrameRegs *regs_;
     StackSegment *seg_;
@@ -1346,17 +1331,17 @@ class ContextStack
     /* Mark the top segment as suspended, without pushing a new one. */
     void saveActiveSegment();
 
     /* Undoes calls to suspendActiveSegment. */
     void restoreSegment();
 
     /*
      * For the five sets of stack operations below:
-     *  - The boolean-valued functions call js_ReportOutOfScriptQuota on OOM.
+     *  - The boolean-valued functions call js_ReportOverRecursed on OOM.
      *  - The "get*Frame" functions do not change any global state, they just
      *    check OOM and return pointers to an uninitialized frame with the
      *    requested missing arguments/slots. Only once the "push*Frame"
      *    function has been called is global state updated. Thus, between
      *    "get*Frame" and "push*Frame", the frame and slots are unrooted.
      *  - Functions taking "*Guard" arguments will use the guard's destructor
      *    to pop the stack. The caller must ensure the guard has the
      *    appropriate lifetime.
@@ -1401,17 +1386,17 @@ class ContextStack
      * limit (see StackSpace::getStackLimit).
      */
     inline StackFrame *
     getInlineFrame(JSContext *cx, Value *sp, uintN nactual,
                    JSFunction *fun, JSScript *script, uint32 *flags) const;
     inline StackFrame *
     getInlineFrameWithinLimit(JSContext *cx, Value *sp, uintN nactual,
                               JSFunction *fun, JSScript *script, uint32 *flags,
-                              StackFrame *base, Value **limit, void *topncode) const;
+                              Value **limit, void *topncode) const;
     inline void pushInlineFrame(JSScript *script, StackFrame *fp, FrameRegs &regs);
     inline void popInlineFrame();
 
     /* For jit use: */
     static size_t offsetOfRegs() { return offsetof(ContextStack, regs_); }
 
     /* Get the topmost script and optional pc on the stack. */
     inline JSScript *currentScript(jsbytecode **pc = NULL) const;
@@ -1487,46 +1472,57 @@ class DummyFrameGuard : public FrameGuar
     FrameRegs regs_;
 };
 
 class GeneratorFrameGuard : public FrameGuard
 {};
 
 /*****************************************************************************/
 
+/* How much expansion of inlined frames to do when inspecting the stack. */
+enum FrameExpandKind {
+    FRAME_EXPAND_NONE,
+    FRAME_EXPAND_TOP,
+    FRAME_EXPAND_ALL
+};
+
 /*
  * While |cx->fp|'s pc/sp are available in |cx->regs|, to compute the saved
  * value of pc/sp for any other frame, it is necessary to know about that
  * frame's next-frame. This iterator maintains this information when walking
  * a chain of stack frames starting at |cx->fp|.
  *
  * Usage:
  *   for (FrameRegsIter i(cx); !i.done(); ++i)
  *     ... i.fp() ... i.sp() ... i.pc()
  */
 class FrameRegsIter
 {
-    JSContext    *cx_;
-    StackSegment *seg_;
-    StackFrame   *fp_;
-    Value        *sp_;
-    jsbytecode   *pc_;
+    JSContext     *cx_;
+    StackSegment  *seg_;
+    StackFrame    *fp_;
+    Value         *sp_;
+    jsbytecode    *pc_;
+    JSInlinedSite *inlined_;
 
     void initSlow();
     void incSlow(StackFrame *oldfp);
 
   public:
-    inline FrameRegsIter(JSContext *cx);
+    FrameRegsIter(JSContext *cx, FrameExpandKind expand);
 
     bool done() const { return fp_ == NULL; }
-    inline FrameRegsIter &operator++();
+    FrameRegsIter &operator++();
+    bool operator==(const FrameRegsIter &rhs) const;
+    bool operator!=(const FrameRegsIter &rhs) const { return !(*this == rhs); }
 
     StackFrame *fp() const { return fp_; }
     Value *sp() const { return sp_; }
     jsbytecode *pc() const { return pc_; }
+    JSInlinedSite *inlined() const { return inlined_; }
 };
 
 /*
  * Utility class for iteration over all active stack frames.
  */
 class AllFramesIter
 {
 public:
--- a/js/src/xpconnect/src/nsXPConnect.cpp
+++ b/js/src/xpconnect/src/nsXPConnect.cpp
@@ -96,16 +96,17 @@ const char XPC_XPCONNECT_CONTRACTID[]   
 /***************************************************************************/
 
 nsXPConnect::nsXPConnect()
     :   mRuntime(nsnull),
         mInterfaceInfoManager(do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID)),
         mDefaultSecurityManager(nsnull),
         mDefaultSecurityManagerFlags(0),
         mShuttingDown(JS_FALSE),
+        mNeedGCBeforeCC(JS_TRUE),
         mCycleCollectionContext(nsnull)
 {
     mRuntime = XPCJSRuntime::newXPCJSRuntime(this);
 
     nsCycleCollector_registerRuntime(nsIProgrammingLanguage::JAVASCRIPT, this);
 #ifdef DEBUG_CC
     mJSRoots.ops = nsnull;
 #endif
@@ -334,16 +335,22 @@ nsXPConnect::GetInfoForIID(const nsIID *
 }
 
 nsresult
 nsXPConnect::GetInfoForName(const char * name, nsIInterfaceInfo** info)
 {
     return FindInfo(NameTester, name, mInterfaceInfoManager, info);
 }
 
+bool
+nsXPConnect::NeedCollect()
+{
+    return !!mNeedGCBeforeCC;
+}
+
 void
 nsXPConnect::Collect()
 {
     // We're dividing JS objects into 2 categories:
     //
     // 1. "real" roots, held by the JS engine itself or rooted through the root
     //    and lock JS APIs. Roots from this category are considered black in the
     //    cycle collector, any cycle they participate in is uncollectable.
@@ -380,16 +387,18 @@ nsXPConnect::Collect()
     // will already be marked by the JS GC and will thus be colored black
     // themselves. Any C++ objects they hold will have a missing (untraversed)
     // edge from the JS object to the C++ object and so it will be marked black
     // too. This decreases the number of objects that the cycle collector has to
     // deal with.
     // To improve debugging, if DEBUG_CC is defined all JS objects are
     // traversed.
 
+    mNeedGCBeforeCC = JS_FALSE;
+
     XPCCallContext ccx(NATIVE_CALLER);
     if(!ccx.IsValid())
         return;
 
     JSContext *cx = ccx.GetJSContext();
 
     // We want to scan the current thread for GC roots only if it was in a
     // request prior to the Collect call to avoid false positives during the
@@ -569,19 +578,45 @@ nsXPConnect::Unroot(void *p)
 
 JSBool
 xpc_GCThingIsGrayCCThing(void *thing)
 {
     uint32 kind = js_GetGCThingTraceKind(thing);
     return ADD_TO_CC(kind) && xpc_IsGrayGCThing(thing);
 }
 
+/*
+ * The GC and CC are run independently. Consequently, the following sequence of
+ * events can occur:
+ * 1. GC runs and marks an object gray.
+ * 2. Some JS code runs that creates a pointer from a JS root to the gray
+ *    object. If we re-ran a GC at this point, the object would now be black.
+ * 3. Now we run the CC. It may think it can collect the gray object, even
+ *    though it's reachable from the JS heap.
+ *
+ * To prevent this badness, we unmark the gray bit of an object when it is
+ * accessed by callers outside XPConnect. This would cause the object to go
+ * black in step 2 above. This must be done on everything reachable from the
+ * object being returned. The following code takes care of the recursive
+ * re-coloring.
+ */
 static void
 UnmarkGrayChildren(JSTracer *trc, void *thing, uint32 kind)
 {
+    int stackDummy;
+    if (!JS_CHECK_STACK_SIZE(trc->context->stackLimit, &stackDummy)) {
+        /*
+         * If we run out of stack, we take a more drastic measure: require that
+         * we GC again before the next CC.
+         */
+        nsXPConnect* xpc = nsXPConnect::GetXPConnect();
+        xpc->EnsureGCBeforeCC();
+        return;
+    }
+
     // If this thing is not a CC-kind or already non-gray then we're done.
     if(!ADD_TO_CC(kind) || !xpc_IsGrayGCThing(thing))
         return;
 
     // Unmark.
     static_cast<js::gc::Cell *>(thing)->unmark(XPC_GC_COLOR_GRAY);
 
     // Trace children.
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -241,20 +241,18 @@ ContextCallback(JSContext *cx, uintN ope
             delete XPCContext::GetXPCContext(cx);
         }
     }
     return JS_TRUE;
 }
 
 xpc::CompartmentPrivate::~CompartmentPrivate()
 {
-    if (waiverWrapperMap)
-        delete waiverWrapperMap;
-    if (expandoMap)
-        delete expandoMap;
+    delete waiverWrapperMap;
+    delete expandoMap;
 }
 
 static JSBool
 CompartmentCallback(JSContext *cx, JSCompartment *compartment, uintN op)
 {
     JS_ASSERT(op == JSCOMPARTMENT_DESTROY);
 
     XPCJSRuntime* self = nsXPConnect::GetRuntimeInstance();
@@ -1282,16 +1280,35 @@ GetPerCompartmentSize(PRInt64 (*f)(JSCom
     JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
     js::AutoLockGC lock(rt);
     PRInt64 n = 0;
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
         n += f(*c);
     return n;
 }
 
+static PRInt64
+GetJSStack(void *data)
+{
+    JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime();
+    PRInt64 n = 0;
+    for (js::ThreadDataIter i(rt); !i.empty(); i.popFront())
+        n += i.threadData()->stackSpace.committedSize();
+    return n;
+}
+
+NS_MEMORY_REPORTER_IMPLEMENT(XPConnectJSStack,
+    "explicit/js/stack",
+    MR_MAPPED,
+    "Memory used for the JavaScript stack.  This is the committed portion "
+    "of the stack;  any uncommitted portion is not measured because it "
+    "hardly costs anything.",
+    GetJSStack,
+    NULL)
+
 #ifdef JS_METHODJIT
 
 static PRInt64
 GetCompartmentMjitCodeSize(JSCompartment *c)
 {
     return c->getMjitCodeSize();
 }
 
@@ -1454,16 +1471,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
         if (!mWatchdogWakeup)
             NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
 
         mJSRuntime->setActivityCallback(ActivityCallback, this);
 
         mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator);
 
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSGCHeap));
+        NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSStack));
 #ifdef JS_METHODJIT
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitCode));
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSMjitData));
 #endif
 #ifdef JS_TRACER
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitCode));
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsMain));
         NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(XPConnectJSTjitDataAllocatorsReserve));
@@ -1555,20 +1573,16 @@ XPCJSRuntime::OnJSContextNew(JSContext *
     if(!tls)
         return JS_FALSE;
 
     XPCContext* xpc = new XPCContext(this, cx);
     if (!xpc)
         return JS_FALSE;
 
     JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024);
-    PRUint64 totalMemory = PR_GetPhysicalMemorySize();
-    size_t quota = PR_MIN(PR_UINT32_MAX, PR_MAX(25 * sizeof(size_t) * 1024 * 1024,
-                                                totalMemory / 4));
-    JS_SetScriptStackQuota(cx, quota);
 
     // we want to mark the global object ourselves since we use a different color
     JS_ToggleOptions(cx, JSOPTION_UNROOTED_GLOBAL);
 
     return JS_TRUE;
 }
 
 JSBool
--- a/js/src/xpconnect/src/xpcprivate.h
+++ b/js/src/xpconnect/src/xpcprivate.h
@@ -514,16 +514,18 @@ public:
     static void InitStatics() { gSelf = nsnull; gOnceAliveNowDead = JS_FALSE; }
     // Called by module code on dll shutdown.
     static void ReleaseXPConnectSingleton();
 
     virtual ~nsXPConnect();
 
     JSBool IsShuttingDown() const {return mShuttingDown;}
 
+    void EnsureGCBeforeCC() { mNeedGCBeforeCC = JS_TRUE; }
+
     nsresult GetInfoForIID(const nsIID * aIID, nsIInterfaceInfo** info);
     nsresult GetInfoForName(const char * name, nsIInterfaceInfo** info);
 
     static nsresult Base64Encode(const nsACString &aString,
                                  nsACString &aBinary);
 
     static nsresult Base64Encode(const nsAString &aString,
                                  nsAString &aBinaryData);
@@ -548,16 +550,17 @@ public:
                         nsCycleCollectionTraversalCallback &cb);
     
     // nsCycleCollectionLanguageRuntime
     virtual nsresult BeginCycleCollection(nsCycleCollectionTraversalCallback &cb,
                                           bool explainExpectedLiveGarbage);
     virtual nsresult FinishTraverse();
     virtual nsresult FinishCycleCollection();
     virtual nsCycleCollectionParticipant *ToParticipant(void *p);
+    virtual bool NeedCollect();
     virtual void Collect();
 #ifdef DEBUG_CC
     virtual void PrintAllReferencesTo(void *p);
 #endif
 
     XPCCallContext *GetCycleCollectionContext()
     {
         return mCycleCollectionContext;
@@ -597,16 +600,17 @@ private:
     static nsXPConnect*      gSelf;
     static JSBool            gOnceAliveNowDead;
 
     XPCJSRuntime*            mRuntime;
     nsCOMPtr<nsIInterfaceInfoSuperManager> mInterfaceInfoManager;
     nsIXPCSecurityManager*   mDefaultSecurityManager;
     PRUint16                 mDefaultSecurityManagerFlags;
     JSBool                   mShuttingDown;
+    JSBool                   mNeedGCBeforeCC;
 #ifdef DEBUG_CC
     PLDHashTable             mJSRoots;
 #endif
     nsAutoPtr<XPCCallContext> mCycleCollectionContext;
 
     typedef nsBaseHashtable<nsVoidPtrHashKey, nsISupports*, nsISupports*> ScopeSet;
     ScopeSet mScopes;
     nsCOMPtr<nsIXPCScriptable> mBackstagePass;
--- a/js/src/xpconnect/src/xpcthreadcontext.cpp
+++ b/js/src/xpconnect/src/xpcthreadcontext.cpp
@@ -416,18 +416,17 @@ XPCPerThreadData::~XPCPerThreadData()
         gLock = nsnull;
     }
 }
 
 static void
 xpc_ThreadDataDtorCB(void* ptr)
 {
     XPCPerThreadData* data = (XPCPerThreadData*) ptr;
-    if(data)
-        delete data;
+    delete data;
 }
 
 void XPCPerThreadData::TraceJS(JSTracer *trc)
 {
 #ifdef XPC_TRACK_AUTOMARKINGPTR_STATS
     {
         static int maxLength = 0;
         int length = 0;
@@ -479,18 +478,17 @@ XPCPerThreadData::GetDataImpl(JSContext 
 
     data = (XPCPerThreadData*) PR_GetThreadPrivate(gTLSIndex);
     if(!data)
     {
         data = new XPCPerThreadData();
         if(!data || !data->IsValid())
         {
             NS_ERROR("new XPCPerThreadData() failed!");
-            if(data)
-                delete data;
+            delete data;
             return nsnull;
         }
         if(PR_FAILURE == PR_SetThreadPrivate(gTLSIndex, data))
         {
             NS_ERROR("PR_SetThreadPrivate failed!");
             delete data;
             return nsnull;
         }
--- a/js/src/xpconnect/tests/mochitest/Makefile.in
+++ b/js/src/xpconnect/tests/mochitest/Makefile.in
@@ -89,17 +89,17 @@ include $(topsrcdir)/config/rules.mk
 		test2_bug629331.html \
 		test_bug618017.html \
 		test_bug636097.html \
 		test_bug650273.html \
 		file_bug650273.html \
 		file_bug658560.html \
 		$(NULL)
 
-ifndef ANDROID
+ifneq ($(OS_TARGET),Android)
 ifndef MOZ_PLATFORM_MAEMO
 _TEST_FILES +=	test_bug657267.html \
 		bug657267.jar
 endif
 endif
 
 
 		#test_bug484107.html \
--- a/mobile/chrome/tests/Makefile.in
+++ b/mobile/chrome/tests/Makefile.in
@@ -98,17 +98,17 @@ include $(topsrcdir)/config/rules.mk
   browser_scrollbar.sjs \
   browser_title.sjs \
   browser_thumbnails.js \
   browser_install.xml \
   browser_upgrade.rdf\
   mock_autocomplete.json\
   $(NULL)
 
-ifndef ANDROID
+ifneq ($(OS_TARGET),Android)
 ifndef MOZ_PLATFORM_MAEMO
 _BROWSER_FILES +=  browser_sidebars.js
 endif
 endif
 
 libs:: $(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
--- a/xpcom/base/nsCycleCollector.cpp
+++ b/xpcom/base/nsCycleCollector.cpp
@@ -977,17 +977,16 @@ struct nsCycleCollectionXPCOMRuntime :
 };
 
 struct nsCycleCollector
 {
     PRBool mCollectionInProgress;
     PRBool mScanInProgress;
     PRBool mFollowupCollection;
     PRUint32 mCollectedObjects;
-    PRBool mFirstCollection;
     TimeStamp mCollectionStart;
 
     nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
     nsCycleCollectionXPCOMRuntime mXPCOMRuntime;
 
     GCGraph mGraph;
 
     nsCycleCollectorParams mParams;
@@ -2142,17 +2141,16 @@ InitMemHook(void)
 ////////////////////////////////////////////////////////////////////////
 // Collector implementation
 ////////////////////////////////////////////////////////////////////////
 
 nsCycleCollector::nsCycleCollector() : 
     mCollectionInProgress(PR_FALSE),
     mScanInProgress(PR_FALSE),
     mCollectedObjects(0),
-    mFirstCollection(PR_TRUE),
     mWhiteNodes(nsnull),
     mWhiteNodeCount(0),
 #ifdef DEBUG_CC
     mPurpleBuf(mParams, mStats),
     mPtrLog(nsnull)
 #else
     mPurpleBuf(mParams)
 #endif
@@ -2549,33 +2547,32 @@ PRBool
 nsCycleCollector::BeginCollection(PRBool aForceGC,
                                   nsICycleCollectorListener *aListener)
 {
     if (mParams.mDoNothing)
         return PR_FALSE;
 
     // The cycle collector uses the mark bitmap to discover what JS objects
     // were reachable only from XPConnect roots that might participate in
-    // cycles. If this is the first cycle collection after startup force
-    // a garbage collection, otherwise the GC might not have run yet and
-    // the bitmap is invalid.
-    if (mFirstCollection && mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]) {
-        aForceGC = PR_TRUE;
-        mFirstCollection = PR_FALSE;
-    }
-
-    if (aForceGC && mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]) {
+    // cycles. We ask the JS runtime whether we need to force a GC before
+    // this CC. It returns true on startup (before the mark bits have been set),
+    // and also when UnmarkGray has run out of stack.
+    if (mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]) {
+        nsCycleCollectionJSRuntime* rt =
+            static_cast<nsCycleCollectionJSRuntime*>
+                (mRuntimes[nsIProgrammingLanguage::JAVASCRIPT]);
+        if (rt->NeedCollect() || aForceGC) {
 #ifdef COLLECT_TIME_DEBUG
-        PRTime start = PR_Now();
+            PRTime start = PR_Now();
 #endif
-        static_cast<nsCycleCollectionJSRuntime*>
-            (mRuntimes[nsIProgrammingLanguage::JAVASCRIPT])->Collect();
+            rt->Collect();
 #ifdef COLLECT_TIME_DEBUG
-        printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC);
+            printf("cc: GC() took %lldms\n", (PR_Now() - start) / PR_USEC_PER_MSEC);
 #endif
+        }
     }
 
     if (aListener && NS_FAILED(aListener->Begin())) {
         aListener = nsnull;
     }
 
     GCGraphBuilder builder(mGraph, mRuntimes, aListener);
     if (!builder.Initialized())
--- a/xpcom/base/nsCycleCollector.h
+++ b/xpcom/base/nsCycleCollector.h
@@ -72,16 +72,21 @@ void nsCycleCollector_shutdown();
 // The JS runtime is special, it needs to call cycle collection during its GC.
 // If the JS runtime is registered nsCycleCollector_collect will call
 // nsCycleCollectionJSRuntime::Collect which will call
 // nsCycleCollector_doCollect, else nsCycleCollector_collect will call
 // nsCycleCollector_doCollect directly.
 struct nsCycleCollectionJSRuntime : public nsCycleCollectionLanguageRuntime
 {
     /**
+     * Should we force a JavaScript GC before a CC?
+     */
+    virtual bool NeedCollect() = 0;
+
+    /**
      * Runs the JavaScript GC.
      */
     virtual void Collect() = 0;
 };
 
 #ifdef DEBUG
 NS_COM void nsCycleCollector_DEBUG_shouldBeFreed(nsISupports *n);
 NS_COM void nsCycleCollector_DEBUG_wasFreed(nsISupports *n);