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 id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersluke
bugs832373
milestone21.0a1
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());
     }