[INFER] Apply stack review comments, bug 657412.
authorBrian Hackett <bhackett1024@gmail.com>
Sun, 05 Jun 2011 22:39:45 -0700
changeset 75141 51de14efb83c7bd632071ffd7d6b19fc986a5f0f
parent 75140 6d423e5f2e488bfaa6466ba6be90b0c352f057f9
child 75142 96342525ae1a282a24c18dd0707976490218c0fa
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
bugs657412
milestone6.0a1
[INFER] Apply stack review comments, bug 657412.
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscntxtinlines.h
js/src/jsdbgapi.cpp
js/src/jsexn.cpp
js/src/jsfun.cpp
js/src/jsinfer.cpp
js/src/jsinferinlines.h
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsopcode.cpp
js/src/jspropertycache.cpp
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/InvokeHelpers.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -873,18 +873,18 @@ static bool
 checkReportFlags(JSContext *cx, uintN *flags)
 {
     if (JSREPORT_IS_STRICT_MODE_ERROR(*flags)) {
         /*
          * Error in strict code; warning with strict option; okay otherwise.
          * We assume that if the top frame is a native, then it is strict if
          * the nearest scripted frame is strict, see bug 536306.
          */
-        StackFrame *fp = js_GetScriptedCaller(cx, NULL);
-        if (fp && fp->script()->strictModeCode)
+        JSScript *script = cx->stack.currentScript();
+        if (script && script->strictModeCode)
             *flags &= ~JSREPORT_WARNING;
         else if (cx->hasStrictOption())
             *flags |= JSREPORT_WARNING;
         else
             return true;
     } else if (JSREPORT_IS_STRICT(*flags)) {
         /* Warning/error only when JSOPTION_STRICT is set. */
         if (!cx->hasStrictOption())
@@ -1389,17 +1389,17 @@ TriggerAllOperationCallbacks(JSRuntime *
 }
 
 } /* namespace js */
 
 StackFrame *
 js_GetScriptedCaller(JSContext *cx, StackFrame *fp)
 {
     if (!fp)
-        fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
+        fp = js_GetTopStackFrame(cx, FRAME_EXPAND_TOP);
     while (fp && fp->isDummyFrame())
         fp = fp->prev();
     JS_ASSERT_IF(fp, fp->isScriptFrame());
     return fp;
 }
 
 jsbytecode*
 js_GetCurrentBytecodePC(JSContext* cx)
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2449,16 +2449,21 @@ namespace js {
 JS_FRIEND_API(void)
 TriggerOperationCallback(JSContext *cx);
 
 void
 TriggerAllOperationCallbacks(JSRuntime *rt);
 
 } /* namespace js */
 
+/*
+ * Get the topmost scripted frame in a context. Note: if the topmost frame is
+ * in the middle of an inline call, that call will be expanded. To avoid this,
+ * use cx->stack.currentScript or cx->stack.currentScriptedScopeChain.
+ */
 extern js::StackFrame *
 js_GetScriptedCaller(JSContext *cx, js::StackFrame *fp);
 
 extern jsbytecode*
 js_GetCurrentBytecodePC(JSContext* cx);
 
 extern bool
 js_CurrentPCIsInImacro(JSContext *cx);
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -47,28 +47,31 @@
 #include "jsxml.h"
 #include "jsregexp.h"
 #include "jsgc.h"
 
 namespace js {
 
 struct PreserveRegsGuard
 {
-    JSContext *cx;
-    const FrameRegs &regs;
-    FrameRegs *prevContextRegs;
     PreserveRegsGuard(JSContext *cx, FrameRegs &regs)
-      : cx(cx), regs(regs), prevContextRegs(cx->maybeRegs()) {
-        cx->stack.repointRegs(&regs);
+      : prevContextRegs(cx->maybeRegs()), cx(cx), regs_(regs) {
+        cx->stack.repointRegs(&regs_);
     }
     ~PreserveRegsGuard() {
-        JS_ASSERT(cx->maybeRegs() == &regs);
-        *prevContextRegs = regs;
+        JS_ASSERT(cx->maybeRegs() == &regs_);
+        *prevContextRegs = regs_;
         cx->stack.repointRegs(prevContextRegs);
     }
+
+    FrameRegs *prevContextRegs;
+
+  private:
+    JSContext *cx;
+    FrameRegs &regs_;
 };
 
 static inline GlobalObject *
 GetGlobalForScopeChain(JSContext *cx)
 {
     /*
      * This is essentially GetScopeChain(cx)->getGlobal(), but without
      * falling off trace.
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1428,17 +1428,17 @@ JS_GetScriptPrincipals(JSContext *cx, JS
 
 /*
  *  Stack Frame Iterator
  */
 JS_PUBLIC_API(JSStackFrame *)
 JS_FrameIterator(JSContext *cx, JSStackFrame **iteratorp)
 {
     StackFrame *fp = Valueify(*iteratorp);
-    *iteratorp = Jsvalify((fp == NULL) ? js_GetTopStackFrame(cx, FRAME_EXPAND_NONE) : fp->prev());
+    *iteratorp = Jsvalify((fp == NULL) ? js_GetTopStackFrame(cx, FRAME_EXPAND_ALL) : fp->prev());
     return *iteratorp;
 }
 
 JS_PUBLIC_API(JSScript *)
 JS_GetFrameScript(JSContext *cx, JSStackFrame *fp)
 {
     return Valueify(fp)->maybeScript();
 }
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -290,17 +290,17 @@ 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_NONE); fp; fp = fp->prev()) {
+    for (fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL); fp; fp = fp->prev()) {
         if (fp->compartment() != cx->compartment)
             break;
         if (fp->isNonEvalFunctionFrame()) {
             Value v = NullValue();
             if (checkAccess &&
                 !checkAccess(cx, &fp->callee(), callerid, JSACC_READ, &v)) {
                 break;
             }
@@ -333,16 +333,18 @@ InitExnPrivate(JSContext *cx, JSObject *
     priv->errorReport = NULL;
     priv->message = message;
     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()) {
         if (fp->compartment() != cx->compartment)
             break;
         if (!fp->isFunctionFrame() || fp->isEvalFrame()) {
             elem->funName = NULL;
             elem->argc = 0;
         } else {
             elem->funName = fp->fun()->atom
@@ -692,20 +694,16 @@ FilenameToString(JSContext *cx, const ch
 }
 
 static JSBool
 Exception(JSContext *cx, uintN argc, Value *vp)
 {
     JSString *message, *filename;
     StackFrame *fp;
 
-#ifdef JS_METHODJIT
-    js::mjit::ExpandInlineFrames(cx, true);
-#endif
-
     /*
      * 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();
@@ -1096,20 +1094,16 @@ js_ErrorToException(JSContext *cx, const
     JSErrNum errorNumber;
     const JSErrorFormatString *errorString;
     JSExnType exn;
     jsval tv[4];
     JSBool ok;
     JSObject *errProto, *errObject;
     JSString *messageStr, *filenameStr;
 
-#ifdef JS_METHODJIT
-    js::mjit::ExpandInlineFrames(cx, true);
-#endif
-
     /*
      * Tell our caller to report immediately if this report is just a warning.
      */
     JS_ASSERT(reportp);
     if (JSREPORT_IS_WARNING(reportp->flags))
         return JS_FALSE;
 
     /* Find the exception index associated with this error. */
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1610,33 +1610,39 @@ fun_getProperty(JSContext *cx, JSObject 
         if (!obj)
             return true;
     }
     JSFunction *fun = obj->getFunctionPrivate();
 
     if (slot == FUN_ARGUMENTS || slot == FUN_CALLER) {
         /*
          * Mark the function's script as uninlineable, to expand any of its
-         * frames on the stack before we go looking for them.
+         * frames on the stack before we go looking for them. This allows the
+         * below walk to only check each explicit frame rather than needing to
+         * check any calls that were inlined.
          */
         if (fun->isInterpreted())
             MarkTypeObjectFlags(cx, fun->getType(), OBJECT_FLAG_UNINLINEABLE);
     }
 
     /* Find fun's top-most activation record. */
     StackFrame *fp;
     for (fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
          fp && (fp->maybeFun() != fun || fp->isDirectEvalOrDebuggerFrame());
          fp = fp->prev()) {
         continue;
     }
 
 #ifdef JS_METHODJIT
     if (slot == FUN_CALLER && fp && fp->prev()) {
-        /* Also make sure the caller is uninlineable. */
+        /*
+         * 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);
         if (inlined) {
             JSFunction *fun = fp->prev()->jit()->inlineFrames()[inlined->inlineIndex].fun;
             MarkTypeObjectFlags(cx, fun->getType(), OBJECT_FLAG_UNINLINEABLE);
         }
     }
 #endif
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -4732,30 +4732,28 @@ MarkTypeCallerUnexpectedSlow(JSContext *
      * Check that we are actually at a scripted callsite. This function is
      * called from JS natives which can be called anywhere a script can be
      * called, such as on property getters or setters. This filtering is not
      * perfect, but we only need to make sure the type result is added wherever
      * the native's type handler was used, i.e. at scripted callsites directly
      * calling the native.
      */
 
-    StackFrame *caller = js_GetScriptedCaller(cx, NULL);
-    if (!caller)
+    jsbytecode *pc;
+    JSScript *script = cx->stack.currentScript(&pc);
+    if (!script)
         return;
 
     /*
      * Watch out if the caller is in a different compartment from this one.
      * This must have gone through a cross-compartment wrapper.
      */
-    if (caller->script()->compartment != cx->compartment)
+    if (script->compartment != cx->compartment)
         return;
 
-    JSScript *script;
-    jsbytecode *pc = caller->inlinepc(cx, &script);
-
     js::analyze::UntrapOpcode untrap(cx, script, pc);
 
     switch ((JSOp)*pc) {
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW:
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -41,16 +41,18 @@
 
 #include "jsarray.h"
 #include "jsanalyze.h"
 #include "jscompartment.h"
 #include "jsinfer.h"
 #include "jsprf.h"
 #include "vm/GlobalObject.h"
 
+#include "vm/Stack-inl.h"
+
 #ifndef jsinferinlines_h___
 #define jsinferinlines_h___
 
 /////////////////////////////////////////////////////////////////////
 // Types
 /////////////////////////////////////////////////////////////////////
 
 namespace js {
@@ -260,22 +262,20 @@ GetTypeNewObject(JSContext *cx, JSProtoK
     return proto->getNewType(cx);
 }
 
 /* Get a type object for the immediate allocation site within a native. */
 inline TypeObject *
 GetTypeCallerInitObject(JSContext *cx, bool isArray)
 {
     if (cx->typeInferenceEnabled()) {
-        StackFrame *caller = js_GetScriptedCaller(cx, NULL);
-        if (caller && caller->script()->compartment == cx->compartment) {
-            JSScript *script;
-            jsbytecode *pc = caller->inlinepc(cx, &script);
+        jsbytecode *pc;
+        JSScript *script = cx->stack.currentScript(&pc);
+        if (script && script->compartment == cx->compartment)
             return script->getTypeInitObject(cx, pc, isArray);
-        }
     }
     return GetTypeNewObject(cx, isArray ? JSProto_Array : JSProto_Object);
 }
 
 /* Mark the immediate scripted caller of a native as having produced an unexpected value. */
 inline void
 MarkTypeCallerUnexpected(JSContext *cx, jstype type)
 {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -108,16 +108,20 @@ using namespace js::gc;
 using namespace js::types;
 
 /* jsinvoke_cpp___ indicates inclusion from jsinvoke.cpp. */
 #if !JS_LONE_INTERPRET ^ defined jsinvoke_cpp___
 
 JSObject *
 js::GetScopeChain(JSContext *cx)
 {
+    /*
+     * Note: we don't need to expand inline frames here, because frames are
+     * only inlined when the caller and callee share the same scope chain.
+     */
     StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
     if (!fp) {
         /*
          * There is no code active on this context. In place of an actual
          * scope chain, use the context's global object, which is set in
          * js_InitFunctionAndObjectClasses, and which represents the default
          * scope chain for the embedding. See also js_FindClassObject.
          *
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1245,24 +1245,24 @@ EvalKernel(JSContext *cx, const CallArgs
  * We once supported a second argument to eval to use as the scope chain
  * when evaluating the code string.  Warn when such uses are seen so that
  * authors will know that support for eval(s, o) has been removed.
  */
 static inline bool
 WarnOnTooManyArgs(JSContext *cx, const CallArgs &call)
 {
     if (call.argc() > 1) {
-        if (StackFrame *caller = js_GetScriptedCaller(cx, NULL)) {
-            if (!caller->script()->warnedAboutTwoArgumentEval) {
+        if (JSScript *script = cx->stack.currentScript()) {
+            if (!script->warnedAboutTwoArgumentEval) {
                 static const char TWO_ARGUMENT_WARNING[] =
                     "Support for eval(code, scopeObject) has been removed. "
                     "Use |with (scopeObject) eval(code);| instead.";
                 if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
                     return false;
-                caller->script()->warnedAboutTwoArgumentEval = true;
+                script->warnedAboutTwoArgumentEval = true;
             }
         } else {
             /*
              * In the case of an indirect call without a caller frame, avoid a
              * potential warning-flood by doing nothing.
              */
         }
     }
@@ -1337,18 +1337,18 @@ PrincipalsForCompiledCode(const CallArgs
      * 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 (StackFrame *caller = js_GetScriptedCaller(cx, NULL)) {
-            if (JSPrincipals *callerPrincipals = caller->scopeChain().principals(cx)) {
+        if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) {
+            if (JSPrincipals *callerPrincipals = scopeChain->principals(cx)) {
                 JS_ASSERT(callerPrincipals->subsume(callerPrincipals, calleePrincipals));
             }
         }
     }
 #endif
 
     return calleePrincipals;
 }
@@ -1358,18 +1358,18 @@ PrincipalsForCompiledCode(const CallArgs
 #if JS_HAS_OBJ_WATCHPOINT
 
 static JSBool
 obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
                   jsval *nvp, void *closure)
 {
     JSObject *callable = (JSObject *) closure;
     if (JSPrincipals *watcher = callable->principals(cx)) {
-        if (StackFrame *caller = js_GetScriptedCaller(cx, NULL)) {
-            if (JSPrincipals *subject = caller->scopeChain().principals(cx)) {
+        if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) {
+            if (JSPrincipals *subject = scopeChain->principals(cx)) {
                 if (!watcher->subsume(watcher, subject)) {
                     /* Silently don't call the watch handler. */
                     return JS_TRUE;
                 }
             }
         }
     }
 
@@ -3129,22 +3129,21 @@ JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR
 /*
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "object-detecting" in the sense used by web scripts, e.g., when
  * checking whether document.all is defined.
  */
 JS_REQUIRES_STACK JSBool
 Detecting(JSContext *cx, jsbytecode *pc)
 {
-    JSScript *script;
     jsbytecode *endpc;
     JSOp op;
     JSAtom *atom;
 
-    cx->fp()->inlinepc(cx, &script);
+    JSScript *script = cx->stack.currentScript();
     endpc = script->code + script->length;
     for (;; pc += js_CodeSpec[op].length) {
         JS_ASSERT_IF(!cx->fp()->hasImacropc(), script->code <= pc && pc < endpc);
 
         /* General case: a branch or equality op follows the access. */
         op = js_GetOpcode(cx, script, pc);
         if (js_CodeSpec[op].format & JOF_DETECTING)
             return JS_TRUE;
@@ -3200,28 +3199,25 @@ js_InferFlags(JSContext *cx, uintN defau
 {
 #ifdef JS_TRACER
     if (JS_ON_TRACE(cx))
         return JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit->lookupFlags;
 #endif
 
     JS_ASSERT_NOT_ON_TRACE(cx);
 
-    jsbytecode *pc;
     const JSCodeSpec *cs;
     uint32 format;
     uintN flags = 0;
 
-    StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
-    if (!fp || !(pc = cx->regs().pc))
+    jsbytecode *pc;
+    JSScript *script = cx->stack.currentScript(&pc);
+    if (!script || !pc)
         return defaultFlags;
 
-    JSScript *script;
-    pc = fp->inlinepc(cx, &script);
-
     cs = &js_CodeSpec[js_GetOpcode(cx, script, pc)];
     format = cs->format;
     if (JOF_MODE(format) != JOF_NAME)
         flags |= JSRESOLVE_QUALIFIED;
     if (format & (JOF_SET | JOF_FOR)) {
         flags |= JSRESOLVE_ASSIGNING;
     } else if (cs->length >= 0) {
         pc += cs->length;
@@ -5465,17 +5461,17 @@ js_FindPropertyHelper(JSContext *cx, jsi
                       JSObject **objp, JSObject **pobjp, JSProperty **propp)
 {
     JSObject *scopeChain, *obj, *parent, *pobj;
     PropertyCacheEntry *entry;
     int scopeIndex;
     JSProperty *prop;
 
     JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
-    scopeChain = &js_GetTopStackFrame(cx, FRAME_EXPAND_NONE)->scopeChain();
+    scopeChain = cx->stack.currentScriptedScopeChain();
 
     /* Scan entries on the scope chain that we can cache across. */
     entry = JS_NO_PROP_CACHE_FILL;
     obj = scopeChain;
     parent = obj->getParent();
     for (scopeIndex = 0;
          parent
          ? IsCacheableNonGlobalScope(obj)
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2905,17 +2905,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                      * We must be in an eval called from jp->fun, where
                      * jp->script is the eval-compiled script.
                      *
                      * However, it's possible that a js_Invoke already
                      * pushed a frame trying to call Construct on an
                      * object that's not a constructor, causing us to be
                      * called with an intervening frame on the stack.
                      */
-                    StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
+                    StackFrame *fp = js_GetTopStackFrame(cx, FRAME_EXPAND_NONE);
                     if (fp) {
                         while (!fp->isEvalFrame())
                             fp = fp->prev();
                         JS_ASSERT(fp->script() == jp->script);
                         JS_ASSERT(fp->prev()->fun() == jp->fun);
                         JS_ASSERT(jp->fun->isInterpreted());
                         JS_ASSERT(jp->script != jp->fun->script());
                         JS_ASSERT(JSScript::isValidOffset(jp->script->upvarsOffset));
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -47,17 +47,16 @@
 using namespace js;
 
 JS_STATIC_ASSERT(sizeof(PCVal) == sizeof(jsuword));
 
 JS_REQUIRES_STACK PropertyCacheEntry *
 PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *pobj,
                     const Shape *shape, JSBool adding)
 {
-    jsbytecode *pc;
     jsuword kshape, vshape;
     JSOp op;
     const JSCodeSpec *cs;
     PCVal vword;
     PropertyCacheEntry *entry;
 
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(!cx->runtime->gcRunning);
@@ -122,18 +121,18 @@ PropertyCache::fill(JSContext *cx, JSObj
         PCMETER(longchains++);
         return JS_NO_PROP_CACHE_FILL;
     }
 
     /*
      * Optimize the cached vword based on our parameters and the current pc's
      * opcode format flags.
      */
-    JSScript *script;
-    pc = cx->fp()->inlinepc(cx, &script);
+    jsbytecode *pc;
+    JSScript *script = cx->stack.currentScript(&pc);
     op = js_GetOpcode(cx, script, pc);
     cs = &js_CodeSpec[op];
     kshape = 0;
 
     do {
         /*
          * Check for a prototype "plain old method" callee computation. What
          * is a plain old method? It's a function-valued property with stub
@@ -147,16 +146,20 @@ PropertyCache::fill(JSContext *cx, JSObj
                  */
                 JS_ASSERT(pobj->hasMethodBarrier());
                 JSObject &funobj = shape->methodObject();
                 JS_ASSERT(funobj == pobj->nativeGetSlot(shape->slot).toObject());
                 vword.setFunObj(funobj);
                 break;
             }
 
+            /*
+             * N.B. Objects are not branded if type inference is enabled, to
+             * allow property accesses without shape checks in JIT code.
+             */
             if (!pobj->generic() && shape->hasDefaultGetter() && pobj->containsSlot(shape->slot) &&
                 !cx->typeInferenceEnabled()) {
                 const Value &v = pobj->nativeGetSlot(shape->slot);
                 JSObject *funobj;
 
                 if (IsFunctionObject(v, &funobj)) {
                     /*
                      * Great, we have a function-valued prototype property
@@ -300,36 +303,32 @@ GetAtomFromBytecode(JSContext *cx, jsbyt
     if (op == JSOP_LENGTH)
         return cx->runtime->atomState.lengthAtom;
 
     // The method JIT's implementation of instanceof contains an internal lookup
     // of the prototype property.
     if (op == JSOP_INSTANCEOF)
         return cx->runtime->atomState.classPrototypeAtom;
 
-    JSScript *script;
-    cx->fp()->inlinepc(cx, &script);
-
+    JSScript *script = cx->stack.currentScript();
     ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
     JSAtom *atom;
     GET_ATOM_FROM_BYTECODE(script, pc, pcoff, atom);
     return atom;
 }
 
 JS_REQUIRES_STACK JSAtom *
 PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject **pobjp,
                         PropertyCacheEntry *entry)
 {
     JSObject *obj, *pobj, *tmp;
     uint32 vcap;
 
     StackFrame *fp = cx->fp();
-
-    JSScript *script;
-    fp->inlinepc(cx, &script);
+    JSScript *script = cx->stack.currentScript();
 
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
     JS_ASSERT(uintN((fp->hasImacropc() ? fp->imacropc() : pc) - script->code)
               < script->length);
 
     JSOp op = js_GetOpcode(cx, script, pc);
     const JSCodeSpec &cs = js_CodeSpec[op];
 
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -7436,17 +7436,17 @@ js_FindXMLProperty(JSContext *cx, const 
         JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass ||
                   nameobj->getClass() == &js_QNameClass);
     }
 
     qn = nameobj;
     if (!IsFunctionQName(cx, qn, &funid))
         return JS_FALSE;
 
-    obj = &js_GetTopStackFrame(cx, FRAME_EXPAND_NONE)->scopeChain();
+    obj = cx->stack.currentScriptedScopeChain();
     do {
         /* Skip any With object that can wrap XML. */
         target = obj;
         while (target->getClass() == &js_WithClass) {
              proto = target->getProto();
              if (!proto)
                  break;
              target = proto;
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -281,17 +281,17 @@ mjit::Compiler::scanInlineCalls(uint32 i
             continue;
 
         /*
          * Compute the maximum height we can grow the stack for inlined frames.
          * We always reserve space for loop temporaries, for an extra stack
          * frame pushed when making a call from the deepest inlined frame, and
          * for the temporary slot used by type barriers.
          */
-        uint32 stackLimit = outerScript->nslots + StackSpace::STACK_EXTRA
+        uint32 stackLimit = outerScript->nslots + StackSpace::STACK_JIT_EXTRA
             - VALUES_PER_STACK_FRAME - FrameState::TEMPORARY_LIMIT - 1;
 
         /* Compute the depth of any frames inlined at this site. */
         uint32 nextDepth = depth + VALUES_PER_STACK_FRAME + script->nfixed + code->stackDepth;
 
         /*
          * Scan each of the possible callees for other conditions precluding
          * inlining. We only inline at a call site if all callees are inlineable.
@@ -737,18 +737,17 @@ mjit::Compiler::generatePrologue()
             stubcc.crossJump(stubcc.masm.jump(), fastPath);
         }
 
         /*
          * Guard that there is enough stack space. Note we reserve space for
          * any inline frames we end up generating, or a callee's stack frame
          * we write to before the callee checks the stack.
          */
-        JS_STATIC_ASSERT(StackSpace::STACK_EXTRA >= VALUES_PER_STACK_FRAME);
-        uint32 nvals = script->nslots + VALUES_PER_STACK_FRAME + StackSpace::STACK_EXTRA;
+        uint32 nvals = VALUES_PER_STACK_FRAME + script->nslots + StackSpace::STACK_JIT_EXTRA;
         masm.addPtr(Imm32(nvals * sizeof(Value)), JSFrameReg, Registers::ReturnReg);
         Jump stackCheck = masm.branchPtr(Assembler::AboveOrEqual, Registers::ReturnReg,
                                          FrameAddress(offsetof(VMFrame, stackLimit)));
 
         /* If the stack check fails... */
         {
             stubcc.linkExitDirect(stackCheck, stubcc.masm.label());
             OOL_STUBCALL(stubs::HitStackQuota, REJOIN_NONE);
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -986,17 +986,17 @@ inline int32
 FrameState::frameOffset(const FrameEntry *fe, ActiveFrame *a) const
 {
     /*
      * The stored frame offsets for analysis temporaries are immediately above
      * the script's normal slots (and will thus be clobbered should a C++ or
      * scripted call push another frame). There must be enough room in the
      * reserved stack space.
      */
-    JS_STATIC_ASSERT(StackSpace::STACK_EXTRA >= TEMPORARY_LIMIT);
+    JS_STATIC_ASSERT(StackSpace::STACK_JIT_EXTRA >= TEMPORARY_LIMIT);
     JS_ASSERT(fe >= a->callee_ && fe < a->sp);
 
     if (fe >= a->locals)
         return StackFrame::offsetOfFixed(uint32(fe - a->locals));
     if (fe >= a->args)
         return StackFrame::offsetOfFormalArg(a->script->fun, uint32(fe - a->args));
     if (fe == a->this_)
         return StackFrame::offsetOfThis(a->script->fun);
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -85,17 +85,17 @@ FrameState::pruneDeadEntries()
     tracker.nentries -= shift;
 }
 
 bool
 FrameState::pushActiveFrame(JSScript *script, uint32 argc)
 {
     if (!a) {
         this->nentries = analyze::TotalSlots(script) + (script->nslots - script->nfixed) +
-            StackSpace::STACK_EXTRA - VALUES_PER_STACK_FRAME;
+            StackSpace::STACK_JIT_EXTRA - VALUES_PER_STACK_FRAME;
         size_t totalBytes = sizeof(FrameEntry) * nentries +       // entries[]
                             sizeof(FrameEntry *) * nentries +     // tracker.entries
                             sizeof(StackEntryExtra) * nentries;   // extraArray
         uint8 *cursor = (uint8 *)cx->calloc_(totalBytes);
         if (!cursor)
             return false;
 
         this->entries = (FrameEntry *) cursor;
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -208,18 +208,17 @@ static inline void
 RemovePartialFrame(JSContext *cx, StackFrame *fp)
 {
     cx->stack.popInlineFrame();
 }
 
 static inline bool
 CheckStackQuota(VMFrame &f)
 {
-    /* Include extra space for any inline frames. */
-    uint32 nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME + StackSpace::STACK_EXTRA;
+    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;
         }
@@ -262,16 +261,23 @@ stubs::FixupArity(VMFrame &f, uint32 nac
 
     /* 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);
+
+    /*
+     * 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)
         THROWV(NULL);
 
     /* Reset the part of the stack frame set by the caller. */
     newfp->initCallFrameCallerHalf(cx, flags, ncode);
 
     /* Reset the part of the stack frame set by the prologue up to now. */
     newfp->initCallFrameEarlyPrologue(fun, nactual);
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -42,16 +42,17 @@
 #define Stack_inl_h__
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "Stack.h"
 
 #include "ArgumentsObject-inl.h"
+#include "methodjit/MethodJIT.h"
 
 namespace js {
 
 /*****************************************************************************/
 
 /* See VM stack layout comment in Stack.h. */
 class StackSegment
 {
@@ -798,17 +799,17 @@ StackSpace::ensureSpace(JSContext *maybe
     return true;
 #endif
 }
 
 inline Value *
 StackSpace::getStackLimit(JSContext *cx)
 {
     FrameRegs &regs = cx->regs();
-    uintN minSpace = regs.fp()->numSlots() + STACK_EXTRA;
+    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))
         return NULL;
@@ -850,30 +851,24 @@ struct LimitCheck
 
     LimitCheck(StackFrame *base, Value **limit, void *topncode)
         : base(base), limit(limit), topncode(topncode)
     {}
 
     JS_ALWAYS_INLINE bool
     operator()(JSContext *cx, StackSpace &space, Value *from, uintN nvals)
     {
-        /*
-         * Include extra space for a new stack frame, inlined frames and loop
-         * temporaries to satisfy the method-jit stackLimit invariant.
-         */
-        nvals += StackSpace::STACK_EXTRA + VALUES_PER_STACK_FRAME;
-
         JS_ASSERT(from < *limit);
         if (*limit - from >= ptrdiff_t(nvals))
             return true;
 
         if (topncode) {
             /*
              * The current regs.pc may not be intact, set it in case bumping
-             * the limit fails.
+             * the limit fails. See FixupArity.
              */
             cx->regs().updateForNcode(cx->fp()->jit(), topncode);
         }
 
         return space.bumpLimitWithinQuota(cx, base, from, nvals, limit);
     }
 };
 
@@ -884,17 +879,17 @@ JS_ALWAYS_INLINE StackFrame *
 ContextStack::getCallFrame(JSContext *cx, Value *firstUnused, uintN nactual,
                            JSFunction *fun, JSScript *script, uint32 *flags,
                            Check check) const
 {
     JS_ASSERT(fun->script() == script);
     JS_ASSERT(space().firstUnused() == firstUnused);
 
     /* Include extra space for inlining by the method-jit. */
-    uintN nvals = StackSpace::STACK_EXTRA + script->nslots;
+    uintN nvals = VALUES_PER_STACK_FRAME + script->nslots + StackSpace::STACK_JIT_EXTRA;
     uintN nformal = fun->nargs;
 
     /* Maintain layout invariant: &formalArgs[0] == ((Value *)fp) - nformal. */
 
     if (nactual == nformal) {
         if (JS_UNLIKELY(!check(cx, space(), firstUnused, nvals)))
             return NULL;
         return reinterpret_cast<StackFrame *>(firstUnused);
@@ -1088,16 +1083,50 @@ ContextStack::findFrameAtLevel(uintN tar
         JS_ASSERT(fp && fp->isScriptFrame());
         if (fp->script()->staticLevel == targetLevel)
             break;
         fp = fp->prev();
     }
     return fp;
 }
 
+inline JSScript *
+ContextStack::currentScript(jsbytecode **ppc) const
+{
+    StackFrame *fp = regs_->fp();
+    if (!fp) {
+        if (ppc)
+            *ppc = NULL;
+        return NULL;
+    }
+    while (fp->isDummyFrame())
+        fp = fp->prev();
+
+#ifdef JS_METHODJIT
+    mjit::CallSite *inlined = regs_->inlined();
+    if (inlined) {
+        JS_ASSERT(inlined->inlineIndex < fp->jit()->nInlineFrames);
+        mjit::InlineFrame *frame = &fp->jit()->inlineFrames()[inlined->inlineIndex];
+        if (ppc)
+            *ppc = frame->fun->script()->code + inlined->pcOffset;
+        return frame->fun->script();
+    }
+#endif
+
+    if (ppc)
+        *ppc = regs_->pc;
+    return fp->script();
+}
+
+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())) {
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -103,36 +103,16 @@ StackFrame::pc(JSContext *cx, StackFrame
             *pinlined = regs.inlined();
         return regs.pc;
     }
     if (!next)
         next = seg.computeNextFrame(this);
     return next->prevpc(pinlined);
 }
 
-jsbytecode *
-StackFrame::inlinepc(JSContext *cx, JSScript **pscript)
-{
-    JSInlinedSite *inlined;
-    jsbytecode *pc = this->pc(cx, NULL, &inlined);
-
-#ifdef JS_METHODJIT
-    if (inlined) {
-        JS_ASSERT(inlined->inlineIndex < jit()->nInlineFrames);
-        js::mjit::InlineFrame *frame = &jit()->inlineFrames()[inlined->inlineIndex];
-        *pscript = frame->fun->script();
-        return (*pscript)->code + inlined->pcOffset;
-    }
-#endif
-
-    JS_ASSERT(!inlined);
-    *pscript = script();
-    return pc;
-}
-
 /*****************************************************************************/
 
 JS_REQUIRES_STACK bool
 StackSegment::contains(const StackFrame *fp) const
 {
     JS_ASSERT(!empty());
 
     if (fp < initialFrame_)
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -438,30 +438,29 @@ class StackFrame
     }
 
     /*
      * Script
      *
      * All function and global frames have an associated JSScript which holds
      * the bytecode being executed for the frame. This script/bytecode does
      * not reflect any inlining that has been performed by the method JIT.
-     *
      * If other frames were inlined into this one, the script/pc reflect the
-     * point of the outermost call. Use inlinepc to get the script/pc for
-     * the innermost inlined frame. Inlined frame invariants:
+     * point of the outermost call. Inlined frame invariants:
      *
      * - Inlined frames have the same scope chain as the outer frame.
      * - Inlined frames have the same strictness as the outer frame.
      */
 
     /*
-     * 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 state
-     * of any inlined frame(s) is returned through pinlined.
+     * 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.
      */
     jsbytecode *pc(JSContext *cx, StackFrame *next = NULL, JSInlinedSite **pinlined = NULL);
 
     jsbytecode *prevpc(JSInlinedSite **pinlined) {
         if (flags_ & HAS_PREVPC) {
             if (pinlined)
                 *pinlined = prevInline_;
             return prevpc_;
@@ -469,19 +468,16 @@ class StackFrame
         return prevpcSlow(pinlined);
     }
 
     JSInlinedSite *prevInline() {
         JS_ASSERT(flags_ & HAS_PREVPC);
         return prevInline_;
     }
 
-    /* Get the innermost pc/script in this frame, looking through any inlining. */
-    jsbytecode *inlinepc(JSContext *cx, JSScript **pscript);
-
     JSScript *script() const {
         JS_ASSERT(isScriptFrame());
         return isFunctionFrame()
                ? isEvalFrame() ? args.script : fun()->script()
                : exec.script;
     }
 
     JSScript *functionScript() const {
@@ -1190,20 +1186,21 @@ class StackSpace
      * 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 before invoking the method JIT.
-     * This may be used for inlined stack frames.
+     * 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.
      */
-    static const size_t STACK_EXTRA = (VALUES_PER_STACK_FRAME + 18) * 10;
+    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
@@ -1410,16 +1407,22 @@ class ContextStack
     getInlineFrameWithinLimit(JSContext *cx, Value *sp, uintN nactual,
                               JSFunction *fun, JSScript *script, uint32 *flags,
                               StackFrame *base, 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;
+
+    /* Get the scope chain for the topmost scripted call on the stack. */
+    inline JSObject *currentScriptedScopeChain() const;
 };
 
 /*****************************************************************************/
 
 class InvokeArgsGuard : public CallArgs
 {
     friend class ContextStack;
     ContextStack     *stack_;  /* null implies nothing pushed */