Bug 832373 part 1 - Refactor eval-in-frame to use AbstractFramePtr. r=luke
authorJan de Mooij <jdemooij@mozilla.com>
Mon, 21 Jan 2013 13:58:50 +0100
changeset 119423 1392d241c13719f919a24521f5a00435e8614b04
parent 119422 b8791be70fd0b66ec406b0f0404be2f48a079f5c
child 119424 5ea59ba8d60439f20074c6873e74f910a43d9874
push id24204
push usereakhgari@mozilla.com
push dateTue, 22 Jan 2013 17:50:00 +0000
treeherdermozilla-central@8962a7fabc33 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs832373
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 832373 part 1 - Refactor eval-in-frame to use AbstractFramePtr. r=luke
js/src/builtin/Eval.cpp
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jsapi.cpp
js/src/jsinterp.cpp
js/src/jsinterp.h
js/src/jsinterpinlines.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -151,20 +151,20 @@ enum EvalType { DIRECT_EVAL = EXECUTE_DI
 //
 // Evaluate call.argv[2], if it is a string, in the context of the given calling
 // frame, with the provided scope chain, with the semantics of either a direct
 // or indirect eval (see ES5 10.4.2).  If this is an indirect eval, scopeobj
 // must be a global object.
 //
 // On success, store the completion value in call.rval and return true.
 static bool
-EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
+EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFramePtr caller,
            HandleObject scopeobj)
 {
-    JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
+    JS_ASSERT((evalType == INDIRECT_EVAL) == !caller);
     JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal());
     AssertInnerizedScopeChain(cx, *scopeobj);
 
     if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
         return false;
     }
 
@@ -182,25 +182,25 @@ EvalKernel(JSContext *cx, const CallArgs
     // ES5 15.1.2.1 steps 2-8.
 
     // Per ES5, indirect eval runs in the global scope. (eval is specified this
     // 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'.)
     unsigned staticLevel;
     RootedValue thisv(cx);
     if (evalType == DIRECT_EVAL) {
-        JS_ASSERT(!caller->runningInIon());
-        staticLevel = caller->script()->staticLevel + 1;
+        JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInIon());
+        staticLevel = caller.script()->staticLevel + 1;
 
         // Direct calls to eval are supposed to see the caller's |this|. If we
         // haven't wrapped that yet, do so now, before we make a copy of it for
         // the eval code to use.
         if (!ComputeThis(cx, caller))
             return false;
-        thisv = caller->thisValue();
+        thisv = caller.thisValue();
     } else {
         JS_ASSERT(args.callee().global() == *scopeobj);
         staticLevel = 0;
 
         // Use the global as 'this', modulo outerization.
         JSObject *thisobj = JSObject::thisObject(cx, scopeobj);
         if (!thisobj)
             return false;
@@ -221,17 +221,17 @@ EvalKernel(JSContext *cx, const CallArgs
     //
     // Don't use the JSON parser if the caller is strict mode code, because in
     // strict mode object literals must not have repeated properties, and the
     // JSON parser cheerfully (and correctly) accepts them.  If you're parsing
     // JSON with eval and using strict mode, you deserve to be slow.
     if (length > 2 &&
         ((chars[0] == '[' && chars[length - 1] == ']') ||
         (chars[0] == '(' && chars[length - 1] == ')')) &&
-         (!caller || !caller->script()->strict))
+         (!caller || !caller.script()->strict))
     {
         // Remarkably, JavaScript syntax is not a superset of JSON syntax:
         // strings in JavaScript cannot contain the Unicode line and paragraph
         // terminator characters U+2028 and U+2029, but strings in JSON can.
         // Rather than force the JSON parser to handle this quirk when used by
         // eval, we simply don't use the JSON parser when either character
         // appears in the provided string.  See bug 657367.
         for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
@@ -252,18 +252,18 @@ EvalKernel(JSContext *cx, const CallArgs
             }
         }
     }
 
     EvalScriptGuard esg(cx);
 
     JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
 
-    if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
-        esg.lookupInEvalCache(stableStr, caller->fun(), staticLevel);
+    if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
+        esg.lookupInEvalCache(stableStr, caller.fun(), staticLevel);
 
     if (!esg.foundScript()) {
         unsigned lineno;
         const char *filename;
         JSPrincipals *originPrincipals;
         CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
                                     evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
                                                             : NOT_CALLED_FROM_JSOP_EVAL);
@@ -278,17 +278,17 @@ EvalKernel(JSContext *cx, const CallArgs
                                                           chars, length, stableStr, staticLevel);
         if (!compiled)
             return false;
 
         esg.setNewScript(compiled);
     }
 
     return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType),
-                         NULL /* evalInFrame */, args.rval().address());
+                         NullFramePtr() /* evalInFrame */, args.rval().address());
 }
 
 // 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 &args)
 {
@@ -313,17 +313,17 @@ WarnOnTooManyArgs(JSContext *cx, const C
 JSBool
 js::IndirectEval(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!WarnOnTooManyArgs(cx, args))
         return false;
 
     Rooted<GlobalObject*> global(cx, &args.callee().global());
-    return EvalKernel(cx, args, INDIRECT_EVAL, NULL, global);
+    return EvalKernel(cx, args, INDIRECT_EVAL, NullFramePtr(), global);
 }
 
 bool
 js::DirectEval(JSContext *cx, const CallArgs &args)
 {
     // Direct eval can assume it was called from an interpreted frame.
     StackFrame *caller = cx->fp();
     JS_ASSERT(IsBuiltinEvalForScope(caller->scopeChain(), args.calleev()));
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -42,17 +42,17 @@ SetSourceMap(JSContext *cx, TokenStream 
     if (tokenStream.hasSourceMap()) {
         if (!ss->setSourceMap(cx, tokenStream.releaseSourceMap(), script->filename))
             return false;
     }
     return true;
 }
 
 UnrootedScript
-frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
+frontend::CompileScript(JSContext *cx, HandleObject scopeChain, AbstractFramePtr callerFrame,
                         const CompileOptions &options,
                         StableCharPtr chars, size_t length,
                         JSString *source_ /* = NULL */,
                         unsigned staticLevel /* = 0 */)
 {
     RootedString source(cx, source_);
 
     class ProbesManager
@@ -101,17 +101,17 @@ frontend::CompileScript(JSContext *cx, H
     parser.sct = &sct;
 
     GlobalSharedContext globalsc(cx, scopeChain, StrictModeFromContext(cx));
 
     ParseContext pc(&parser, &globalsc, staticLevel, /* bodyid = */ 0);
     if (!pc.init())
         return UnrootedScript(NULL);
 
-    bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame->isFunctionFrame();
+    bool savedCallerFun = options.compileAndGo && callerFrame && callerFrame.isFunctionFrame();
     Rooted<JSScript*> script(cx, JSScript::Create(cx, NullPtr(), savedCallerFun,
                                                   options, staticLevel, ss, 0, length));
     if (!script)
         return UnrootedScript(NULL);
 
     // Global/eval script bindings are always empty (all names are added to the
     // scope dynamically via JSOP_DEFFUN/VAR).
     InternalHandle<Bindings*> bindings(script, &script->bindings);
@@ -124,38 +124,38 @@ frontend::CompileScript(JSContext *cx, H
     JS_ASSERT_IF(globalScope, JSCLASS_HAS_GLOBAL_FLAG_AND_SLOTS(globalScope->getClass()));
 
     BytecodeEmitter bce(/* parent = */ NULL, &parser, &globalsc, script, callerFrame, !!globalScope,
                         options.lineno, options.selfHostingMode);
     if (!bce.init())
         return UnrootedScript(NULL);
 
     /* If this is a direct call to eval, inherit the caller's strictness.  */
-    if (callerFrame && callerFrame->script()->strict)
+    if (callerFrame && callerFrame.script()->strict)
         globalsc.strict = true;
 
     if (options.compileAndGo) {
         if (source) {
             /*
              * Save eval program source in script->atoms[0] for the
              * eval cache (see EvalCacheLookup in jsobj.cpp).
              */
             JSAtom *atom = AtomizeString(cx, source);
             jsatomid _;
             if (!atom || !bce.makeAtomIndex(atom, &_))
                 return UnrootedScript(NULL);
         }
 
-        if (callerFrame && callerFrame->isFunctionFrame()) {
+        if (callerFrame && callerFrame.isFunctionFrame()) {
             /*
              * An eval script in a caller frame needs to have its enclosing
              * function captured in case it refers to an upvar, and someone
              * wishes to decompile it while it's running.
              */
-            JSFunction *fun = callerFrame->fun();
+            JSFunction *fun = callerFrame.fun();
             ObjectBox *funbox = parser.newFunctionBox(fun, &pc, fun->strict());
             if (!funbox)
                 return UnrootedScript(NULL);
             bce.objectList.add(funbox);
         }
     }
 
     ParseNode *pn;
@@ -212,17 +212,17 @@ frontend::CompileScript(JSContext *cx, H
      */
     if (pn && onlyXML && !callerFrame) {
         parser.reportError(NULL, JSMSG_XML_WHOLE_PROGRAM);
         return UnrootedScript(NULL);
     }
 #endif
 
     // It's an error to use |arguments| in a function that has a rest parameter.
-    if (callerFrame && callerFrame->isFunctionFrame() && callerFrame->fun()->hasRest()) {
+    if (callerFrame && callerFrame.isFunctionFrame() && callerFrame.fun()->hasRest()) {
         HandlePropertyName arguments = cx->names().arguments;
         for (AtomDefnRange r = pc.lexdeps->all(); !r.empty(); r.popFront()) {
             if (r.front().key() == arguments) {
                 parser.reportError(NULL, JSMSG_ARGUMENTS_AND_REST);
                 return UnrootedScript(NULL);
             }
         }
     }
@@ -313,17 +313,18 @@ frontend::CompileFunctionBody(JSContext 
         // Reparse in strict mode.
         parser.tokenStream.seek(start);
         pn = parser.standaloneFunctionBody(fun, formals, script, fn, &funbox,
                                            /* strict = */ true);
         if (!pn)
             return false;
     }
 
-    BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script, /* callerFrame = */ NULL,
+    BytecodeEmitter funbce(/* parent = */ NULL, &parser, funbox, script,
+                           /* callerFrame = */ NullFramePtr(),
                            /* hasGlobalScope = */ false, options.lineno);
     if (!funbce.init())
         return false;
 
     if (!NameFunctions(cx, pn))
         return false;
 
     if (fn->pn_body) {
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -9,17 +9,17 @@
 #define BytecodeCompiler_h__
 
 #include "frontend/Parser.h"
 
 namespace js {
 namespace frontend {
 
 UnrootedScript
-CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
+CompileScript(JSContext *cx, HandleObject scopeChain, AbstractFramePtr callerFrame,
               const CompileOptions &options, StableCharPtr chars, size_t length,
               JSString *source_ = NULL, unsigned staticLevel = 0);
 
 bool
 CompileFunctionBody(JSContext *cx, HandleFunction fun, CompileOptions options,
                     const AutoNameVector &formals, StableCharPtr chars, size_t length);
 
 } /* namespace frontend */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -95,17 +95,17 @@ struct frontend::StmtInfoBCE : public St
 
     ptrdiff_t &guardJump() {
         JS_ASSERT(type == STMT_TRY || type == STMT_FINALLY);
         return continues;
     }
 };
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
-                                 HandleScript script, StackFrame *callerFrame, bool hasGlobalScope,
+                                 HandleScript script, AbstractFramePtr callerFrame, bool hasGlobalScope,
                                  unsigned lineno, bool selfHostingMode)
   : sc(sc),
     parent(parent),
     script(sc->context, script),
     parser(parser),
     callerFrame(callerFrame),
     topStmt(NULL),
     topScopeStmt(NULL),
@@ -1276,32 +1276,31 @@ BindNameToSlot(JSContext *cx, BytecodeEm
                     return false;
                 }
             }
             pn->setOp(op = JSOP_NAME);
         }
     }
 
     if (dn->pn_cookie.isFree()) {
-        StackFrame *caller = bce->callerFrame;
-        if (caller) {
+        if (AbstractFramePtr caller = bce->callerFrame) {
             JS_ASSERT(bce->script->compileAndGo);
 
             /*
              * Don't generate upvars on the left side of a for loop. See
              * bug 470758.
              */
             if (bce->emittingForInit)
                 return true;
 
             /*
              * If this is an eval in the global scope, then unbound variables
              * must be globals, so try to use GNAME ops.
              */
-            if (caller->isGlobalFrame() && TryConvertToGname(bce, pn, &op)) {
+            if (caller.isGlobalFrame() && TryConvertToGname(bce, pn, &op)) {
                 pn->setOp(op);
                 pn->pn_dflags |= PND_BOUND;
                 return true;
             }
 
             /*
              * Out of tricks, so we must rely on PICs to optimize named
              * accesses from direct eval called from function code.
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -78,17 +78,17 @@ struct BytecodeEmitter
         ptrdiff_t   lastNoteOffset; /* code offset for last source note */
         unsigned    currentLine;    /* line number for tree-based srcnote gen */
         unsigned    lastColumn;     /* zero-based column index on currentLine of
                                        last SRC_COLSPAN-annotated opcode */
     } prolog, main, *current;
 
     Parser          *const parser;  /* the parser */
 
-    StackFrame      *const callerFrame; /* scripted caller frame for eval and dbgapi */
+    AbstractFramePtr callerFrame;   /* scripted caller frame for eval and dbgapi */
 
     StmtInfoBCE     *topStmt;       /* top of statement info stack */
     StmtInfoBCE     *topScopeStmt;  /* top lexical scope statement */
     Rooted<StaticBlockObject *> blockChain;
                                     /* compile time block scope chain */
 
     OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
@@ -121,17 +121,17 @@ struct BytecodeEmitter
                                            global object */
 
     const bool      selfHostingMode:1;  /* Emit JSOP_CALLINTRINSIC instead of JSOP_NAME
                                            and assert that JSOP_NAME and JSOP_*GNAME
                                            don't ever get emitted. See the comment for
                                            the field |selfHostingMode| in Parser.h for details. */
 
     BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc,
-                    HandleScript script, StackFrame *callerFrame, bool hasGlobalScope,
+                    HandleScript script, AbstractFramePtr callerFrame, bool hasGlobalScope,
                     unsigned lineno, bool selfHostingMode = false);
     bool init();
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destructor call.
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -5281,17 +5281,17 @@ JS::Compile(JSContext *cx, HandleObject 
 
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JS_ASSERT_IF(options.principals, cx->compartment->principals == options.principals);
     AutoLastFrameCheck lfc(cx);
 
-    return frontend::CompileScript(cx, obj, NULL, options, StableCharPtr(chars, length), length);
+    return frontend::CompileScript(cx, obj, NullFramePtr(), options, StableCharPtr(chars, length), length);
 }
 
 JSScript *
 JS::Compile(JSContext *cx, HandleObject obj, CompileOptions options,
             const char *bytes, size_t length)
 {
     jschar *chars;
     if (options.utf8)
@@ -5634,17 +5634,17 @@ JS::Evaluate(JSContext *cx, HandleObject
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
     JS_ASSERT_IF(options.principals, cx->compartment->principals == options.principals);
 
     AutoLastFrameCheck lfc(cx);
 
     options.setCompileAndGo(true);
     options.setNoScriptRval(!rval);
-    RootedScript script(cx, frontend::CompileScript(cx, obj, NULL, options,
+    RootedScript script(cx, frontend::CompileScript(cx, obj, NullFramePtr(), options,
                                                     StableCharPtr(chars, length), length));
     if (!script)
         return false;
 
     JS_ASSERT(script->getVersion() == options.version);
 
     return Execute(cx, script, *obj, rval);
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -509,17 +509,17 @@ js::InvokeGetterOrSetter(JSContext *cx, 
      */
     JS_CHECK_RECURSION(cx, return false);
 
     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
 }
 
 bool
 js::ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Value &thisv,
-                  ExecuteType type, StackFrame *evalInFrame, Value *result)
+                  ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
 {
     JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
     JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChain.isScope());
 
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
@@ -569,17 +569,17 @@ js::Execute(JSContext *cx, HandleScript 
 
     /* Use the scope chain as 'this', modulo outerization. */
     JSObject *thisObj = JSObject::thisObject(cx, scopeChain);
     if (!thisObj)
         return false;
     Value thisv = ObjectValue(*thisObj);
 
     return ExecuteKernel(cx, script, *scopeChain, thisv, EXECUTE_GLOBAL,
-                         NULL /* evalInFrame */, rval);
+                         NullFramePtr() /* evalInFrame */, rval);
 }
 
 bool
 js::HasInstance(JSContext *cx, HandleObject obj, HandleValue v, JSBool *bp)
 {
     Class *clasp = obj->getClass();
     RootedValue local(cx, v);
     if (clasp->hasInstance)
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -72,17 +72,17 @@ BoxNonStrictThis(JSContext *cx, MutableH
 
 /*
  * Ensure that fp->thisValue() is the correct value of |this| for the scripted
  * call represented by |fp|. ComputeThis is necessary because fp->thisValue()
  * may be set to 'undefined' when 'this' should really be the global object (as
  * an optimization to avoid global-this computation).
  */
 inline bool
-ComputeThis(JSContext *cx, StackFrame *fp);
+ComputeThis(JSContext *cx, AbstractFramePtr frame);
 
 enum MaybeConstruct {
     NO_CONSTRUCT = INITIAL_NONE,
     CONSTRUCT = INITIAL_CONSTRUCT
 };
 
 extern bool
 ReportIsNotFunction(JSContext *cx, const Value &v, MaybeConstruct construct = NO_CONSTRUCT);
@@ -155,17 +155,17 @@ InvokeConstructor(JSContext *cx, const V
 /*
  * Executes a script with the given scopeChain/this. The 'type' indicates
  * whether this is eval code or global code. To support debugging, the
  * evalFrame parameter can point to an arbitrary frame in the context's call
  * stack to simulate executing an eval in that frame.
  */
 extern bool
 ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChain, const Value &thisv,
-              ExecuteType type, StackFrame *evalInFrame, Value *result);
+              ExecuteType type, AbstractFramePtr evalInFrame, Value *result);
 
 /* Execute a script with the given scopeChain as global code. */
 extern bool
 Execute(JSContext *cx, HandleScript script, JSObject &scopeChain, Value *rval);
 
 /* Flags to toggle js::Interpret() execution. */
 enum InterpMode
 {
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -77,35 +77,40 @@ ComputeImplicitThis(JSContext *cx, Handl
     if (!nobj)
         return false;
 
     vp->setObject(*nobj);
     return true;
 }
 
 inline bool
-ComputeThis(JSContext *cx, StackFrame *fp)
+ComputeThis(JSContext *cx, AbstractFramePtr frame)
 {
-    JS_ASSERT(!fp->runningInIon());
-    Value &thisv = fp->thisValue();
-    if (thisv.isObject())
+    JS_ASSERT_IF(frame.isStackFrame(), !frame.asStackFrame()->runningInIon());
+    if (frame.thisValue().isObject())
         return true;
-    if (fp->isFunctionFrame()) {
-        if (fp->fun()->strict() || fp->fun()->isSelfHostedBuiltin())
+    RootedValue thisv(cx, frame.thisValue());
+    if (frame.isFunctionFrame()) {
+        if (frame.fun()->strict() || frame.fun()->isSelfHostedBuiltin())
             return true;
         /*
          * Eval function frames have their own |this| slot, which is a copy of the function's
          * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
          * eval's frame will get the wrapper, but the function's frame will not. To prevent
          * this, we always wrap a function's |this| before pushing an eval frame, and should
          * thus never see an unwrapped primitive in a non-strict eval function frame.
          */
-        JS_ASSERT(!fp->isEvalFrame());
+        JS_ASSERT(!frame.isEvalFrame());
     }
-    return BoxNonStrictThis(cx, fp->callReceiver());
+    bool modified;
+    if (!BoxNonStrictThis(cx, &thisv, &modified))
+        return false;
+
+    frame.thisValue() = thisv;
+    return true;
 }
 
 /*
  * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined
  * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when
  * one is received, act as if the value were the function's ArgumentsObject.
  * Additionally, it is possible that, after 'arguments' was copied into a
  * temporary, the arguments object has been created a some other failed guard
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -3678,50 +3678,50 @@ DebuggerFrame_setOnPop(JSContext *cx, un
     return true;
 }
 
 /*
  * Evaluate |chars[0..length-1]| in the environment |env|, treating that
  * source as appearing starting at |lineno| in |filename|. Store the return
  * value in |*rval|. Use |thisv| as the 'this' value.
  *
- * If |fp| is non-NULL, evaluate as for a direct eval in that frame; |env|
- * must be either |fp|'s DebugScopeObject, or some extension of that
- * environment; either way, |fp|'s scope is where newly declared variables
- * go. In this case, |fp| must have a computed 'this' value, equal to |thisv|.
+ * If |frame| is non-NULL, evaluate as for a direct eval in that frame; |env|
+ * must be either |frame|'s DebugScopeObject, or some extension of that
+ * environment; either way, |frame|'s scope is where newly declared variables
+ * go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
  */
 JSBool
-js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, StackFrame *fp,
+js::EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
                   StableCharPtr chars, unsigned length, const char *filename, unsigned lineno,
                   Value *rval)
 {
-    assertSameCompartment(cx, env, fp);
-    JS_ASSERT_IF(fp, thisv.get() == fp->thisValue());
+    assertSameCompartment(cx, env, frame);
+    JS_ASSERT_IF(frame, thisv.get() == frame.thisValue());
 
     JS_ASSERT(!IsPoisonedPtr(chars.get()));
 
     /*
      * NB: This function breaks the assumption that the compiler can see all
      * calls and properly compute a static level. In practice, any non-zero
      * static level will suffice.
      */
     CompileOptions options(cx);
     options.setPrincipals(env->compartment()->principals)
            .setCompileAndGo(true)
            .setNoScriptRval(false)
            .setFileAndLine(filename, lineno);
-    RootedScript script(cx, frontend::CompileScript(cx, env, fp, options, chars, length,
+    RootedScript script(cx, frontend::CompileScript(cx, env, frame, options, chars, length,
                                                     /* source = */ NULL,
-                                                    /* staticLevel = */ fp ? 1 : 0));
+                                                    /* staticLevel = */ frame ? 1 : 0));
     if (!script)
         return false;
 
     script->isActiveEval = true;
-    ExecuteType type = !fp && env->isGlobal() ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG;
-    return ExecuteKernel(cx, script, *env, thisv, type, fp, rval);
+    ExecuteType type = !frame && env->isGlobal() ? EXECUTE_DEBUG_GLOBAL : EXECUTE_DEBUG;
+    return ExecuteKernel(cx, script, *env, thisv, type, frame, rval);
 }
 
 static JSBool
 DebuggerGenericEval(JSContext *cx, const char *fullMethodName,
                     const Value &code, Value *bindings, MutableHandleValue vp,
                     Debugger *dbg, HandleObject scope, ScriptFrameIter *iter)
 {
     /* Either we're specifying the frame, or a global. */
@@ -3801,18 +3801,18 @@ DebuggerGenericEval(JSContext *cx, const
                 return false;
             }
         }
     }
 
     /* Run the code and produce the completion value. */
     Value rval;
     JS::Anchor<JSString *> anchor(stable);
-    StackFrame *fp = iter ? iter->interpFrame() : NULL;
-    bool ok = EvaluateInEnv(cx, env, thisv, fp, stable->chars(), stable->length(),
+    AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
+    bool ok = EvaluateInEnv(cx, env, thisv, frame, stable->chars(), stable->length(),
                             "debugger eval code", 1, &rval);
     return dbg->receiveCompletionValue(ac, ok, rval, vp);
 }
 
 static JSBool
 DebuggerFrame_eval(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_FRAME(cx, argc, vp, "eval", args, thisobj, iter);
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -686,15 +686,15 @@ bool
 Debugger::onNewGlobalObject(JSContext *cx, Handle<GlobalObject *> global)
 {
     if (JS_CLIST_IS_EMPTY(&cx->runtime->onNewGlobalObjectWatchers))
         return true;
     return Debugger::slowPathOnNewGlobalObject(cx, global);
 }
 
 extern JSBool
-EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, StackFrame *fp,
+EvaluateInEnv(JSContext *cx, Handle<Env*> env, HandleValue thisv, AbstractFramePtr frame,
               StableCharPtr chars, unsigned length, const char *filename, unsigned lineno,
               Value *rval);
 
 }
 
 #endif /* Debugger_h__ */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -876,17 +876,17 @@ js::CloneStaticBlockObject(JSContext *cx
 
     return clone;
 }
 
 /*****************************************************************************/
 
 ScopeIter::ScopeIter(JSContext *cx
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : frame_(),
+  : frame_(NullFramePtr()),
     cur_(cx, reinterpret_cast<JSObject *>(-1)),
     block_(cx, reinterpret_cast<StaticBlockObject *>(-1)),
     type_(Type(-1))
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 ScopeIter::ScopeIter(const ScopeIter &si, JSContext *cx
@@ -897,17 +897,17 @@ ScopeIter::ScopeIter(const ScopeIter &si
     type_(si.type_),
     hasScopeObject_(si.hasScopeObject_)
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 ScopeIter::ScopeIter(JSObject &enclosingScope, JSContext *cx
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
-  : frame_(),
+  : frame_(NullFramePtr()),
     cur_(cx, &enclosingScope),
     block_(cx, reinterpret_cast<StaticBlockObject *>(-1)),
     type_(Type(-1))
 {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 ScopeIter::ScopeIter(AbstractFramePtr frame, JSContext *cx
@@ -981,33 +981,33 @@ ScopeIter::operator++()
     JS_ASSERT(!done());
     switch (type_) {
       case Call:
         if (hasScopeObject_) {
             cur_ = &cur_->asCall().enclosingScope();
             if (CallObjectLambdaName(*frame_.fun()))
                 cur_ = &cur_->asDeclEnv().enclosingScope();
         }
-        frame_ = AbstractFramePtr();
+        frame_ = NullFramePtr();
         break;
       case Block:
         block_ = block_->enclosingBlock();
         if (hasScopeObject_)
             cur_ = &cur_->asClonedBlock().enclosingScope();
         settle();
         break;
       case With:
         JS_ASSERT(hasScopeObject_);
         cur_ = &cur_->asWith().enclosingScope();
         settle();
         break;
       case StrictEvalScope:
         if (hasScopeObject_)
             cur_ = &cur_->asCall().enclosingScope();
-        frame_ = AbstractFramePtr();
+        frame_ = NullFramePtr();
         break;
     }
     return *this;
 }
 
 void
 ScopeIter::settle()
 {
@@ -1044,24 +1044,24 @@ ScopeIter::settle()
             hasScopeObject_ = false;
         }
     } else if (frame_.isNonStrictDirectEvalFrame() && cur_ == frame_.evalPrev().scopeChain()) {
         if (block_) {
             JS_ASSERT(!block_->needsClone());
             type_ = Block;
             hasScopeObject_ = false;
         } else {
-            frame_ = AbstractFramePtr();
+            frame_ = NullFramePtr();
         }
     } else if (frame_.isNonEvalFunctionFrame() && !frame_.hasCallObj()) {
         JS_ASSERT(cur_ == frame_.fun()->environment());
-        frame_ = AbstractFramePtr();
+        frame_ = NullFramePtr();
     } else if (frame_.isStrictEvalFrame() && !frame_.hasCallObj()) {
         JS_ASSERT(cur_ == frame_.evalPrev().scopeChain());
-        frame_ = AbstractFramePtr();
+        frame_ = NullFramePtr();
     } else if (cur_->isWith()) {
         JS_ASSERT_IF(frame_.isFunctionFrame(), frame_.fun()->isHeavyweight());
         JS_ASSERT_IF(block_, block_->needsClone());
         JS_ASSERT_IF(block_, block_->stackDepth() < cur_->asWith().stackDepth());
         type_ = With;
         hasScopeObject_ = true;
     } else if (block_) {
         type_ = Block;
@@ -1070,17 +1070,17 @@ ScopeIter::settle()
     } else if (cur_->isCall()) {
         CallObject &callobj = cur_->asCall();
         type_ = callobj.isForEval() ? StrictEvalScope : Call;
         hasScopeObject_ = true;
         JS_ASSERT_IF(type_ == Call, callobj.callee().nonLazyScript() == frame_.script());
     } else {
         JS_ASSERT(!cur_->isScope());
         JS_ASSERT(frame_.isGlobalFrame() || frame_.isDebuggerFrame());
-        frame_ = AbstractFramePtr();
+        frame_ = NullFramePtr();
     }
 }
 
 /* static */ HashNumber
 ScopeIterKey::hash(ScopeIterKey si)
 {
     /* hasScopeObject_ is determined by the other fields. */
     return size_t(si.frame_.raw()) ^ size_t(si.cur_) ^ size_t(si.block_) ^ si.type_;
@@ -1954,17 +1954,17 @@ DebugScopes::updateLiveScopes(JSContext 
     return true;
 }
 
 AbstractFramePtr
 DebugScopes::hasLiveFrame(ScopeObject &scope)
 {
     DebugScopes *scopes = scope.compartment()->debugScopes;
     if (!scopes)
-        return AbstractFramePtr();
+        return NullFramePtr();
 
     if (LiveScopeMap::Ptr p = scopes->liveScopes.lookup(&scope)) {
         AbstractFramePtr frame = p->value;
 
         /*
          * Since liveScopes is effectively a weak pointer, we need a read
          * barrier. The scenario where this is necessary is:
          *  1. GC starts, a suspended generator is not live
@@ -1975,17 +1975,17 @@ DebugScopes::hasLiveFrame(ScopeObject &s
          *  4. GC completes, live objects may now point to values that weren't
          *     marked and thus may point to swept GC things
          */
         if (JSGenerator *gen = frame.maybeSuspendedGenerator(scope.compartment()->rt))
             JSObject::readBarrier(gen->obj);
 
         return frame;
     }
-    return AbstractFramePtr();
+    return NullFramePtr();
 }
 
 /*****************************************************************************/
 
 static JSObject *
 GetDebugScope(JSContext *cx, const ScopeIter &si);
 
 static DebugScopeObject *
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -467,17 +467,17 @@ class ScopeIter
 class ScopeIterKey
 {
     AbstractFramePtr frame_;
     JSObject *cur_;
     StaticBlockObject *block_;
     ScopeIter::Type type_;
 
   public:
-    ScopeIterKey() : frame_(), cur_(NULL), block_(NULL), type_() {}
+    ScopeIterKey() : frame_(NullFramePtr()), cur_(NULL), block_(NULL), type_() {}
     ScopeIterKey(const ScopeIter &si)
       : frame_(si.frame_), cur_(si.cur_), block_(si.block_), type_(si.type_)
     {}
 
     AbstractFramePtr frame() const { return frame_; }
     ScopeIter::Type type() const { return type_; }
 
     /* For use as hash policy */
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -701,10 +701,219 @@ inline Value &
 AbstractFramePtr::unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing)
 {
     if (isStackFrame())
         return asStackFrame()->unaliasedFormal(i, checkAliasing);
     JS_NOT_REACHED("Invalid frame");
     return asStackFrame()->unaliasedFormal(i);
 }
 
+inline JSGenerator *
+AbstractFramePtr::maybeSuspendedGenerator(JSRuntime *rt) const
+{
+    if (isStackFrame())
+        return asStackFrame()->maybeSuspendedGenerator(rt);
+    return NULL;
+}
+
+inline StaticBlockObject *
+AbstractFramePtr::maybeBlockChain() const
+{
+    if (isStackFrame())
+        return asStackFrame()->maybeBlockChain();
+    return NULL;
+}
+inline bool
+AbstractFramePtr::hasCallObj() const
+{
+    if (isStackFrame())
+        return asStackFrame()->hasCallObj();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline bool
+AbstractFramePtr::isGeneratorFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isGeneratorFrame();
+    return false;
+}
+inline bool
+AbstractFramePtr::isYielding() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isYielding();
+    return false;
+}
+inline bool
+AbstractFramePtr::isFunctionFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isFunctionFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline bool
+AbstractFramePtr::isGlobalFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isGlobalFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline bool
+AbstractFramePtr::isEvalFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isEvalFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline bool
+AbstractFramePtr::isFramePushedByExecute() const
+{
+    return isGlobalFrame() || isEvalFrame();
+}
+inline bool
+AbstractFramePtr::isDebuggerFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isDebuggerFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline UnrootedScript
+AbstractFramePtr::script() const
+{
+    if (isStackFrame())
+        return asStackFrame()->script();
+    JS_NOT_REACHED("Invalid frame");
+    return NULL;
+}
+inline JSFunction *
+AbstractFramePtr::fun() const
+{
+    if (isStackFrame())
+        return asStackFrame()->fun();
+    JS_NOT_REACHED("Invalid frame");
+    return NULL;
+}
+inline JSFunction &
+AbstractFramePtr::callee() const
+{
+    if (isStackFrame())
+        return asStackFrame()->callee();
+    JS_NOT_REACHED("Invalid frame");
+    return asStackFrame()->callee();
+}
+inline bool
+AbstractFramePtr::isNonEvalFunctionFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isNonEvalFunctionFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline bool
+AbstractFramePtr::isNonStrictDirectEvalFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isNonStrictDirectEvalFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline bool
+AbstractFramePtr::isStrictEvalFrame() const
+{
+    if (isStackFrame())
+        return asStackFrame()->isStrictEvalFrame();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+
+inline Value *
+AbstractFramePtr::formals() const
+{
+    if (isStackFrame())
+        return asStackFrame()->formals();
+    JS_NOT_REACHED("Invalid frame");
+    return NULL;
+}
+inline Value *
+AbstractFramePtr::actuals() const
+{
+    if (isStackFrame())
+        return asStackFrame()->actuals();
+    JS_NOT_REACHED("Invalid frame");
+    return NULL;
+}
+inline bool
+AbstractFramePtr::hasArgsObj() const
+{
+    if (isStackFrame())
+        return asStackFrame()->hasArgsObj();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline ArgumentsObject &
+AbstractFramePtr::argsObj() const
+{
+    if (isStackFrame())
+        return asStackFrame()->argsObj();
+    JS_NOT_REACHED("Invalid frame");
+    return asStackFrame()->argsObj();
+}
+inline void
+AbstractFramePtr::initArgsObj(ArgumentsObject &argsobj) const
+{
+    if (isStackFrame()) {
+        asStackFrame()->initArgsObj(argsobj);
+        return;
+    }
+    JS_NOT_REACHED("Invalid frame");
+}
+inline bool
+AbstractFramePtr::copyRawFrameSlots(AutoValueVector *vec) const
+{
+    if (isStackFrame())
+        return asStackFrame()->copyRawFrameSlots(vec);
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+
+inline bool
+AbstractFramePtr::prevUpToDate() const
+{
+    if (isStackFrame())
+        return asStackFrame()->prevUpToDate();
+    JS_NOT_REACHED("Invalid frame");
+    return false;
+}
+inline void
+AbstractFramePtr::setPrevUpToDate() const
+{
+    if (isStackFrame()) {
+        asStackFrame()->setPrevUpToDate();
+        return;
+    }
+    JS_NOT_REACHED("Invalid frame");
+}
+inline AbstractFramePtr
+AbstractFramePtr::evalPrev() const
+{
+    JS_ASSERT(isEvalFrame());
+    if (isStackFrame())
+        return AbstractFramePtr(asStackFrame()->prev());
+    JS_NOT_REACHED("Invalid frame");
+    return NullFramePtr();
+}
+
+inline Value &
+AbstractFramePtr::thisValue() const
+{
+    if (isStackFrame())
+        return asStackFrame()->thisValue();
+    JS_NOT_REACHED("Invalid frame");
+    return asStackFrame()->thisValue();
+}
+
 } /* namespace js */
 #endif /* Stack_inl_h__ */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1046,17 +1046,17 @@ ContextStack::pushInvokeFrame(JSContext 
     if (!pushInvokeFrame(cx, REPORT_ERROR, args, fun, initial, ifg))
         return false;
     return true;
 }
 
 bool
 ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
                                JSObject &scopeChain, ExecuteType type,
-                               StackFrame *evalInFrame, ExecuteFrameGuard *efg)
+                               AbstractFramePtr evalInFrame, ExecuteFrameGuard *efg)
 {
     AssertCanGC();
 
     /*
      * Even though global code and indirect eval do not execute in the context
      * of the current frame, prev-link these to the current frame so that the
      * callstack looks right to the debugger (via CAN_EXTEND). This is safe
      * since the scope chain is what determines name lookup and access, not
@@ -1066,37 +1066,37 @@ ContextStack::pushExecuteFrame(JSContext
      * (possibly in the middle of some previous segment). Thus pass CANT_EXTEND
      * (to start a new segment) and link the frame and call chain manually
      * below.
      */
     CallArgsList *evalInFrameCalls = NULL;  /* quell overwarning */
     MaybeExtend extend;
     if (evalInFrame) {
         /* Though the prev-frame is given, need to search for prev-call. */
-        StackSegment &seg = cx->stack.space().containingSegment(evalInFrame);
+        StackSegment &seg = cx->stack.space().containingSegment(evalInFrame.asStackFrame());
         StackIter iter(cx->runtime, seg);
         /* Debug-mode currently disables Ion compilation. */
-        JS_ASSERT(!evalInFrame->runningInIon());
-        JS_ASSERT_IF(evalInFrame->compartment() == iter.compartment(), !iter.isIon());
-        while (!iter.isScript() || iter.isIon() || iter.interpFrame() != evalInFrame) {
+        JS_ASSERT(!evalInFrame.asStackFrame()->runningInIon());
+        JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIon());
+        while (!iter.isScript() || iter.isIon() || iter.abstractFramePtr() != evalInFrame) {
             ++iter;
-            JS_ASSERT_IF(evalInFrame->compartment() == iter.compartment(), !iter.isIon());
+            JS_ASSERT_IF(evalInFrame.compartment() == iter.compartment(), !iter.isIon());
         }
         evalInFrameCalls = iter.data_.calls_;
         extend = CANT_EXTEND;
     } else {
         extend = CAN_EXTEND;
     }
 
     unsigned nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
     if (!firstUnused)
         return false;
 
-    StackFrame *prev = evalInFrame ? evalInFrame : maybefp();
+    StackFrame *prev = evalInFrame ? evalInFrame.asStackFrame() : maybefp();
     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
     fp->initExecuteFrame(script, prev, seg_->maybeRegs(), thisv, scopeChain, type);
     fp->initVarsToUndefined();
     efg->regs_.prepareToRun(*fp, script);
 
     /* pushRegs() below links the prev-frame; manually link the prev-call. */
     if (evalInFrame && evalInFrameCalls)
         seg_->pointAtCall(*evalInFrameCalls);
@@ -1757,17 +1757,17 @@ StackIter::abstractFramePtr() const
         break;
       case SCRIPTED:
         JS_ASSERT(interpFrame());
         return AbstractFramePtr(interpFrame());
       case NATIVE:
         break;
     }
     JS_NOT_REACHED("Unexpected state");
-    return AbstractFramePtr();
+    return NullFramePtr();
 }
 
 void
 StackIter::updatePcQuadratic()
 {
     switch (data_.state_) {
       case DONE:
         break;
@@ -2150,10 +2150,10 @@ AllFramesIter::abstractFramePtr() const
       case SCRIPTED:
         return AbstractFramePtr(interpFrame());
       case ION:
         break;
       case DONE:
         break;
     }
     JS_NOT_REACHED("Unexpected state");
-    return AbstractFramePtr();
+    return NullFramePtr();
 }
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -208,16 +208,119 @@ CallArgsListFromVp(unsigned argc, Value 
 }
 
 /*****************************************************************************/
 
 enum MaybeCheckAliasing { CHECK_ALIASING = true, DONT_CHECK_ALIASING = false };
 
 /*****************************************************************************/
 
+class BaselineFrame;
+
+/* Pointer to either a StackFrame or a baseline JIT frame. */
+class AbstractFramePtr
+{
+    uintptr_t ptr_;
+
+  protected:
+    AbstractFramePtr()
+      : ptr_(0)
+    {}
+
+  public:
+    AbstractFramePtr(StackFrame *fp)
+        : ptr_(fp ? uintptr_t(fp) | 0x1 : 0)
+    {
+        JS_ASSERT((uintptr_t(fp) & 1) == 0);
+    }
+
+    AbstractFramePtr(BaselineFrame *fp)
+      : ptr_(uintptr_t(fp))
+    {
+        JS_ASSERT((uintptr_t(fp) & 1) == 0);
+    }
+
+    bool isStackFrame() const {
+        return ptr_ & 0x1;
+    }
+
+    StackFrame *asStackFrame() const {
+        JS_ASSERT(isStackFrame());
+        StackFrame *res = (StackFrame *)(ptr_ & ~0x1);
+        JS_ASSERT(res);
+        return res;
+    }
+
+    void *raw() const { return reinterpret_cast<void *>(ptr_); }
+
+    bool operator ==(const AbstractFramePtr &other) const { return ptr_ == other.ptr_; }
+    bool operator !=(const AbstractFramePtr &other) const { return ptr_ != other.ptr_; }
+
+    operator bool() const { return !!ptr_; }
+
+    inline JSGenerator *maybeSuspendedGenerator(JSRuntime *rt) const;
+
+    inline UnrootedObject scopeChain() const;
+    inline CallObject &callObj() const;
+    inline JSCompartment *compartment() const;
+
+    inline StaticBlockObject *maybeBlockChain() const;
+    inline bool hasCallObj() const;
+    inline bool isGeneratorFrame() const;
+    inline bool isYielding() const;
+    inline bool isFunctionFrame() const;
+    inline bool isGlobalFrame() const;
+    inline bool isEvalFrame() const;
+    inline bool isFramePushedByExecute() const;
+    inline bool isDebuggerFrame() const;
+
+    inline UnrootedScript script() const;
+    inline JSFunction *fun() const;
+    inline JSFunction &callee() const;
+    inline Value &thisValue() const;
+
+    inline bool isNonEvalFunctionFrame() const;
+    inline bool isNonStrictDirectEvalFrame() const;
+    inline bool isStrictEvalFrame() const;
+
+    inline unsigned numActualArgs() const;
+    inline unsigned numFormalArgs() const;
+
+    inline Value *formals() const;
+    inline Value *actuals() const;
+
+    inline bool hasArgsObj() const;
+    inline ArgumentsObject &argsObj() const;
+    inline void initArgsObj(ArgumentsObject &argsobj) const;
+
+    inline bool copyRawFrameSlots(AutoValueVector *vec) const;
+
+    inline Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+    inline Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+    inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
+
+    inline bool prevUpToDate() const;
+    inline void setPrevUpToDate() const;
+    inline AbstractFramePtr evalPrev() const;
+
+    inline void *maybeHookData() const;
+    inline void setHookData(void *data) const;
+    inline void setReturnValue(const Value &rval) const;
+};
+
+class NullFramePtr : public AbstractFramePtr
+{
+  public:
+    NullFramePtr()
+      : AbstractFramePtr()
+    { }
+};
+
+/*****************************************************************************/
+
 /* Flags specified for a frame as it is constructed. */
 enum InitialFrameFlags {
     INITIAL_NONE           =          0,
     INITIAL_CONSTRUCT      =       0x20, /* == StackFrame::CONSTRUCTING, asserted below */
     INITIAL_LOWERED        =    0x80000  /* == StackFrame::LOWERED_CALL_APPLY, asserted below */
 };
 
 enum ExecuteType {
@@ -1587,17 +1690,17 @@ class ContextStack
 
     /* Called by Invoke for a scripted function call. */
     bool pushInvokeFrame(JSContext *cx, const CallArgs &args,
                          InitialFrameFlags initial, InvokeFrameGuard *ifg);
 
     /* Called by Execute for execution of eval or global code. */
     bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
                           JSObject &scopeChain, ExecuteType type,
-                          StackFrame *evalInFrame, ExecuteFrameGuard *efg);
+                          AbstractFramePtr evalInFrame, ExecuteFrameGuard *efg);
 
     /* Allocate actual argument space for the bailed frame */
     bool pushBailoutArgs(JSContext *cx, const ion::IonBailoutIterator &it,
                          InvokeArgsGuard *iag);
 
     /* Bailout for normal functions. */
     StackFrame *pushBailoutFrame(JSContext *cx, const ion::IonBailoutIterator &it,
                                  const CallArgs &args, BailoutFrameGuard *bfg);
@@ -1711,216 +1814,16 @@ class GeneratorFrameGuard : public Frame
 {
     friend class ContextStack;
     JSGenerator *gen_;
     Value *stackvp_;
   public:
     ~GeneratorFrameGuard() { if (pushed()) stack_->popGeneratorFrame(*this); }
 };
 
-/* Pointer to either a StackFrame or a baseline JIT frame. */
-class AbstractFramePtr
-{
-    uintptr_t ptr_;
-
-  public:
-    AbstractFramePtr()
-      : ptr_(0)
-    {}
-
-    AbstractFramePtr(StackFrame *fp)
-      : ptr_(uintptr_t(fp) | 0x1)
-    {
-        JS_ASSERT(fp);
-    }
-
-    bool isStackFrame() const {
-        return ptr_ & 0x1;
-    }
-
-    StackFrame *asStackFrame() const {
-        JS_ASSERT(isStackFrame());
-        StackFrame *res = (StackFrame *)(ptr_ & ~0x1);
-        JS_ASSERT(res);
-        return res;
-    }
-
-    void *raw() const { return reinterpret_cast<void *>(ptr_); }
-
-    bool operator ==(const AbstractFramePtr &other) const { return ptr_ == other.ptr_; }
-    bool operator !=(const AbstractFramePtr &other) const { return ptr_ != other.ptr_; }
-
-    operator bool() const { return !!ptr_; }
-
-    JSGenerator *maybeSuspendedGenerator(JSRuntime *rt) const {
-        if (isStackFrame())
-            return asStackFrame()->maybeSuspendedGenerator(rt);
-        return NULL;
-    }
-
-    inline UnrootedObject scopeChain() const;
-    inline CallObject &callObj() const;
-    inline JSCompartment *compartment() const;
-
-    StaticBlockObject *maybeBlockChain() const {
-        if (isStackFrame())
-            return asStackFrame()->maybeBlockChain();
-        return NULL;
-    }
-    bool hasCallObj() const {
-        if (isStackFrame())
-            return asStackFrame()->hasCallObj();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    bool isGeneratorFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isGeneratorFrame();
-        return false;
-    }
-    bool isYielding() const {
-        if (isStackFrame())
-            return asStackFrame()->isYielding();
-        return false;
-    }
-    bool isFunctionFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isFunctionFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    bool isGlobalFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isGlobalFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    bool isEvalFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isEvalFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    bool isFramePushedByExecute() const {
-        return isGlobalFrame() || isEvalFrame();
-    }
-    bool isDebuggerFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isDebuggerFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    JSScript *script() const {
-        if (isStackFrame())
-            return asStackFrame()->script();
-        JS_NOT_REACHED("Invalid frame");
-        return NULL;
-    }
-    UnrootedFunction fun() const {
-        if (isStackFrame())
-            return asStackFrame()->fun();
-        JS_NOT_REACHED("Invalid frame");
-        return NULL;
-    }
-    JSFunction &callee() const {
-        if (isStackFrame())
-            return asStackFrame()->callee();
-        JS_NOT_REACHED("Invalid frame");
-        return asStackFrame()->callee();
-    }
-    bool isNonEvalFunctionFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isNonEvalFunctionFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    bool isNonStrictDirectEvalFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isNonStrictDirectEvalFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    bool isStrictEvalFrame() const {
-        if (isStackFrame())
-            return asStackFrame()->isStrictEvalFrame();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-
-    inline unsigned numActualArgs() const;
-    inline unsigned numFormalArgs() const;
-
-    Value *formals() const {
-        if (isStackFrame())
-            return asStackFrame()->formals();
-        JS_NOT_REACHED("Invalid frame");
-        return NULL;
-    }
-    Value *actuals() const {
-        if (isStackFrame())
-            return asStackFrame()->actuals();
-        JS_NOT_REACHED("Invalid frame");
-        return NULL;
-    }
-    bool hasArgsObj() const {
-        if (isStackFrame())
-            return asStackFrame()->hasArgsObj();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    ArgumentsObject &argsObj() const {
-        if (isStackFrame())
-            return asStackFrame()->argsObj();
-        JS_NOT_REACHED("Invalid frame");
-        return asStackFrame()->argsObj();
-    }
-    void initArgsObj(ArgumentsObject &argsobj) const {
-        if (isStackFrame()) {
-            asStackFrame()->initArgsObj(argsobj);
-            return;
-        }
-        JS_NOT_REACHED("Invalid frame");
-    }
-    bool copyRawFrameSlots(AutoValueVector *vec) const {
-        if (isStackFrame())
-            return asStackFrame()->copyRawFrameSlots(vec);
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-
-    inline Value &unaliasedVar(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
-    inline Value &unaliasedLocal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
-    inline Value &unaliasedFormal(unsigned i, MaybeCheckAliasing checkAliasing = CHECK_ALIASING);
-
-    bool prevUpToDate() const {
-        if (isStackFrame())
-            return asStackFrame()->prevUpToDate();
-        JS_NOT_REACHED("Invalid frame");
-        return false;
-    }
-    void setPrevUpToDate() const {
-        if (isStackFrame()) {
-            asStackFrame()->setPrevUpToDate();
-            return;
-        }
-        JS_NOT_REACHED("Invalid frame");
-    }
-    AbstractFramePtr evalPrev() const {
-        JS_ASSERT(isEvalFrame());
-        if (isStackFrame())
-            return AbstractFramePtr(asStackFrame()->prev());
-        JS_NOT_REACHED("Invalid frame");
-        return AbstractFramePtr();
-    }
-
-    inline void *maybeHookData() const;
-    inline void setHookData(void *data) const;
-    inline void setReturnValue(const Value &rval) const;
-};
-
 template <>
 struct DefaultHasher<AbstractFramePtr> {
     typedef AbstractFramePtr Lookup;
 
     static js::HashNumber hash(const Lookup &key) {
         return size_t(key.raw());
     }