Merge TM -> JM
authorBrian Hackett <bhackett1024@gmail.com>
Mon, 06 Jun 2011 09:48:04 -0700
changeset 75144 4f8b85723213489d3cf8699dddad477347ffc316
parent 75143 a4492fe5a236a99a606c9143d3e37a471b305fea (current diff)
parent 70665 7e6f3b1796441e3cb3a2ae87d43bf793a46580a3 (diff)
child 75145 334428e1d5aac565a771d1bbb1a785797fd89f81
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
milestone6.0a1
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);