backing out bug 446386 due to unit test failures
authorIgor Bukanov <igor@mir2.org>
Tue, 02 Sep 2008 09:27:10 +0200
changeset 18572 4aab0fc277d69717e0934bfc19ab6ee227bb315c
parent 18571 177e0a27bd42be895ff4bd2c00156ca7ebab1d8a
child 18573 6f9506ad113eb8ec99bd982d9727ae1d7cec2a36
push idunknown
push userunknown
push dateunknown
bugs446386
milestone1.9.1b1pre
backing out bug 446386 due to unit test failures
js/src/jsapi.cpp
js/src/jsdbgapi.cpp
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsinterp.h
js/src/jsobj.cpp
js/src/jsparse.cpp
js/src/jsparse.h
js/src/jsscript.cpp
js/src/jsxml.cpp
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4564,17 +4564,17 @@ JS_CompileUCScriptForPrincipals(JSContex
                                 const jschar *chars, size_t length,
                                 const char *filename, uintN lineno)
 {
     uint32 tcflags;
     JSScript *script;
 
     CHECK_REQUEST(cx);
     tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
-    script = js_CompileScript(cx, obj, NULL, principals, tcflags,
+    script = js_CompileScript(cx, obj, principals, tcflags,
                               chars, length, NULL, filename, lineno);
     LAST_FRAME_CHECKS(cx, script);
     return script;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
                           const char *bytes, size_t length)
@@ -4591,18 +4591,17 @@ JS_BufferIsCompilableUnit(JSContext *cx,
         return JS_TRUE;
 
     /*
      * Return true on any out-of-memory error, so our caller doesn't try to
      * collect more buffered source.
      */
     result = JS_TRUE;
     exnState = JS_SaveExceptionState(cx);
-    if (js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL, NULL,
-                            1)) {
+    if (js_InitParseContext(cx, &pc, NULL, chars, length, NULL, NULL, 1)) {
         older = JS_SetErrorReporter(cx, NULL);
         if (!js_ParseScript(cx, obj, &pc) &&
             (pc.tokenStream.flags & TSF_UNEXPECTED_EOF)) {
             /*
              * We ran into an error.  If it was because we ran out of source,
              * we return false, so our caller will know to try to collect more
              * buffered source.
              */
@@ -4631,17 +4630,17 @@ JS_CompileFile(JSContext *cx, JSObject *
         if (!fp) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_OPEN,
                                  filename, "No such file or directory");
             return NULL;
         }
     }
 
     tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
-    script = js_CompileScript(cx, obj, NULL, NULL, tcflags,
+    script = js_CompileScript(cx, obj, NULL, tcflags,
                               NULL, 0, fp, filename, 1);
     if (fp != stdin)
         fclose(fp);
     LAST_FRAME_CHECKS(cx, script);
     return script;
 }
 
 JS_PUBLIC_API(JSScript *)
@@ -4656,17 +4655,17 @@ JS_CompileFileHandleForPrincipals(JSCont
                                   const char *filename, FILE *file,
                                   JSPrincipals *principals)
 {
     uint32 tcflags;
     JSScript *script;
 
     CHECK_REQUEST(cx);
     tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
-    script = js_CompileScript(cx, obj, NULL, principals, tcflags,
+    script = js_CompileScript(cx, obj, principals, tcflags,
                               NULL, 0, file, filename, 1);
     LAST_FRAME_CHECKS(cx, script);
     return script;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_NewScriptObject(JSContext *cx, JSScript *script)
 {
@@ -4994,17 +4993,17 @@ JS_EvaluateUCScriptForPrincipals(JSConte
                                  const jschar *chars, uintN length,
                                  const char *filename, uintN lineno,
                                  jsval *rval)
 {
     JSScript *script;
     JSBool ok;
 
     CHECK_REQUEST(cx);
-    script = js_CompileScript(cx, obj, NULL, principals,
+    script = js_CompileScript(cx, obj, principals,
                               !rval
                               ? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL
                               : TCF_COMPILE_N_GO,
                               chars, length, NULL, filename, lineno);
     if (!script) {
         LAST_FRAME_CHECKS(cx, script);
         return JS_FALSE;
     }
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1150,22 +1150,29 @@ JS_PUBLIC_API(JSFunction *)
 JS_GetFrameFunction(JSContext *cx, JSStackFrame *fp)
 {
     return fp->fun;
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
 {
-    if (!fp->fun)
-        return NULL;
+    /*
+     * Test both fp->fun and fp->argv to defend against any control flow from
+     * the compiler reaching this API entry point, where fp is a frame pushed
+     * by the compiler that has non-null fun but null argv.
+     */
+    if (fp->fun && fp->argv) {
+        JSObject *obj = fp->callee;
 
-    JS_ASSERT(OBJ_GET_CLASS(cx, fp->callee) == &js_FunctionClass);
-    JS_ASSERT(OBJ_GET_PRIVATE(cx, fp->callee) == fp->fun);
-    return fp->callee;
+        JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_FunctionClass);
+        JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == fp->fun);
+        return obj;
+    }
+    return NULL;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
 {
     return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
 }
 
@@ -1240,28 +1247,36 @@ JS_SetDestroyScriptHook(JSRuntime *rt, J
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
                           const jschar *chars, uintN length,
                           const char *filename, uintN lineno,
                           jsval *rval)
 {
     JSObject *scobj;
+    uint32 flags;
     JSScript *script;
     JSBool ok;
 
     scobj = JS_GetFrameScopeChain(cx, fp);
     if (!scobj)
         return JS_FALSE;
 
-    script = js_CompileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
+    /*
+     * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL
+     * flags to the code generator.
+     */
+    flags = fp->flags;
+    fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL;
+    script = js_CompileScript(cx, scobj, JS_StackFramePrincipals(cx, fp),
                               TCF_COMPILE_N_GO |
                               TCF_PUT_STATIC_DEPTH(fp->script->staticDepth + 1),
                               chars, length, NULL,
                               filename, lineno);
+    fp->flags = flags;
     if (!script)
         return JS_FALSE;
 
     ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
                     rval);
     js_DestroyScript(cx, script);
     return ok;
 }
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1563,17 +1563,18 @@ LookupCompileTimeConstant(JSContext *cx,
 
     /*
      * Chase down the cg stack, but only until we reach the outermost cg.
      * This enables propagating consts from top-level into switch cases in a
      * function compiled along with the top-level script.
      */
     *vp = JSVAL_HOLE;
     do {
-        if (cg->treeContext.flags & (TCF_IN_FUNCTION | TCF_COMPILE_N_GO)) {
+        if ((cg->treeContext.flags & TCF_IN_FUNCTION) ||
+            cx->fp->varobj == cx->fp->scopeChain) {
             /* XXX this will need revising when 'let const' is added. */
             stmt = js_LexicalLookup(&cg->treeContext, atom, NULL);
             if (stmt)
                 return JS_TRUE;
 
             ATOM_LIST_SEARCH(ale, &cg->constList, atom);
             if (ale) {
                 JS_ASSERT(ALE_VALUE(ale) != JSVAL_HOLE);
@@ -1584,23 +1585,22 @@ LookupCompileTimeConstant(JSContext *cx,
             /*
              * Try looking in the variable object for a direct property that
              * is readonly and permanent.  We know such a property can't be
              * shadowed by another property on obj's prototype chain, or a
              * with object or catch variable; nor can prop's value be changed,
              * nor can prop be deleted.
              */
             if (cg->treeContext.flags & TCF_IN_FUNCTION) {
-                if (js_LookupLocal(cx, cg->treeContext.u.fun, atom, NULL) !=
+                if (js_LookupLocal(cx, cg->treeContext.fun, atom, NULL) !=
                     JSLOCAL_NONE) {
                     break;
                 }
-            } else {
-                JS_ASSERT(cg->treeContext.flags & TCF_COMPILE_N_GO);
-                obj = cg->treeContext.u.scopeChain;
+            } else if (cg->treeContext.flags & TCF_COMPILE_N_GO) {
+                obj = cx->fp->varobj;
                 ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj,
                                          &prop);
                 if (!ok)
                     return JS_FALSE;
                 if (pobj == obj) {
                     /*
                      * We're compiling code that will be executed immediately,
                      * not re-executed against a different scope chain and/or
@@ -1768,17 +1768,17 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, 
  *
  * The function returns -1 on failures.
  */
 static jsint
 AdjustBlockSlot(JSContext *cx, JSCodeGenerator *cg, jsint slot)
 {
     JS_ASSERT((jsuint) slot < cg->maxStackDepth);
     if (cg->treeContext.flags & TCF_IN_FUNCTION) {
-        slot += cg->treeContext.u.fun->u.i.nvars;
+        slot += cg->treeContext.fun->u.i.nvars;
         if ((uintN) slot >= SLOTNO_LIMIT) {
             js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL,
                                         JSREPORT_ERROR,
                                         JSMSG_TOO_MANY_LOCALS);
             slot = -1;
         }
     }
     return slot;
@@ -1805,16 +1805,17 @@ AdjustBlockSlot(JSContext *cx, JSCodeGen
 static JSBool
 BindNameToSlot(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn)
 {
     JSTreeContext *tc;
     JSAtom *atom;
     JSStmtInfo *stmt;
     jsint slot;
     JSOp op;
+    JSStackFrame *fp;
     JSLocalKind localKind;
     uintN index;
     JSAtomListElement *ale;
     JSBool constOp;
 
     JS_ASSERT(pn->pn_type == TOK_NAME);
     if (pn->pn_slot >= 0 || pn->pn_op == JSOP_ARGUMENTS)
         return JS_TRUE;
@@ -1864,74 +1865,89 @@ BindNameToSlot(JSContext *cx, JSCodeGene
      * We can't optimize if var and closure (a local function not in a larger
      * expression and not at top-level within another's body) collide.
      * XXX suboptimal: keep track of colliding names and deoptimize only those
      */
     if (tc->flags & TCF_FUN_CLOSURE_VS_VAR)
         return JS_TRUE;
 
     if (!(tc->flags & TCF_IN_FUNCTION)) {
-        JSStackFrame *caller;
-
-        caller = tc->parseContext->callerFrame;
-        if (caller) {
-            JS_ASSERT(tc->flags & TCF_COMPILE_N_GO);
-            JS_ASSERT(caller->script);
-            if (!caller->fun || caller->varobj != tc->u.scopeChain)
-                return JS_TRUE;
-
-            /*
-             * We are compiling eval or debug script inside a function frame
-             * and the scope chain matches function's variable object.
-             * Optimize access to function's arguments and variable and the
-             * arguments object.
-             */
-            if (PN_OP(pn) != JSOP_NAME || cg->staticDepth > JS_DISPLAY_SIZE)
-                goto arguments_check;
-            localKind = js_LookupLocal(cx, caller->fun, atom, &index);
-            if (localKind == JSLOCAL_NONE)
-                goto arguments_check;
-
-            ATOM_LIST_SEARCH(ale, &cg->upvarList, atom);
-            if (!ale) {
-                uint32 cookie, length, *vector;
-
-                ale = js_IndexAtom(cx, atom, &cg->upvarList);
-                if (!ale)
-                    return JS_FALSE;
-                JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
-
-                length = cg->upvarMap.length;
-                JS_ASSERT(ALE_INDEX(ale) <= length);
-                if (ALE_INDEX(ale) == length) {
-                    length = 2 * JS_MAX(2, length);
-                    vector = (uint32 *)
-                             JS_realloc(cx, cg->upvarMap.vector,
-                                        length * sizeof *vector);
-                    if (!vector)
-                        return JS_FALSE;
-                    cg->upvarMap.vector = vector;
-                    cg->upvarMap.length = length;
-                }
-
-                if (localKind != JSLOCAL_ARG)
-                    index += caller->fun->nargs;
-                if (index >= JS_BIT(16)) {
-                    cg->treeContext.flags |= TCF_FUN_USES_NONLOCALS;
+        if ((cx->fp->flags & JSFRAME_SPECIAL) && cx->fp->fun) {
+            if (cg->staticDepth > JS_DISPLAY_SIZE)
+                goto out;
+
+            localKind = js_LookupLocal(cx, cx->fp->fun, atom, &index);
+            if (localKind != JSLOCAL_NONE) {
+                if (PN_OP(pn) == JSOP_NAME) {
+                    ATOM_LIST_SEARCH(ale, &cg->upvarList, atom);
+                    if (!ale) {
+                        uint32 cookie, length, *vector;
+
+                        ale = js_IndexAtom(cx, atom, &cg->upvarList);
+                        if (!ale)
+                            return JS_FALSE;
+                        JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
+
+                        length = cg->upvarMap.length;
+                        JS_ASSERT(ALE_INDEX(ale) <= length);
+                        if (ALE_INDEX(ale) == length) {
+                            length = 2 * JS_MAX(2, length);
+                            vector = (uint32 *)
+                                     JS_realloc(cx, cg->upvarMap.vector,
+                                                length * sizeof *vector);
+                            if (!vector)
+                                return JS_FALSE;
+                            cg->upvarMap.vector = vector;
+                            cg->upvarMap.length = length;
+                        }
+
+                        if (localKind != JSLOCAL_ARG)
+                            index += cx->fp->fun->nargs;
+                        if (index >= JS_BIT(16)) {
+                            cg->treeContext.flags |= TCF_FUN_USES_NONLOCALS;
+                            return JS_TRUE;
+                        }
+
+                        cookie = MAKE_UPVAR_COOKIE(1, index);
+                        cg->upvarMap.vector[ALE_INDEX(ale)] = cookie;
+                    }
+
+                    pn->pn_op = JSOP_GETUPVAR;
+                    pn->pn_slot = ALE_INDEX(ale);
                     return JS_TRUE;
                 }
-
-                cookie = MAKE_UPVAR_COOKIE(1, index);
-                cg->upvarMap.vector[ALE_INDEX(ale)] = cookie;
             }
-
-            pn->pn_op = JSOP_GETUPVAR;
-            pn->pn_slot = ALE_INDEX(ale);
+            goto out;
+        }
+
+        /*
+         * We are compiling a script or eval, and eval is not inside a function
+         * activation.
+         */
+        fp = cx->fp;
+        if (fp->scopeChain != fp->varobj)
             return JS_TRUE;
-        }
+
+        /*
+         * A Script object can be used to split an eval into a compile step
+         * done at construction time, and an execute step done separately,
+         * possibly in a different scope altogether.  We therefore cannot do
+         * any name-to-slot optimizations, but must lookup names at runtime.
+         * Note that script_exec ensures that its caller's frame has a Call
+         * object, so arg and var name lookups will succeed.
+         */
+        if (fp->flags & JSFRAME_SCRIPT_OBJECT)
+            return JS_TRUE;
+
+        /*
+         * We cannot optimize the name access when compiling with an eval or
+         * debugger frame.
+         */
+        if (fp->flags & JSFRAME_SPECIAL)
+            return JS_TRUE;
 
         /*
          * We are optimizing global variables and there may be no pre-existing
          * global property named atom.  If atom was declared via const or var,
          * optimize pn to access fp->vars using the appropriate JSOP_*GVAR op.
          */
         ATOM_LIST_SEARCH(ale, &tc->decls, atom);
         if (!ale) {
@@ -1975,17 +1991,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
     }
 
     if (tc->flags & TCF_IN_FUNCTION) {
         /*
          * We are compiling a function body and may be able to optimize name
          * to stack slot. Look for an argument or variable in the function and
          * rewrite pn_op and update pn accordingly.
          */
-        localKind = js_LookupLocal(cx, tc->u.fun, atom, &index);
+        localKind = js_LookupLocal(cx, tc->fun, atom, &index);
         if (localKind != JSLOCAL_NONE) {
             op = PN_OP(pn);
             if (localKind == JSLOCAL_ARG) {
                 switch (op) {
                   case JSOP_NAME:     op = JSOP_GETARG; break;
                   case JSOP_SETNAME:  op = JSOP_SETARG; break;
                   case JSOP_INCNAME:  op = JSOP_INCARG; break;
                   case JSOP_NAMEINC:  op = JSOP_ARGINC; break;
@@ -2015,27 +2031,27 @@ BindNameToSlot(JSContext *cx, JSCodeGene
             }
             pn->pn_op = op;
             pn->pn_slot = index;
             return JS_TRUE;
         }
         tc->flags |= TCF_FUN_USES_NONLOCALS;
     }
 
-  arguments_check:
+out:
     /*
-     * Here we either compiling a function body or an eval or debug script
-     * inside a function and couldn't optimize pn, so it's not a global or
-     * local slot name. We are also outside of any with blocks. Check if we
-     * can optimize the predefined arguments variable.
+     * Here we either compiling a function body or an eval script inside a
+     * function and couldn't optimize pn, so it's not a global or local slot
+     * name.
+     *
+     * Now we must check for the predefined arguments variable.  It may be
+     * overridden by assignment, in which case the function is heavyweight
+     * and the interpreter will look up 'arguments' in the function's call
+     * object.
      */
-    JS_ASSERT((tc->flags & TCF_IN_FUNCTION) ||
-              (tc->parseContext->callerFrame &&
-               tc->parseContext->callerFrame->fun &&
-               tc->parseContext->callerFrame->varobj == tc->u.scopeChain));
     if (pn->pn_op == JSOP_NAME &&
         atom == cx->runtime->atomState.argumentsAtom) {
         pn->pn_op = JSOP_ARGUMENTS;
         return JS_TRUE;
     }
     return JS_TRUE;
 }
 
@@ -3142,16 +3158,21 @@ js_EmitFunctionScript(JSContext *cx, JSC
         /* JSOP_GENERATOR must be the first instruction. */
         CG_SWITCH_TO_PROLOG(cg);
         JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
         if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
             return JS_FALSE;
         CG_SWITCH_TO_MAIN(cg);
     }
 
+    if (!(cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT) &&
+        (cg->treeContext.flags & TCF_COMPILE_N_GO)) {
+        STOBJ_SET_PARENT(FUN_OBJECT(cg->treeContext.fun), cx->fp->scopeChain);
+    }
+
     return js_EmitTree(cx, cg, body) &&
            js_Emit1(cx, cg, JSOP_STOP) >= 0 &&
            js_NewScriptFromCG(cx, cg);
 }
 
 /* A macro for inlining at the top of js_EmitTree (whence it came). */
 #define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn)                                  \
     JS_BEGIN_MACRO                                                            \
@@ -3966,17 +3987,17 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         if (!cg2) {
             js_ReportOutOfScriptQuota(cx);
             return JS_FALSE;
         }
         js_InitCodeGenerator(cx, cg2, cg->treeContext.parseContext,
                              cg->codePool, cg->notePool,
                              pn->pn_pos.begin.lineno);
         cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION);
-        cg2->treeContext.u.fun = fun;
+        cg2->treeContext.fun = fun;
         cg2->staticDepth = cg->staticDepth + 1;
         cg2->parent = cg;
 
         /* We metered the max scope depth when parsed the function. */ 
         JS_SCOPE_DEPTH_METERING(cg2->treeContext.maxScopeDepth = (uintN) -1);
         if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) {
             pn = NULL;
         } else {
@@ -4021,30 +4042,28 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
         if (!(cg->treeContext.flags & TCF_IN_FUNCTION)) {
             CG_SWITCH_TO_PROLOG(cg);
 
             /*
              * Emit JSOP_CLOSURE for eval code to do fewer checks when
              * instantiating top-level functions in the non-eval case.
              */
             JS_ASSERT(!cg->treeContext.topStmt);
-            op = (cg->treeContext.parseContext->callerFrame)
-                 ? JSOP_CLOSURE
-                 : JSOP_DEFFUN;
+            op = (cx->fp->flags & JSFRAME_EVAL) ? JSOP_CLOSURE : JSOP_DEFFUN;
             EMIT_INDEX_OP(op, index);
             CG_SWITCH_TO_MAIN(cg);
 
             /* Emit NOP for the decompiler. */
             if (!EmitFunctionDefNop(cx, cg, index))
                 return JS_FALSE;
         } else {
 #ifdef DEBUG
             JSLocalKind localKind =
 #endif
-                js_LookupLocal(cx, cg->treeContext.u.fun, fun->atom, &slot);
+                js_LookupLocal(cx, cg->treeContext.fun, fun->atom, &slot);
             JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
             JS_ASSERT(pn->pn_index == (uint32) -1);
             pn->pn_index = index;
             if (!EmitSlotIndexOp(cx, JSOP_DEFLOCALFUN, slot, index, cg))
                 return JS_FALSE;
         }
         break;
       }
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -166,24 +166,18 @@ struct JSTreeContext {              /* t
     JSStmtInfo      *topScopeStmt;  /* top lexical scope statement */
     JSObject        *blockChain;    /* compile time block scope chain (NB: one
                                        deeper than the topScopeStmt/downScope
                                        chain when in head of let block/expr) */
     JSParseNode     *blockNode;     /* parse node for a lexical scope.
                                        XXX combine with blockChain? */
     JSAtomList      decls;          /* function, const, and var declarations */
     JSParseContext  *parseContext;
-
-    union {
-
-        JSFunction  *fun;           /* function to store argument and variable
+    JSFunction      *fun;           /* function to store argument and variable
                                        names when flags & TCF_IN_FUNCTION */
-        JSObject    *scopeChain;    /* scope chain object for the script */
-    } u;
-
 #ifdef JS_SCOPE_DEPTH_METER
     uint16          scopeDepth;     /* current lexical scope chain depth */
     uint16          maxScopeDepth;  /* maximum lexical scope chain depth */
 #endif
 };
 
 #define TCF_IN_FUNCTION        0x01 /* parsing inside function body */
 #define TCF_RETURN_EXPR        0x02 /* function has 'return expr;' */
@@ -196,16 +190,17 @@ struct JSTreeContext {              /* t
 #define TCF_HAS_DEFXMLNS      0x100 /* default xml namespace = ...; parsed */
 #define TCF_HAS_FUNCTION_STMT 0x200 /* block contains a function statement */
 #define TCF_GENEXP_LAMBDA     0x400 /* flag lambda from generator expression */
 #define TCF_COMPILE_N_GO      0x800 /* compiler-and-go mode of script, can
                                        optimize name references based on scope
                                        chain */
 #define TCF_NO_SCRIPT_RVAL   0x1000 /* API caller does not want result value
                                        from global script */
+
 /*
  * Flags to propagate out of the blocks.
  */
 #define TCF_RETURN_FLAGS        (TCF_RETURN_EXPR | TCF_RETURN_VOID)
 
 /*
  * Flags to propagate from FunctionBody.
  */
@@ -230,17 +225,17 @@ struct JSTreeContext {              /* t
 
 #define TREE_CONTEXT_INIT(tc, pc)                                             \
     ((tc)->flags = (tc)->ngvars = 0,                                          \
      (tc)->topStmt = (tc)->topScopeStmt = NULL,                               \
      (tc)->blockChain = NULL,                                                 \
      ATOM_LIST_INIT(&(tc)->decls),                                            \
      (tc)->blockNode = NULL,                                                  \
      (tc)->parseContext = (pc),                                               \
-     (tc)->u.scopeChain = NULL,                                               \
+     (tc)->fun = NULL,                                                        \
      JS_SCOPE_DEPTH_METERING((tc)->scopeDepth = (tc)->maxScopeDepth = 0))
 
 /*
  * For functions TREE_CONTEXT_FINISH is called the second time to finish the
  * extra tc created during code generation. We skip stats update in such
  * cases.
  */
 #define TREE_CONTEXT_FINISH(cx, tc)                                           \
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -786,17 +786,17 @@ CallPropertyOp(JSContext *cx, JSObject *
                 if (!argsobj)
                     return JS_FALSE;
                 *vp = OBJECT_TO_JSVAL(argsobj);
             } else {
                 *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS);
             }
         }
         return JS_TRUE;
-    }
+  }
 
     JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id));
     i = (uint16) JSVAL_TO_INT(id);
     JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
     JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
 
     if (!fp) {
         i += CALL_CLASS_FIXED_RESERVED_SLOTS;
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -123,21 +123,22 @@ typedef struct JSInlineFrame {
 
 /* JS stack frame flags. */
 #define JSFRAME_CONSTRUCTING   0x01 /* frame is for a constructor invocation */
 #define JSFRAME_COMPUTED_THIS  0x02 /* frame.thisp was computed already */
 #define JSFRAME_ASSIGNING      0x04 /* a complex (not simplex JOF_ASSIGNING) op
                                        is currently assigning to a property */
 #define JSFRAME_DEBUGGER       0x08 /* frame for JS_EvaluateInStackFrame */
 #define JSFRAME_EVAL           0x10 /* frame for obj_eval */
-#define JSFRAME_ROOTED_ARGV    0x20 /* frame.argv is rooted by the caller */
+#define JSFRAME_SCRIPT_OBJECT  0x20 /* compiling source for a Script object */
 #define JSFRAME_YIELDING       0x40 /* js_Interpret dispatched JSOP_YIELD */
 #define JSFRAME_ITERATOR       0x80 /* trying to get an iterator for for-in */
 #define JSFRAME_POP_BLOCKS    0x100 /* scope chain contains blocks to pop */
 #define JSFRAME_GENERATOR     0x200 /* frame belongs to generator-iterator */
+#define JSFRAME_ROOTED_ARGV   0x400 /* frame.argv is rooted by the caller */
 
 #define JSFRAME_OVERRIDE_SHIFT 24   /* override bit-set params; see jsfun.c */
 #define JSFRAME_OVERRIDE_BITS  8
 
 #define JSFRAME_SPECIAL       (JSFRAME_DEBUGGER | JSFRAME_EVAL)
 
 /*
  * Property cache with structurally typed capabilities for invalidation, for
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1171,17 +1171,16 @@ js_obj_eval(JSContext *cx, JSObject *obj
 {
     JSStackFrame *fp, *caller;
     JSBool indirectCall;
     JSObject *scopeobj;
     JSString *str;
     const char *file;
     uintN line;
     JSPrincipals *principals;
-    uint32 tcflags;
     JSScript *script;
     JSBool ok;
 #if JS_HAS_EVAL_THIS_SCOPE
     JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
     JSObject *setCallerScopeChain = NULL;
     JSBool setCallerVarObj = JS_FALSE;
 #endif
 
@@ -1302,20 +1301,31 @@ js_obj_eval(JSContext *cx, JSObject *obj
         principals = JS_EvalFramePrincipals(cx, fp, caller);
         file = js_ComputeFilename(cx, caller, principals, &line);
     } else {
         file = NULL;
         line = 0;
         principals = NULL;
     }
 
-    tcflags = TCF_COMPILE_N_GO;
-    if (caller)
-        tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
-    script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
+    /*
+     * Set JSFRAME_EVAL on fp and any frames (e.g., fun_call if eval.call was
+     * invoked) between fp and its scripted caller, to help the compiler easily
+     * find the same caller whose scope and var obj we've set.
+     *
+     * XXX this nonsense could, and perhaps should, go away with a better way
+     * to pass params to the compiler than via the top-most frame.
+     */
+    do {
+        fp->flags |= JSFRAME_EVAL;
+    } while ((fp = fp->down) != caller);
+
+    script = js_CompileScript(cx, scopeobj, principals,
+                              TCF_COMPILE_N_GO |
+                              TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1),
                               JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
                               NULL, file, line);
     if (!script) {
         ok = JS_FALSE;
         goto out;
     }
 
     if (argc < 2) {
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -153,31 +153,27 @@ static JSParenParser   ParenExpr;
 #ifdef METER_PARSENODES
 static uint32 parsenodes = 0;
 static uint32 maxparsenodes = 0;
 static uint32 recyclednodes = 0;
 #endif
 
 JSBool
 js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
-                    JSStackFrame *callerFrame,
                     const jschar *base, size_t length,
                     FILE *fp, const char *filename, uintN lineno)
 {
-    JS_ASSERT_IF(callerFrame, callerFrame->script);
-
     pc->tempPoolMark = JS_ARENA_MARK(&cx->tempPool);
     if (!js_InitTokenStream(cx, TS(pc), base, length, fp, filename, lineno)) {
         JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark);
         return JS_FALSE;
     }
     if (principals)
         JSPRINCIPALS_HOLD(cx, principals);
     pc->principals = principals;
-    pc->callerFrame = callerFrame;
     pc->nodeList = NULL;
     pc->traceListHead = NULL;
 
     /* Root atoms and objects allocated for the parsed tree. */
     JS_KEEP_ATOMS(cx->runtime);
     JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx, pc, &pc->tempRoot);
     return JS_TRUE;
 }
@@ -454,100 +450,158 @@ CheckGetterOrSetter(JSContext *cx, JSTok
                                          name)) {
             return TOK_ERROR;
         }
     }
     return tt;
 }
 #endif
 
+static void
+MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp,
+                JSStackFrame *newfp)
+{
+    /*
+     * Always push a new frame if the current frame is special, so that
+     * Variables gets the correct variables object: the one from the special
+     * frame's caller.
+     */
+    if (oldfp &&
+        oldfp->varobj &&
+        oldfp->scopeChain == chain &&
+        !(oldfp->flags & JSFRAME_SPECIAL)) {
+        return;
+    }
+
+    memset(newfp, 0, sizeof *newfp);
+
+    /* Default to sharing the same variables object and scope chain. */
+    newfp->varobj = newfp->scopeChain = chain;
+    if (cx->options & JSOPTION_VAROBJFIX) {
+        while ((chain = JS_GetParent(cx, chain)) != NULL)
+            newfp->varobj = chain;
+    }
+    newfp->down = oldfp;
+    if (oldfp) {
+        /*
+         * In the case of eval and debugger frames, we need to dig down and find
+         * the real variables objects and function that our new stack frame is
+         * going to use.
+         */
+        newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_SCRIPT_OBJECT);
+        while (oldfp->flags & JSFRAME_SPECIAL) {
+            oldfp = oldfp->down;
+            if (!oldfp)
+                break;
+        }
+        if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) {
+            newfp->varobj = oldfp->varobj;
+            newfp->callee = oldfp->callee;
+            newfp->fun = oldfp->fun;
+        }
+    }
+    cx->fp = newfp;
+}
+
 /*
  * Parse a top-level JS script.
  */
 JSParseNode *
 js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc)
 {
+    JSStackFrame *fp, frame;
     JSTreeContext tc;
     JSParseNode *pn;
 
     /*
+     * Push a compiler frame if we have no frames, or if the top frame is a
+     * lightweight function activation, or if its scope chain doesn't match
+     * the one passed to us.
+     */
+    fp = cx->fp;
+    MaybeSetupFrame(cx, chain, fp, &frame);
+
+    /*
      * Protect atoms from being collected by a GC activation, which might
      * - nest on this thread due to out of memory (the so-called "last ditch"
      *   GC attempted within js_NewGCThing), or
      * - run for any reason on another thread if this thread is suspended on
      *   an object lock before it finishes generating bytecode into a script
      *   protected from the GC by a root or a stack frame reference.
      */
     TREE_CONTEXT_INIT(&tc, pc);
-    tc.u.scopeChain = chain;
     pn = Statements(cx, TS(pc), &tc);
     if (pn) {
         if (!js_MatchToken(cx, TS(pc), TOK_EOF)) {
             js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
                                         JSMSG_SYNTAX_ERROR);
             pn = NULL;
         } else {
             pn->pn_type = TOK_LC;
             if (!js_FoldConstants(cx, pn, &tc))
                 pn = NULL;
         }
     }
 
     TREE_CONTEXT_FINISH(cx, &tc);
+    cx->fp = fp;
     return pn;
 }
 
 /*
  * Compile a top-level script.
  */
-extern JSScript *
-js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
-                 JSPrincipals *principals, uint32 tcflags,
-                 const jschar *chars, size_t length,
+JSScript *
+js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
+                 uint32 tcflags, const jschar *chars, size_t length,
                  FILE *file, const char *filename, uintN lineno)
 {
     JSParseContext pc;
+    JSStackFrame *fp, frame;
     JSArenaPool codePool, notePool;
     JSCodeGenerator cg;
     JSTokenType tt;
     JSParseNode *pn;
     uint32 scriptGlobals;
     JSScript *script;
 #ifdef METER_PARSENODES
     void *sbrk(ptrdiff_t), *before = sbrk(0);
 #endif
 
-    JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL |
-                            TCF_STATIC_DEPTH_MASK)));
+    JS_ASSERT(!(tcflags & ~(TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL | TCF_STATIC_DEPTH_MASK)));
+
+    if (!js_InitParseContext(cx, &pc, principals, chars, length, file,
+                             filename, lineno)) {
+        return NULL;
+    }
 
     /*
-     * The scripted callerFrame can only be given for compile-and-go scripts
-     * and non-zero static depth requires callerFrame.
+     * From this point the control must flow through the label out.
+     *
+     * Push a compiler frame if we have no frames, or if the top frame is a
+     * lightweight function activation, or if its scope chain doesn't match
+     * the one passed to us.
      */
-    JS_ASSERT_IF(callerFrame, tcflags & TCF_COMPILE_N_GO);
-    JS_ASSERT_IF(TCF_GET_STATIC_DEPTH(tcflags) != 0, callerFrame);
-
-    if (!js_InitParseContext(cx, &pc, principals, callerFrame, chars, length,
-                             file, filename, lineno)) {
-        return NULL;
-    }
+    fp = cx->fp;
+    MaybeSetupFrame(cx, obj, fp, &frame);
 
     JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
                        &cx->scriptStackQuota);
     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
                        &cx->scriptStackQuota);
     js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,
                          pc.tokenStream.lineno);
 
     /* From this point the control must flow via the label out. */
     cg.treeContext.flags |= (uint16) tcflags;
-    cg.treeContext.u.scopeChain = scopeChain;
     cg.staticDepth = TCF_GET_STATIC_DEPTH(tcflags);
 
-    /* Inline Statements() to emit as we go to save space. */
+    /*
+     * Inline Statements() to emit as we go to save space.
+     */
     for (;;) {
         pc.tokenStream.flags |= TSF_OPERAND;
         tt = js_PeekToken(cx, &pc.tokenStream);
         pc.tokenStream.flags &= ~TSF_OPERAND;
         if (tt <= TOK_EOF) {
             if (tt == TOK_EOF)
                 break;
             JS_ASSERT(tt == TOK_ERROR);
@@ -555,16 +609,17 @@ js_CompileScript(JSContext *cx, JSObject
             goto out;
         }
 
         pn = Statement(cx, &pc.tokenStream, &cg.treeContext);
         if (!pn) {
             script = NULL;
             goto out;
         }
+
         JS_ASSERT(!cg.treeContext.blockNode);
 
         if (!js_FoldConstants(cx, pn, &cg.treeContext) ||
             !js_EmitTree(cx, &cg, pn)) {
             script = NULL;
             goto out;
         }
         RecycleTree(pn, &cg.treeContext);
@@ -632,28 +687,29 @@ js_CompileScript(JSContext *cx, JSObject
 #endif
 #ifdef JS_ARENAMETER
     JS_DumpArenaStats(stdout);
 #endif
     script = js_NewScriptFromCG(cx, &cg);
 
 #ifdef JS_SCOPE_DEPTH_METER
     if (script) {
-        JSObject *obj = scopeChain;
+        JSObject *pobj = obj;
         uintN depth = 1;
-        while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL)
+        while ((pobj = OBJ_GET_PARENT(cx, pobj)) != NULL)
             ++depth;
         JS_BASIC_STATS_ACCUM(&cx->runtime->hostenvScopeDepthStats, depth);
     }
 #endif
 
   out:
     js_FinishCodeGenerator(cx, &cg);
     JS_FinishArenaPool(&codePool);
     JS_FinishArenaPool(&notePool);
+    cx->fp = fp;
     js_FinishParseContext(cx, &pc);
     return script;
 
   too_many_slots:
     js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL,
                                 JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS);
     script = NULL;
     goto out;
@@ -788,18 +844,18 @@ HasFinalReturn(JSParseNode *pn)
 
 static JSBool
 ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum,
                 uintN anonerrnum)
 {
     const char *name;
 
     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
-    if (tc->u.fun->atom) {
-        name = js_AtomToPrintableString(cx, tc->u.fun->atom);
+    if (tc->fun->atom) {
+        name = js_AtomToPrintableString(cx, tc->fun->atom);
     } else {
         errnum = anonerrnum;
         name = NULL;
     }
     return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags,
                                        errnum, name);
 }
 
@@ -883,30 +939,30 @@ js_CompileFunctionBody(JSContext *cx, JS
                        const jschar *chars, size_t length,
                        const char *filename, uintN lineno)
 {
     JSParseContext pc;
     JSArenaPool codePool, notePool;
     JSCodeGenerator funcg;
     JSParseNode *pn;
 
-    if (!js_InitParseContext(cx, &pc, principals, NULL, chars, length, NULL,
+    if (!js_InitParseContext(cx, &pc, principals, chars, length, NULL,
                              filename, lineno)) {
         return JS_FALSE;
     }
 
     /* No early return from this point until js_FinishParseContext call. */
     JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode),
                        &cx->scriptStackQuota);
     JS_INIT_ARENA_POOL(&notePool, "note", 1024, sizeof(jssrcnote),
                        &cx->scriptStackQuota);
     js_InitCodeGenerator(cx, &funcg, &pc, &codePool, &notePool,
                          pc.tokenStream.lineno);
     funcg.treeContext.flags |= TCF_IN_FUNCTION;
-    funcg.treeContext.u.fun = fun;
+    funcg.treeContext.fun = fun;
 
     /*
      * Farble the body so that it looks like a block statement to js_EmitTree,
      * which is called beneath FunctionBody; see Statements, further below in
      * this file.  FunctionBody pushes a STMT_BLOCK record around its call to
      * Statements, so Statements will not compile each statement as it loops
      * to save JSParseNode space -- it will not compile at all, only build a
      * JSParseNode tree.
@@ -964,28 +1020,28 @@ static JSBool
 BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc)
 {
     const char *name;
 
     /*
      * Check for a duplicate parameter name, a "feature" required by ECMA-262.
      */
     JS_ASSERT(tc->flags & TCF_IN_FUNCTION);
-    if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
+    if (js_LookupLocal(cx, tc->fun, atom, NULL) != JSLOCAL_NONE) {
         name = js_AtomToPrintableString(cx, atom);
         if (!name ||
             !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL,
                                          JSREPORT_WARNING | JSREPORT_STRICT,
                                          JSMSG_DUPLICATE_FORMAL,
                                          name)) {
             return JS_FALSE;
         }
     }
 
-    return js_AddLocal(cx, tc->u.fun, atom, JSLOCAL_ARG);
+    return js_AddLocal(cx, tc->fun, atom, JSLOCAL_ARG);
 }
 
 static JSBool
 BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom,
                   JSLocalKind localKind)
 {
     JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
 
@@ -1020,44 +1076,42 @@ BindDestructuringArg(JSContext *cx, Bind
     ATOM_LIST_SEARCH(ale, &tc->decls, atom);
     if (!ale) {
         ale = js_IndexAtom(cx, atom, &tc->decls);
         if (!ale)
             return JS_FALSE;
         ALE_SET_JSOP(ale, data->op);
     }
 
-    if (js_LookupLocal(cx, tc->u.fun, atom, NULL) != JSLOCAL_NONE) {
+    if (js_LookupLocal(cx, tc->fun, atom, NULL) != JSLOCAL_NONE) {
         name = js_AtomToPrintableString(cx, atom);
         if (!name ||
             !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn,
                                          JSREPORT_WARNING | JSREPORT_STRICT,
                                          JSMSG_DUPLICATE_FORMAL,
                                          name)) {
             return JS_FALSE;
         }
     } else {
-        if (!BindLocalVariable(cx, tc->u.fun, atom, JSLOCAL_VAR))
+        if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR))
             return JS_FALSE;
     }
     return JS_TRUE;
 }
 #endif /* JS_HAS_DESTRUCTURING */
 
 static JSFunction *
 NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom,
                     uintN lambda)
 {
     JSObject *parent;
     JSFunction *fun;
 
     JS_ASSERT((lambda & ~JSFUN_LAMBDA) == 0);
-    parent = (tc->flags & TCF_IN_FUNCTION)
-             ? FUN_OBJECT(tc->u.fun)
-             : tc->u.scopeChain;
+    parent = (tc->flags & TCF_IN_FUNCTION) ? FUN_OBJECT(tc->fun) : cx->fp->varobj;
     fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED | lambda,
                          parent, atom);
     if (fun && !(tc->flags & TCF_COMPILE_N_GO)) {
         STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
         STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
     }
     return fun;
 }
@@ -1154,19 +1208,19 @@ FunctionDef(JSContext *cx, JSTokenStream
 
             /*
              * Define a property on the outer function so that BindNameToSlot
              * can properly optimize accesses. Note that we need a variable,
              * not an argument, for the function statement. Thus we add a
              * variable even if the parameter with the given name already
              * exists.
              */
-            localKind = js_LookupLocal(cx, tc->u.fun, funAtom, NULL);
+            localKind = js_LookupLocal(cx, tc->fun, funAtom, NULL);
             if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) {
-                if (!js_AddLocal(cx, tc->u.fun, funAtom, JSLOCAL_VAR))
+                if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR))
                     return NULL;
             }
         }
     }
 
     fun = NewCompilerFunction(cx, tc, funAtom, lambda);
     if (!fun)
         return NULL;
@@ -1182,17 +1236,17 @@ FunctionDef(JSContext *cx, JSTokenStream
      */
     funpob = js_NewParsedObjectBox(cx, tc->parseContext, FUN_OBJECT(fun));
     if (!funpob)
         return NULL;
 
     /* Initialize early for possible flags mutation via DestructuringExpr. */
     TREE_CONTEXT_INIT(&funtc, tc->parseContext);
     funtc.flags |= TCF_IN_FUNCTION | (tc->flags & TCF_COMPILE_N_GO);
-    funtc.u.fun = fun;
+    funtc.fun = fun;
 
     /* Now parse formal argument list and compute fun->nargs. */
     MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL);
     if (!js_MatchToken(cx, ts, TOK_RP)) {
         do {
             tt = js_GetToken(cx, ts);
             switch (tt) {
 #if JS_HAS_DESTRUCTURING
@@ -1621,29 +1675,29 @@ BindVarOrConst(JSContext *cx, BindData *
     if (!(tc->flags & TCF_IN_FUNCTION)) {
         /*
          * Don't lookup global variables or variables in an active frame at
          * compile time.
          */
         return JS_TRUE;
     }
 
-    localKind = js_LookupLocal(cx, tc->u.fun, atom, NULL);
+    localKind = js_LookupLocal(cx, tc->fun, atom, NULL);
     if (localKind == JSLOCAL_NONE) {
         /*
          * Property not found in current variable scope: we have not seen this
          * variable before.  Define a new local variable by adding a property
          * to the function's scope, allocating one slot in the function's vars
          * frame. Any locals declared in with statement bodies are handled at
          * runtime, by script prolog JSOP_DEFVAR opcodes generated for
          * slot-less vars.
          */
         localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR;
         if (!js_InWithStatement(tc) &&
-            !BindLocalVariable(cx, tc->u.fun, atom, localKind)) {
+            !BindLocalVariable(cx, tc->fun, atom, localKind)) {
             return JS_FALSE;
         }
     } else if (localKind == JSLOCAL_ARG) {
         name = js_AtomToPrintableString(cx, atom);
         if (!name)
             return JS_FALSE;
 
         if (op == JSOP_DEFCONST) {
@@ -3396,16 +3450,17 @@ Statement(JSContext *cx, JSTokenStream *
 static JSParseNode *
 Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSTokenType tt;
     JSBool let;
     JSStmtInfo *scopeStmt;
     BindData data;
     JSParseNode *pn, *pn2;
+    JSStackFrame *fp;
     JSAtom *atom;
 
     /*
      * The three options here are:
      * - TOK_LET: We are parsing a let declaration.
      * - TOK_LP: We are parsing the head of a let block.
      * - Otherwise, we're parsing var declarations.
      */
@@ -3435,16 +3490,17 @@ Variables(JSContext *cx, JSTokenStream *
      * The tricky part of this code is to create special parsenode opcodes for
      * getting and setting variables (which will be stored as special slots in
      * the frame).  The most complicated case is an eval() inside a function.
      * If the evaluated string references variables in the enclosing function,
      * then we need to generate the special variable opcodes.  We determine
      * this by looking up the variable's id in the current variable object.
      * Fortunately, we can avoid doing this for let declared variables.
      */
+    fp = cx->fp;
     if (let) {
         JS_ASSERT(tc->blockChain == scopeStmt->u.blockObj);
         data.binder = BindLet;
         data.u.let.overflow = JSMSG_TOO_MANY_LOCALS;
     } else {
         data.binder = BindVarOrConst;
     }
 
@@ -5145,43 +5201,46 @@ XMLElementOrListRoot(JSContext *cx, JSTo
     JS_SetOptions(cx, oldopts);
     return pn;
 }
 
 JS_FRIEND_API(JSParseNode *)
 js_ParseXMLText(JSContext *cx, JSObject *chain, JSParseContext *pc,
                 JSBool allowList)
 {
+    JSStackFrame *fp, frame;
     JSParseNode *pn;
     JSTreeContext tc;
     JSTokenType tt;
 
     /*
      * Push a compiler frame if we have no frames, or if the top frame is a
      * lightweight function activation, or if its scope chain doesn't match
      * the one passed to us.
      */
+    fp = cx->fp;
+    MaybeSetupFrame(cx, chain, fp, &frame);
     TREE_CONTEXT_INIT(&tc, pc);
-    tc.u.scopeChain = chain;
 
     /* Set XML-only mode to turn off special treatment of {expr} in XML. */
     TS(pc)->flags |= TSF_OPERAND | TSF_XMLONLYMODE;
     tt = js_GetToken(cx, TS(pc));
     TS(pc)->flags &= ~TSF_OPERAND;
 
     if (tt != TOK_XMLSTAGO) {
         js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR,
                                     JSMSG_BAD_XML_MARKUP);
         pn = NULL;
     } else {
         pn = XMLElementOrListRoot(cx, TS(pc), &tc, allowList);
     }
 
     TS(pc)->flags &= ~TSF_XMLONLYMODE;
     TREE_CONTEXT_FINISH(cx, &tc);
+    cx->fp = fp;
     return pn;
 }
 
 #endif /* JS_HAS_XMLSUPPORT */
 
 static JSParseNode *
 PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
             JSTokenType tt, JSBool afterDot)
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -427,18 +427,16 @@ struct JSParsedObjectBox {
     JSParsedObjectBox   *emitLink;
     JSObject            *object;
 };
 
 struct JSParseContext {
     JSTokenStream       tokenStream;
     void                *tempPoolMark;  /* initial JSContext.tempPool mark */
     JSPrincipals        *principals;    /* principals associated with source */
-    JSStackFrame        *callerFrame;   /* scripted caller frame for eval and
-                                           debug scripts */
     JSParseNode         *nodeList;      /* list of recyclable parse-node
                                            structs */
     JSParsedObjectBox   *traceListHead; /* list of parsed object for GC
                                            tracing */
     JSTempValueRooter   tempRoot;       /* root to trace traceListHead */
 };
 
 /*
@@ -448,19 +446,18 @@ struct JSParseContext {
 
 /*
  * Parse a top-level JS script.
  */
 extern JSParseNode *
 js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc);
 
 extern JSScript *
-js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
-                 JSPrincipals *principals, uint32 tcflags,
-                 const jschar *chars, size_t length,
+js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals,
+                 uint32 tcflags, const jschar *chars, size_t length,
                  FILE *file, const char *filename, uintN lineno);
 
 extern JSBool
 js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
                        const jschar *chars, size_t length,
                        const char *filename, uintN lineno);
 
 extern JSBool
@@ -477,17 +474,16 @@ js_ParseXMLText(JSContext *cx, JSObject 
  * js_InitTokenStream.
  *
  * The parse context owns the arena pool "tops-of-stack" space above the
  * current JSContext.tempPool mark. This means you cannot allocate from
  * tempPool and save the pointer beyond the next js_FinishParseContext.
  */
 extern JSBool
 js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
-                    JSStackFrame *callerFrame,
                     const jschar *base, size_t length, FILE *fp,
                     const char *filename, uintN lineno);
 
 extern void
 js_FinishParseContext(JSContext *cx, JSParseContext *pc);
 
 extern void
 js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -53,17 +53,16 @@
 #include "jsdbgapi.h"
 #include "jsemit.h"
 #include "jsfun.h"
 #include "jsinterp.h"
 #include "jslock.h"
 #include "jsnum.h"
 #include "jsopcode.h"
 #include "jsparse.h"
-#include "jsscope.h"
 #include "jsscript.h"
 #if JS_HAS_XDR
 #include "jsxdrapi.h"
 #endif
 
 #if JS_HAS_SCRIPT_OBJECT
 
 static const char js_script_exec_str[]    = "Script.prototype.exec";
@@ -196,17 +195,17 @@ script_toString(JSContext *cx, uintN arg
 static JSBool
 script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                    jsval *rval)
 {
     JSString *str;
     JSObject *scopeobj;
     jsval v;
     JSScript *script, *oldscript;
-    JSStackFrame *caller;
+    JSStackFrame *fp, *caller;
     const char *file;
     uintN line;
     JSPrincipals *principals;
     uint32 tcflags;
     jsint execDepth;
 
     /* Make sure obj is a Script object. */
     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
@@ -225,49 +224,52 @@ script_compile_sub(JSContext *cx, JSObje
     scopeobj = NULL;
     if (argc >= 2) {
         if (!js_ValueToObject(cx, argv[1], &scopeobj))
             return JS_FALSE;
         argv[1] = OBJECT_TO_JSVAL(scopeobj);
     }
 
     /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
-    caller = JS_GetScriptedCaller(cx, cx->fp);
-    JS_ASSERT(!caller || cx->fp->scopeChain == caller->scopeChain);
+    fp = cx->fp;
+    caller = JS_GetScriptedCaller(cx, fp);
+    JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain);
 
     if (caller) {
         if (!scopeobj) {
             scopeobj = js_GetScopeChain(cx, caller);
             if (!scopeobj)
                 return JS_FALSE;
+            fp->scopeChain = scopeobj;  /* for the compiler's benefit */
         }
 
-        principals = JS_EvalFramePrincipals(cx, cx->fp, caller);
+        principals = JS_EvalFramePrincipals(cx, fp, caller);
         file = js_ComputeFilename(cx, caller, principals, &line);
     } else {
         file = NULL;
         line = 0;
         principals = NULL;
     }
 
     /* Ensure we compile this script with the right (inner) principals. */
     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile_str);
     if (!scopeobj)
         return JS_FALSE;
 
     /*
      * Compile the new script using the caller's scope chain, a la eval().
      * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in
-     * tcflags and use NULL for the callerFrame argument, because compilation
-     * is here separated from execution, and the run-time scope chain may not
-     * match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and
-     * jsparse.c to optimize based on identity of run- and compile-time scope.
+     * tcflags, because compilation is here separated from execution, and the
+     * run-time scope chain may not match the compile-time.  JSFRAME_EVAL is
+     * tested in jsemit.c and jsscan.c to optimize based on identity of run-
+     * and compile-time scope.
      */
-    tcflags = 0;
-    script = js_CompileScript(cx, scopeobj, NULL, principals, tcflags,
+    fp->flags |= JSFRAME_SCRIPT_OBJECT;
+    tcflags = caller ? TCF_PUT_STATIC_DEPTH(caller->staticDepth + 1) : 0;
+    script = js_CompileScript(cx, scopeobj, principals, tcflags,
                               JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
                               NULL, file, line);
     if (!script)
         return JS_FALSE;
 
     JS_LOCK_OBJ(cx, obj);
     execDepth = GetScriptExecDepth(cx, obj);
 
@@ -832,24 +834,24 @@ static const char js_thaw_str[] = "thaw"
 
 #endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */
 #endif /* JS_HAS_XDR */
 
 #if JS_HAS_SCRIPT_OBJECT
 
 static JSFunctionSpec script_methods[] = {
 #if JS_HAS_TOSOURCE
-    JS_FN(js_toSource_str,   script_toSource,   0,0),
+    JS_FN(js_toSource_str,   script_toSource,   0,0,0),
 #endif
-    JS_FN(js_toString_str,   script_toString,   0,0),
-    JS_FN("compile",         script_compile,    2,0),
-    JS_FN("exec",            script_exec,       1,0),
+    JS_FN(js_toString_str,   script_toString,   0,0,0),
+    JS_FN("compile",         script_compile,    0,2,0),
+    JS_FN("exec",            script_exec,       0,1,0),
 #if JS_HAS_XDR_FREEZE_THAW
-    JS_FN("freeze",          script_freeze,     0,0),
-    JS_FN(js_thaw_str,       script_thaw,       1,0),
+    JS_FN("freeze",          script_freeze,     0,0,0),
+    JS_FN(js_thaw_str,       script_thaw,       0,1,0),
 #endif /* JS_HAS_XDR_FREEZE_THAW */
     JS_FS_END
 };
 
 #endif /* JS_HAS_SCRIPT_OBJECT */
 
 static void
 script_finalize(JSContext *cx, JSObject *obj)
@@ -925,24 +927,24 @@ static JSBool
 script_static_thaw(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
 
     obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
     if (!obj)
         return JS_FALSE;
     vp[1] = OBJECT_TO_JSVAL(obj);
-    if (!script_thaw(cx, argc, vp))
+    if (!script_thaw(cx, vp))
         return JS_FALSE;
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 static JSFunctionSpec script_static_methods[] = {
-    JS_FN(js_thaw_str,       script_static_thaw,     1,0),
+    JS_FN(js_thaw_str,       script_static_thaw,     1,1,0),
     JS_FS_END
 };
 
 #else  /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
 
 #define script_static_methods   NULL
 
 #endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
@@ -1466,17 +1468,17 @@ js_NewScriptFromCG(JSContext *cx, JSCode
     if (!script)
         return NULL;
 
     /* Now that we have script, error control flow must go to label bad. */
     script->main += prologLength;
     memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
     memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
     nfixed = (cg->treeContext.flags & TCF_IN_FUNCTION)
-             ? cg->treeContext.u.fun->u.i.nvars
+             ? cg->treeContext.fun->u.i.nvars
              : cg->treeContext.ngvars + cg->regexpList.length;
     JS_ASSERT(nfixed < SLOTNO_LIMIT);
     script->nfixed = (uint16) nfixed;
     js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
 
     filename = cg->treeContext.parseContext->tokenStream.filename;
     if (filename) {
         script->filename = js_SaveScriptFilename(cx, filename);
@@ -1516,17 +1518,17 @@ js_NewScriptFromCG(JSContext *cx, JSCode
     }
 
     /*
      * We initialize fun->u.script to be the script constructed above
      * so that the debugger has a valid FUN_SCRIPT(fun).
      */
     fun = NULL;
     if (cg->treeContext.flags & TCF_IN_FUNCTION) {
-        fun = cg->treeContext.u.fun;
+        fun = cg->treeContext.fun;
         JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
         JS_ASSERT_IF(script->upvarsOffset != 0,
                      JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars);
 
         js_FreezeLocalNames(cx, fun);
         fun->u.i.script = script;
 #ifdef CHECK_SCRIPT_OWNER
         script->owner = NULL;
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1882,17 +1882,17 @@ ParseXMLSource(JSContext *cx, JSString *
             lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
             for (endp = srcp + srclen; srcp < endp; srcp++) {
                 if (*srcp == '\n')
                     --lineno;
             }
         }
     }
 
-    if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
+    if (!js_InitParseContext(cx, &pc, NULL, chars, length, NULL,
                              filename, lineno))
         goto out;
     pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
     if (pn && XMLArrayInit(cx, &nsarray, 1)) {
         if (GetXMLSettingFlags(cx, &flags))
             xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
 
         XMLArrayFinish(cx, &nsarray);