Add local and argument closure information to JSScript (bug 592973, r=brendan+dmandelin).
☠☠ backed out by 5eb565456a99 ☠ ☠
authorDavid Anderson <danderson@mozilla.com>
Mon, 13 Sep 2010 22:33:44 -0700
changeset 74583 99e1b185792f0fcd5b5361554a8f32a177c12b8b
parent 74582 c76f61d9595b09e3fcc0d5813b42cc13bb5dca1a
child 74584 3754355930d5047abd919605f738d07212d4fad2
child 74586 5eb565456a99fee3ed3bb89a8aec3cd5a3cfe081
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersbrendan
bugs592973
milestone2.0b6pre
Add local and argument closure information to JSScript (bug 592973, r=brendan+dmandelin).
js/src/jsemit.cpp
js/src/jsemit.h
js/src/jsfun.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsopcode.tbl
js/src/jsparse.cpp
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jstracer.cpp
js/src/jsxdrapi.h
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/FrameState-inl.h
js/src/methodjit/FrameState.cpp
js/src/methodjit/FrameState.h
js/src/methodjit/MethodJIT.h
js/src/trace-test/tests/jaeger/bug592973.js
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -97,17 +97,19 @@ JSCodeGenerator::JSCodeGenerator(Parser 
     stackDepth(0), maxStackDepth(0),
     ntrynotes(0), lastTryNode(NULL),
     spanDeps(NULL), jumpTargets(NULL), jtFreeList(NULL),
     numSpanDeps(0), numJumpTargets(0), spanDepTodo(0),
     arrayCompDepth(0),
     emitLevel(0),
     constMap(parser->context),
     constList(parser->context),
-    globalUses(ContextAllocPolicy(parser->context))
+    globalUses(ContextAllocPolicy(parser->context)),
+    closedArgs(ContextAllocPolicy(parser->context)),
+    closedVars(ContextAllocPolicy(parser->context))
 {
     flags = TCF_COMPILING;
     memset(&prolog, 0, sizeof prolog);
     memset(&main, 0, sizeof main);
     current = &main;
     firstLine = prolog.currentLine = main.currentLine = lineno;
     prolog.noteMask = main.noteMask = SRCNOTE_CHUNK - 1;
     memset(&upvarMap, 0, sizeof upvarMap);
@@ -1805,16 +1807,23 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, 
         return JS_FALSE;
     pc = CG_CODE(cg, off);
     SET_UINT16(pc, slot);
     pc += 2;
     SET_INDEX(pc, index);
     return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0;
 }
 
+bool
+JSCodeGenerator::shouldNoteClosedName(JSParseNode *pn)
+{
+    JSDefinition *dn = (JSDefinition *)pn;
+    return !callsEval() && pn->pn_defn && (dn->pn_dflags & PND_CLOSED);
+}
+
 /*
  * Adjust the slot for a block local to account for the number of variables
  * that share the same index space with locals. Due to the incremental code
  * generation for top-level script, we do the adjustment via code patching in
  * Compiler::compileScript; see comments there.
  *
  * The function returns -1 on failures.
  */
@@ -1860,24 +1869,26 @@ EmitEnterBlock(JSContext *cx, JSParseNod
         dn->pn_cookie.set(dn->pn_cookie.level(), uint16(dn->frameSlot() + depth));
 #ifdef DEBUG
         for (JSParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
             JS_ASSERT(pnu->pn_lexdef == dn);
             JS_ASSERT(!(pnu->pn_dflags & PND_BOUND));
             JS_ASSERT(pnu->pn_cookie.isFree());
         }
 #endif
+
+        /*
+         * If this variable is closed over, and |eval| is not present, then
+         * then set a bit in dslots so the Method JIT can deoptimize this
+         * slot.
+         */
+        bool isClosed = cg->shouldNoteClosedName(dn);
+        blockObj->setSlot(slot, BooleanValue(isClosed));
     }
 
-    /*
-     * Shrink slots to free blockObj->dslots and ensure a prompt safe crash if
-     * by accident some code tries to get a slot from a compiler-created Block
-     * prototype instead of from a clone.
-     */
-    blockObj->shrinkSlots(cx, base);
     return true;
 }
 
 /*
  * When eval is called from a function, the eval code or function code it
  * compiles may reference upvars that live in the eval-calling function. The
  * eval-invoked compiler does not have explicit definitions for these upvars
  * and we do not attempt to create them a-priori (by inspecting the function's
@@ -3632,17 +3643,17 @@ js_EmitFunctionScript(JSContext *cx, JSC
 
     if (cg->flags & TCF_FUN_UNBRAND_THIS) {
         if (js_Emit1(cx, cg, JSOP_UNBRANDTHIS) < 0)
             return false;
     }
 
     return js_EmitTree(cx, cg, body) &&
            js_Emit1(cx, cg, JSOP_STOP) >= 0 &&
-           js_NewScriptFromCG(cx, cg);
+           JSScript::NewScriptFromCG(cx, cg);
 }
 
 /* A macro for inlining at the top of js_EmitTree (whence it came). */
 #define UPDATE_LINE_NUMBER_NOTES(cx, cg, line)                                \
     JS_BEGIN_MACRO                                                            \
         uintN line_ = (line);                                                 \
         uintN delta_ = line_ - CG_CURRENT_LINE(cg);                           \
         if (delta_ != 0) {                                                    \
@@ -3701,23 +3712,21 @@ MaybeEmitVarDecl(JSContext *cx, JSCodeGe
         CG_SWITCH_TO_PROLOG(cg);
         if (!UpdateLineNumberNotes(cx, cg, pn->pn_pos.begin.lineno))
             return JS_FALSE;
         EMIT_INDEX_OP(prologOp, atomIndex);
         CG_SWITCH_TO_MAIN(cg);
     }
 
     if (JOF_OPTYPE(pn->pn_op) == JOF_LOCAL &&
-        !(cg->flags & TCF_FUN_CALLS_EVAL) &&
-        pn->pn_defn &&
-        (((JSDefinition *)pn)->pn_dflags & PND_CLOSED))
+        pn->pn_cookie.slot() < cg->fun->u.i.nvars &&
+        cg->shouldNoteClosedName(pn))
     {
-        CG_SWITCH_TO_PROLOG(cg);
-        EMIT_UINT16_IMM_OP(JSOP_DEFUPVAR, pn->pn_cookie.asInteger());
-        CG_SWITCH_TO_MAIN(cg);
+        if (!cg->closedVars.append(pn->pn_cookie.slot()))
+            return JS_FALSE;
     }
 
     if (result)
         *result = atomIndex;
     return JS_TRUE;
 }
 
 #if JS_HAS_DESTRUCTURING
@@ -4536,30 +4545,41 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
 #ifdef DEBUG
             JSLocalKind localKind =
 #endif
                 cg->fun->lookupLocal(cx, fun->atom, &slot);
             JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
             JS_ASSERT(index < JS_BIT(20));
             pn->pn_index = index;
             op = FUN_FLAT_CLOSURE(fun) ? JSOP_DEFLOCALFUN_FC : JSOP_DEFLOCALFUN;
-            if ((pn->pn_dflags & PND_CLOSED) && !(cg->flags & TCF_FUN_CALLS_EVAL)) {
-                CG_SWITCH_TO_PROLOG(cg);
-                EMIT_UINT16_IMM_OP(JSOP_DEFUPVAR, pn->pn_cookie.asInteger());
-                CG_SWITCH_TO_MAIN(cg);
+            if ((pn->pn_dflags & PND_CLOSED) &&
+                !cg->callsEval() &&
+                !cg->closedVars.append(pn->pn_cookie.slot())) {
+                return JS_FALSE;
             }
             if (!EmitSlotIndexOp(cx, op, slot, index, cg))
                 return JS_FALSE;
         }
         break;
       }
 
       case TOK_ARGSBODY:
-        ok = js_EmitTree(cx, cg, pn->last());
+      {
+        JSParseNode *pnlast = pn->last();
+        for (JSParseNode *pn2 = pn->pn_head; pn2 != pnlast; pn2 = pn2->pn_next) {
+            if (!BindNameToSlot(cx, cg, pn2))
+                return JS_FALSE;
+            if (JOF_OPTYPE(pn2->pn_op) == JOF_QARG && cg->shouldNoteClosedName(pn2)) {
+                if (!cg->closedArgs.append(pn2->pn_cookie.slot()))
+                    return JS_FALSE;
+            }
+        }
+        ok = js_EmitTree(cx, cg, pnlast);
         break;
+      }
 
       case TOK_UPVARS:
         JS_ASSERT(cg->lexdeps.count == 0);
         JS_ASSERT(pn->pn_names.count != 0);
         cg->lexdeps = pn->pn_names;
         ok = js_EmitTree(cx, cg, pn->pn_tree);
         break;
 
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -367,17 +367,16 @@ struct JSTreeContext {              /* t
         return flags & TCF_FUN_USES_ARGUMENTS;
     }
 
     void noteCallsEval() {
         flags |= TCF_FUN_CALLS_EVAL;
     }
 
     bool callsEval() const {
-        JS_ASSERT(inFunction());
         return flags & TCF_FUN_CALLS_EVAL;
     }
 
     void noteParameterMutation() {
         JS_ASSERT(inFunction());
         flags |= TCF_FUN_MUTATES_PARAMETER;
     }
 
@@ -545,16 +544,21 @@ struct JSCodeGenerator : public JSTreeCo
     JSAtomList      upvarList;      /* map of atoms to upvar indexes */
     JSUpvarArray    upvarMap;       /* indexed upvar pairs (JS_realloc'ed) */
 
     typedef js::Vector<js::GlobalSlotArray::Entry, 16, js::ContextAllocPolicy> GlobalUseVector;
 
     GlobalUseVector globalUses;     /* per-script global uses */
     JSAtomList      globalMap;      /* per-script map of global name to globalUses vector */
 
+    /* Vectors of pn_cookie slot values. */
+    typedef js::Vector<uint32, 8, js::ContextAllocPolicy> SlotVector;
+    SlotVector      closedArgs;
+    SlotVector      closedVars;
+
     /*
      * Initialize cg to allocate bytecode space from codePool, source note
      * space from notePool, and all other arena-allocated temporaries from
      * parser->context->tempPool.
      */
     JSCodeGenerator(js::Parser *parser,
                     JSArenaPool *codePool, JSArenaPool *notePool,
                     uintN lineno);
@@ -577,16 +581,18 @@ struct JSCodeGenerator : public JSTreeCo
         return rv;
     }
 
     uintN sharpSlots() {
         return hasSharps() ? SHARP_NSLOTS : 0;
     }
 
     bool compilingForEval() { return !!(flags & TCF_COMPILE_FOR_EVAL); }
+
+    bool shouldNoteClosedName(JSParseNode *pn);
 };
 
 #define CG_TS(cg)               TS((cg)->parser)
 
 #define CG_BASE(cg)             ((cg)->current->base)
 #define CG_LIMIT(cg)            ((cg)->current->limit)
 #define CG_NEXT(cg)             ((cg)->current->next)
 #define CG_CODE(cg,offset)      (CG_BASE(cg) + (offset))
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -383,34 +383,36 @@ WrapEscapingClosure(JSContext *cx, JSSta
     JSScript *script = fun->u.i.script;
     jssrcnote *snbase = script->notes();
     jssrcnote *sn = snbase;
     while (!SN_IS_TERMINATOR(sn))
         sn = SN_NEXT(sn);
     uintN nsrcnotes = (sn - snbase) + 1;
 
     /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
-    JSScript *wscript = js_NewScript(cx, script->length, nsrcnotes,
-                                     script->atomMap.length,
-                                     (script->objectsOffset != 0)
-                                     ? script->objects()->length
-                                     : 0,
-                                     fun->u.i.nupvars,
-                                     (script->regexpsOffset != 0)
-                                     ? script->regexps()->length
-                                     : 0,
-                                     (script->trynotesOffset != 0)
-                                     ? script->trynotes()->length
-                                     : 0,
-                                     (script->constOffset != 0)
-                                     ? script->consts()->length
-                                     : 0,
-                                     (script->globalsOffset != 0)
-                                     ? script->globals()->length
-                                     : 0);
+    JSScript *wscript = JSScript::NewScript(cx, script->length, nsrcnotes,
+                                            script->atomMap.length,
+                                            (script->objectsOffset != 0)
+                                            ? script->objects()->length
+                                            : 0,
+                                            fun->u.i.nupvars,
+                                            (script->regexpsOffset != 0)
+                                            ? script->regexps()->length
+                                            : 0,
+                                            (script->trynotesOffset != 0)
+                                            ? script->trynotes()->length
+                                            : 0,
+                                            (script->constOffset != 0)
+                                            ? script->consts()->length
+                                            : 0,
+                                            (script->globalsOffset != 0)
+                                            ? script->globals()->length
+                                            : 0,
+                                            script->nClosedArgs,
+                                            script->nClosedVars);
     if (!wscript)
         return NULL;
 
     memcpy(wscript->code, script->code, script->length);
     wscript->main = wscript->code + (script->main - script->code);
 
     memcpy(wscript->notes(), snbase, nsrcnotes * sizeof(jssrcnote));
     memcpy(wscript->atomMap.vector, script->atomMap.vector,
@@ -426,16 +428,18 @@ WrapEscapingClosure(JSContext *cx, JSSta
     if (script->trynotesOffset != 0) {
         memcpy(wscript->trynotes()->vector, script->trynotes()->vector,
                wscript->trynotes()->length * sizeof(JSTryNote));
     }
     if (script->globalsOffset != 0) {
         memcpy(wscript->globals()->vector, script->globals()->vector,
                wscript->globals()->length * sizeof(GlobalSlotArray::Entry));
     }
+    if (script->nClosedArgs + script->nClosedVars != 0)
+        script->copyClosedSlotsTo(wscript);
 
     if (wfun->u.i.nupvars != 0) {
         JS_ASSERT(wfun->u.i.nupvars == wscript->upvars()->length);
         memcpy(wscript->upvars()->vector, script->upvars()->vector,
                wfun->u.i.nupvars * sizeof(uint32));
     }
 
     jsbytecode *pc = wscript->code;
@@ -476,16 +480,20 @@ WrapEscapingClosure(JSContext *cx, JSSta
     wscript->strictModeCode = script->strictModeCode;
     wscript->setVersion(script->getVersion());
     wscript->nfixed = script->nfixed;
     wscript->filename = script->filename;
     wscript->lineno = script->lineno;
     wscript->nslots = script->nslots;
     wscript->staticLevel = script->staticLevel;
     wscript->principals = script->principals;
+    wscript->compileAndGo = script->compileAndGo;
+    wscript->usesEval = script->usesEval;
+    wscript->warnedAboutTwoArgumentEval = script->warnedAboutTwoArgumentEval;
+    wscript->usesArguments = script->usesArguments;
     if (wscript->principals)
         JSPRINCIPALS_HOLD(cx, wscript->principals);
 #ifdef CHECK_SCRIPT_OWNER
     wscript->owner = script->owner;
 #endif
 
     /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
     FUN_SET_KIND(wfun, JSFUN_INTERPRETED);
@@ -1124,31 +1132,39 @@ js_PutCallObject(JSContext *cx, JSStackF
     JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
                      1 + JSObject::CALL_RESERVED_SLOTS);
     if (n != 0) {
         JS_ASSERT(JSFunction::FIRST_FREE_SLOT + n <= callobj.numSlots());
 
         uint32 nargs = fun->nargs;
         uint32 nvars = fun->u.i.nvars;
 
-#ifdef JS_METHODJIT
         JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_PRIVATE + JSObject::CALL_RESERVED_SLOTS + 1);
+
         JSScript *script = fun->u.i.script;
-        memcpy(callobj.dslots, fp->formalArgs(), nargs * sizeof(Value));
-        if (!script->jit || script->usesEval) {
-            memcpy(callobj.dslots + nargs, fp->slots(), nvars * sizeof(Value));
-        } else if (script->jit) {
-            for (uint32 i = 0; i < script->jit->nescaping; i++) {
-                uint32 e = script->jit->escaping[i];
+        if (script->usesEval) {
+            CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
+        } else {
+            /*
+             * For each arg & var that is closed over, copy it from the stack
+             * into the call object.
+             */
+            JSScript *script = fun->u.i.script;
+            uint32 nclosed = script->nClosedArgs;
+            for (uint32 i = 0; i < nclosed; i++) {
+                uint32 e = script->getClosedArg(i);
+                callobj.dslots[e] = fp->formalArg(e);
+            }
+
+            nclosed = script->nClosedVars;
+            for (uint32 i = 0; i < nclosed; i++) {
+                uint32 e = script->getClosedVar(i);
                 callobj.dslots[nargs + e] = fp->slots()[e];
             }
         }
-#else
-        CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
-#endif
     }
 
     /* Clear private pointers to fp, which is about to go away (js_Invoke). */
     if (js_IsNamedLambda(fun)) {
         JSObject *env = callobj.getParent();
 
         JS_ASSERT(env->getClass() == &js_DeclEnvClass);
         JS_ASSERT(env->getPrivate() == fp);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2406,18 +2406,17 @@ END_EMPTY_CASES
 BEGIN_CASE(JSOP_TRACE)
 #ifdef JS_METHODJIT
     LEAVE_ON_SAFE_POINT();
 #endif
 END_CASE(JSOP_TRACE)
 
 /* ADD_EMPTY_CASE is not used here as JSOP_LINENO_LENGTH == 3. */
 BEGIN_CASE(JSOP_LINENO)
-BEGIN_CASE(JSOP_DEFUPVAR)
-END_CASE(JSOP_DEFUPVAR)
+END_CASE(JSOP_LINENO)
 
 BEGIN_CASE(JSOP_PUSH)
     PUSH_UNDEFINED();
 END_CASE(JSOP_PUSH)
 
 BEGIN_CASE(JSOP_POP)
     regs.sp--;
 END_CASE(JSOP_POP)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3000,17 +3000,17 @@ js_PutBlockObject(JSContext *cx, JSBool 
     uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
     JS_ASSERT(depth <= size_t(cx->regs->sp - fp->base()));
     JS_ASSERT(count <= size_t(cx->regs->sp - fp->base() - depth));
 
     /* See comments in CheckDestructuring from jsparse.cpp. */
     JS_ASSERT(count >= 1);
 
     if (normalUnwind) {
-        uintN slot = JSSLOT_BLOCK_DEPTH + 1;
+        uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
         uintN flen = JS_MIN(count, JS_INITIAL_NSLOTS - slot);
         uintN stop = slot + flen;
 
         depth += fp->numFixed();
         while (slot < stop)
             obj->fslots[slot++] = fp->slots()[depth++];
         count -= flen;
         if (count != 0)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -1212,16 +1212,17 @@ inline bool JSObject::isBlock() const  {
  * A With object is like a Block object, in that both have one reserved slot
  * telling the stack depth of the relevant slots (the slot whose value is the
  * object named in the with statement, the slots containing the block's local
  * variables); and both have a private slot referring to the JSStackFrame in
  * whose activation they were created (or null if the with or block object
  * outlives the frame).
  */
 static const uint32 JSSLOT_BLOCK_DEPTH = JSSLOT_PRIVATE + 1;
+static const uint32 JSSLOT_BLOCK_FIRST_FREE_SLOT = JSSLOT_BLOCK_DEPTH + 1;
 
 inline bool
 JSObject::isStaticBlock() const
 {
     return isBlock() && !getProto();
 }
 
 inline bool
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -606,10 +606,8 @@ OPDEF(JSOP_GETGLOBAL,     239,"getglobal
 OPDEF(JSOP_SETGLOBAL,     240,"setglobal",     NULL,  3,  1,  1,  3,  JOF_GLOBAL|JOF_NAME|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INCGLOBAL,     241,"incglobal",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_TMPSLOT2)
 OPDEF(JSOP_DECGLOBAL,     242,"decglobal",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT2)
 OPDEF(JSOP_GLOBALINC,     243,"globalinc",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2)
 OPDEF(JSOP_GLOBALDEC,     244,"globaldec",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2)
 OPDEF(JSOP_CALLGLOBAL,    245,"callglobal",    NULL,  3,  0,  2, 19,  JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
 OPDEF(JSOP_FORGLOBAL,     246,"forglobal",     NULL,  3,  1,  1, 19,  JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
 
-OPDEF(JSOP_DEFUPVAR,      247,"defupvar",      NULL,  3,  0,  0, 19,  JOF_LOCAL|JOF_NAME)
-
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1028,17 +1028,17 @@ Compiler::compileScript(JSContext *cx, J
         goto out;
 #ifdef METER_PARSENODES
     printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n",
            (char *)sbrk(0) - (char *)before, CG_OFFSET(&cg), cg.noteCount);
 #endif
 #ifdef JS_ARENAMETER
     JS_DumpArenaStats(stdout);
 #endif
-    script = js_NewScriptFromCG(cx, &cg);
+    script = JSScript::NewScriptFromCG(cx, &cg);
     if (script && funbox && script != script->emptyScript())
         script->savedCallerFun = true;
 
 #ifdef JS_SCOPE_DEPTH_METER
     if (script) {
         JSObject *obj = scopeChain;
         uintN depth = 1;
         while ((obj = obj->getParent()) != NULL)
@@ -2555,17 +2555,17 @@ LeaveFunction(JSParseNode *fn, JSTreeCon
             }
 
             JSAtomListElement *outer_ale = tc->decls.lookup(atom);
 
             /*
              * Make sure to deoptimize lexical dependencies that are polluted
              * by eval or with, to safely statically bind globals (see bug 561923).
              */
-            if ((funtc->flags & TCF_FUN_CALLS_EVAL) ||
+            if (funtc->callsEval() ||
                 (outer_ale && tc->innermostWith &&
                  ALE_DEFN(outer_ale)->pn_pos < tc->innermostWith->pn_pos)) {
                 DeoptimizeUsesWithin(dn, fn->pn_pos);
             }
 
             JSDefinition *outer_dn;
 
             if (!outer_ale)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -73,46 +73,62 @@
 using namespace js;
 
 static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
 
 /* static */ const JSScript JSScript::emptyScriptConst = {
     JS_INIT_STATIC_CLIST(NULL),
     const_cast<jsbytecode*>(emptyScriptCode),
     1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, 0, true, false, false, false, false,
-    false, true, 
+    false,      /* usesEval */
+    false,      /* usesArguments */
+    true,       /* warnedAboutTwoArgumentEval */
 #ifdef JS_METHODJIT
-    /* debugMode */
-    false,
+    false,      /* debugMode */
 #endif
     const_cast<jsbytecode*>(emptyScriptCode),
-    {0, NULL}, NULL, 0, 0, 0, NULL, {NULL},
+    {0, NULL}, NULL, 0, 0, 0,
+    0,          /* nClosedArgs */
+    0,          /* nClosedVars */
+    NULL, {NULL},
 #ifdef CHECK_SCRIPT_OWNER
     reinterpret_cast<JSThread*>(1)
 #endif
 };
 
 #if JS_HAS_XDR
 
+enum ScriptBits {
+    ScriptBit_NoScriptRval,
+    ScriptBit_SavedCallerFun,
+    ScriptBit_HasSharps,
+    ScriptBit_StrictModeCode,
+    ScriptBit_UsesEval,
+    ScriptBit_CompileAndGo,
+    ScriptBit_UsesArguments
+};
+
 JSBool
 js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
              JSBool *hasMagic)
 {
     JSContext *cx;
     JSScript *script, *oldscript;
     JSBool ok;
     jsbytecode *code;
     uint32 length, lineno, nslots, magic;
     uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i;
     uint32 prologLength, version;
+    uint16 nClosedArgs, nClosedVars;
     JSPrincipals *principals;
     uint32 encodeable;
     JSBool filenameWasSaved;
     jssrcnote *notes, *sn;
     JSSecurityCallbacks *callbacks;
+    uint8 scriptBits = 0;
 
     cx = xdr->cx;
     script = *scriptp;
     nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = nconsts = 0;
     filenameWasSaved = JS_FALSE;
     notes = NULL;
 
     /* Should not XDR scripts optimized for a single global object. */
@@ -154,17 +170,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         if (cx->debugHooks->newScriptHook)
             needMutableScript = true;
         if (needMutableScript) {
             /*
              * We need a mutable empty script but the encoder serialized only
              * the shorthand (0 length word) for us. Make a new mutable empty
              * script here and return it immediately.
              */
-            script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0);
+            script = JSScript::NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
             if (!script)
                 return JS_FALSE;
 
             script->setVersion(JSVERSION_DEFAULT);
             script->noScriptRval = true;
             script->code[0] = JSOP_STOP;
             script->code[1] = SRC_NULL;
             *scriptp = script;
@@ -196,16 +212,34 @@ js_XDRScript(JSXDRState *xdr, JSScript *
         if (script->upvarsOffset != 0)
             nupvars = script->upvars()->length;
         if (script->regexpsOffset != 0)
             nregexps = script->regexps()->length;
         if (script->trynotesOffset != 0)
             ntrynotes = script->trynotes()->length;
         if (script->constOffset != 0)
             nconsts = script->consts()->length;
+
+        nClosedArgs = script->nClosedArgs;
+        nClosedVars = script->nClosedVars;
+
+        if (script->noScriptRval)
+            scriptBits |= (1 << ScriptBit_NoScriptRval);
+        if (script->savedCallerFun)
+            scriptBits |= (1 << ScriptBit_SavedCallerFun);
+        if (script->hasSharps)
+            scriptBits |= (1 << ScriptBit_HasSharps);
+        if (script->strictModeCode)
+            scriptBits |= (1 << ScriptBit_StrictModeCode);
+        if (script->compileAndGo)
+            scriptBits |= (1 << ScriptBit_CompileAndGo);
+        if (script->usesEval)
+            scriptBits |= (1 << ScriptBit_UsesEval);
+        if (script->usesArguments)
+            scriptBits |= (1 << ScriptBit_UsesArguments);
     }
 
     if (!JS_XDRUint32(xdr, &prologLength))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &version))
         return JS_FALSE;
 
     /*
@@ -221,33 +255,55 @@ js_XDRScript(JSXDRState *xdr, JSScript *
     if (!JS_XDRUint32(xdr, &nobjects))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &nupvars))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &nregexps))
         return JS_FALSE;
     if (!JS_XDRUint32(xdr, &nconsts))
         return JS_FALSE;
+    if (!JS_XDRUint16(xdr, &nClosedArgs))
+        return JS_FALSE;
+    if (!JS_XDRUint16(xdr, &nClosedVars))
+        return JS_FALSE;
+    if (!JS_XDRUint8(xdr, &scriptBits))
+        return JS_FALSE;
 
     AutoScriptRooter tvr(cx, NULL);
 
     if (xdr->mode == JSXDR_DECODE) {
-        script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
-                              nregexps, ntrynotes, nconsts, 0);
+        script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
+                                     nregexps, ntrynotes, nconsts, 0, nClosedArgs,
+                                     nClosedVars);
         if (!script)
             return JS_FALSE;
 
         script->main += prologLength;
         script->setVersion(JSVersion(version & 0xffff));
         script->nfixed = uint16(version >> 16);
 
         /* If we know nsrcnotes, we allocated space for notes in script. */
         notes = script->notes();
         *scriptp = script;
         tvr.setScript(script);
+
+        if (scriptBits & (1 << ScriptBit_NoScriptRval))
+            script->noScriptRval = true;
+        if (scriptBits & (1 << ScriptBit_SavedCallerFun))
+            script->savedCallerFun = true;
+        if (scriptBits & (1 << ScriptBit_HasSharps))
+            script->hasSharps = true;
+        if (scriptBits & (1 << ScriptBit_StrictModeCode))
+            script->strictModeCode = true;
+        if (scriptBits & (1 << ScriptBit_CompileAndGo))
+            script->compileAndGo = true;
+        if (scriptBits & (1 << ScriptBit_UsesEval))
+            script->usesEval = true;
+        if (scriptBits & (1 << ScriptBit_UsesArguments))
+            script->usesArguments = true;
     }
 
     /*
      * Control hereafter must goto error on failure, in order for the
      * DECODE case to destroy script.
      */
     oldscript = xdr->script;
     code = script->code;
@@ -306,16 +362,17 @@ js_XDRScript(JSXDRState *xdr, JSScript *
                 goto error;
             cx->free((void *) script->filename);
             script->filename = filename;
             filenameWasSaved = JS_TRUE;
         }
         script->lineno = (uintN)lineno;
         script->nslots = (uint16)nslots;
         script->staticLevel = (uint16)(nslots >> 16);
+
     }
 
     for (i = 0; i != natoms; ++i) {
         if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
             goto error;
     }
 
     /*
@@ -347,16 +404,26 @@ js_XDRScript(JSXDRState *xdr, JSScript *
     for (i = 0; i != nupvars; ++i) {
         if (!JS_XDRUint32(xdr, reinterpret_cast<uint32 *>(&script->upvars()->vector[i])))
             goto error;
     }
     for (i = 0; i != nregexps; ++i) {
         if (!js_XDRRegExpObject(xdr, &script->regexps()->vector[i]))
             goto error;
     }
+    for (i = 0; i != nClosedArgs; ++i) {
+        uint32 e = script->getClosedArg(i);
+        if (!JS_XDRUint32(xdr, &e))
+            goto error;
+    }
+    for (i = 0; i != nClosedVars; ++i) {
+        uint32 e = script->getClosedVar(i);
+        if (!JS_XDRUint32(xdr, &e))
+            goto error;
+    }
 
     if (ntrynotes != 0) {
         /*
          * We combine tn->kind and tn->stackDepth when serializing as XDR is not
          * efficient when serializing small integer types.
          */
         JSTryNote *tn, *tnfirst;
         uint32 kindAndDepth;
@@ -834,38 +901,43 @@ JS_STATIC_ASSERT(sizeof(jsbytecode) % si
 /*
  * Check that uint8 offset for object, upvar, regexp, and try note arrays is
  * sufficient.
  */
 JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) +
                  sizeof(JSUpvarArray) < JS_BIT(8));
 
 JSScript *
-js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
-             uint32 nobjects, uint32 nupvars, uint32 nregexps,
-             uint32 ntrynotes, uint32 nconsts, uint32 nglobals)
+JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
+                    uint32 nobjects, uint32 nupvars, uint32 nregexps,
+                    uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
+                    uint32 nClosedArgs, uint32 nClosedVars)
 {
     size_t size, vectorSize;
     JSScript *script;
     uint8 *cursor;
     unsigned constPadding = 0;
 
+    uint32 totalClosed = nClosedArgs + nClosedVars;
+
     size = sizeof(JSScript) +
            sizeof(JSAtom *) * natoms;
     
     if (nobjects != 0)
         size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
     if (nupvars != 0)
         size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32);
     if (nregexps != 0)
         size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
     if (ntrynotes != 0)
         size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
     if (nglobals != 0)
         size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry);
+    if (totalClosed != 0)
+        size += totalClosed * sizeof(uint32);
 
     if (nconsts != 0) {
         size += sizeof(JSConstArray);
         /*
          * Calculate padding assuming that consts go after the other arrays,
          * but before the bytecode and source notes.
          */
         constPadding = (8 - (size % 8)) % 8;
@@ -951,16 +1023,23 @@ js_NewScript(JSContext *cx, uint32 lengt
 
     if (nglobals != 0) {
         script->globals()->length = nglobals;
         script->globals()->vector = (GlobalSlotArray::Entry *)cursor;
         vectorSize = nglobals * sizeof(script->globals()->vector[0]);
         cursor += vectorSize;
     }
 
+    if (totalClosed != 0) {
+        script->nClosedArgs = nClosedArgs;
+        script->nClosedVars = nClosedVars;
+        script->closedSlots = (uint32 *)cursor;
+        cursor += totalClosed * sizeof(uint32);
+    }
+
     /*
      * NB: We allocate the vector of uint32 upvar cookies after all vectors of
      * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
      */
     if (nupvars != 0) {
         script->upvars()->length = nupvars;
         script->upvars()->vector = reinterpret_cast<UpvarCookie *>(cursor);
         vectorSize = nupvars * sizeof(script->upvars()->vector[0]);
@@ -989,17 +1068,17 @@ js_NewScript(JSContext *cx, uint32 lengt
     script->owner = cx->thread;
 #endif
 
     JS_APPEND_LINK(&script->links, &cx->compartment->scripts);
     return script;
 }
 
 JSScript *
-js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
+JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
 {
     uint32 mainLength, prologLength, nsrcnotes, nfixed;
     JSScript *script;
     const char *filename;
     JSFunction *fun;
 
     /* The counts of indexed things must be checked during code generation. */
     JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
@@ -1066,21 +1145,22 @@ js_NewScriptFromCG(JSContext *cx, JSCode
             JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
             JS_RUNTIME_METER(cx->runtime, totalEmptyScripts);
             return empty;
         }
     }
 
   skip_empty:
     CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
-    script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
-                          cg->atomList.count, cg->objectList.length,
-                          cg->upvarList.count, cg->regexpList.length,
-                          cg->ntrynotes, cg->constList.length(),
-                          cg->globalUses.length());
+    script = NewScript(cx, prologLength + mainLength, nsrcnotes,
+                       cg->atomList.count, cg->objectList.length,
+                       cg->upvarList.count, cg->regexpList.length,
+                       cg->ntrynotes, cg->constList.length(),
+                       cg->globalUses.length(), cg->closedArgs.length(),
+                       cg->closedVars.length());
     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->flags & TCF_IN_FUNCTION)
@@ -1120,33 +1200,42 @@ js_NewScriptFromCG(JSContext *cx, JSCode
     if (cg->flags & TCF_NO_SCRIPT_RVAL)
         script->noScriptRval = true;
     if (cg->hasSharps())
         script->hasSharps = true;
     if (cg->flags & TCF_STRICT_MODE_CODE)
         script->strictModeCode = true;
     if (cg->flags & TCF_COMPILE_N_GO)
         script->compileAndGo = true;
-    if (cg->flags & TCF_FUN_CALLS_EVAL)
+    if (cg->callsEval())
         script->usesEval = true;
+    if (cg->flags & TCF_FUN_USES_ARGUMENTS)
+        script->usesArguments = true;
 
     if (cg->upvarList.count != 0) {
         JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
         memcpy(script->upvars()->vector, cg->upvarMap.vector,
                cg->upvarList.count * sizeof(uint32));
         cg->upvarList.clear();
         cx->free(cg->upvarMap.vector);
         cg->upvarMap.vector = NULL;
     }
 
     if (cg->globalUses.length()) {
         memcpy(script->globals()->vector, &cg->globalUses[0],
                cg->globalUses.length() * sizeof(GlobalSlotArray::Entry));
     }
 
+    if (script->nClosedArgs)
+        memcpy(script->closedSlots, &cg->closedArgs[0], script->nClosedArgs * sizeof(uint32));
+    if (script->nClosedVars) {
+        memcpy(&script->closedSlots[script->nClosedArgs], &cg->closedVars[0],
+               script->nClosedVars * sizeof(uint32));
+    }
+
     /*
      * 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->flags & TCF_IN_FUNCTION) {
         fun = cg->fun;
         JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
@@ -1556,8 +1645,14 @@ bool
 JSScript::isValidJitCode(void *jcode)
 {
     return (char*)jcode >= (char*)jit->invoke &&
            (char*)jcode < (char*)jit->invoke +
            jit->inlineLength + jit->outOfLineLength;
 }
 #endif
 
+void
+JSScript::copyClosedSlotsTo(JSScript *other)
+{
+    memcpy(other->closedSlots, closedSlots, nClosedArgs + nClosedVars);
+}
+
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -184,16 +184,40 @@ namespace ic {
 # endif
 }
 struct CallSite;
 }
 }
 #endif
 
 struct JSScript {
+    /*
+     * Two successively less primitive ways to make a new JSScript.  The first
+     * does *not* call a non-null cx->runtime->newScriptHook -- only the second,
+     * NewScriptFromCG, calls this optional debugger hook.
+     *
+     * The NewScript function can't know whether the script it creates belongs
+     * to a function, or is top-level or eval code, but the debugger wants access
+     * to the newly made script's function, if any -- so callers of NewScript
+     * are responsible for notifying the debugger after successfully creating any
+     * kind (function or other) of new JSScript.
+     *
+     * NB: NewScript always creates a new script; it never returns the empty
+     * script singleton (JSScript::emptyScript()). Callers who know they can use
+     * that read-only singleton are responsible for choosing it instead of calling
+     * NewScript with length and nsrcnotes equal to 1 and other parameters save
+     * cx all zero.
+     */
+    static JSScript *NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
+                               uint32 nobjects, uint32 nupvars, uint32 nregexps,
+                               uint32 ntrynotes, uint32 nconsts, uint32 nglobals,
+                               uint32 nClosedArgs, uint32 nClosedVars);
+
+    static JSScript *NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
+
     /* FIXME: bug 586181 */
     JSCList         links;      /* Links for compartment script list */
     jsbytecode      *code;      /* bytecodes and their immediate operands */
     uint32          length;     /* length of code vector */
     uint16          version;    /* JS version under which script was compiled */
     uint16          nfixed;     /* number of slots besides stack operands in
                                    slot array */
     uint8           objectsOffset;  /* offset to the array of nested function,
@@ -211,29 +235,32 @@ struct JSScript {
                                        0 if none */
     bool            noScriptRval:1; /* no need for result value of last
                                        expression statement */
     bool            savedCallerFun:1; /* object 0 is caller function */
     bool            hasSharps:1;      /* script uses sharp variables */
     bool            strictModeCode:1; /* code is in strict mode */
     bool            compileAndGo:1;   /* script was compiled with TCF_COMPILE_N_GO */
     bool            usesEval:1;       /* script uses eval() */
+    bool            usesArguments:1;  /* script uses arguments */
     bool            warnedAboutTwoArgumentEval:1; /* have warned about use of
                                                      obsolete eval(s, o) in
                                                      this script */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
 #endif
 
     jsbytecode      *main;      /* main entry point, after predef'ing prolog */
     JSAtomMap       atomMap;    /* maps immediate index to literal struct */
     const char      *filename;  /* source filename or null */
     uint32          lineno;     /* base line number of script */
     uint16          nslots;     /* vars plus maximum stack depth */
     uint16          staticLevel;/* static level for display maintenance */
+    uint16          nClosedArgs; /* number of args which are closed over. */
+    uint16          nClosedVars; /* number of vars which are closed over. */
     JSPrincipals    *principals;/* principals for this script */
     union {
         /*
          * A script object of class js_ScriptClass, to ensure the script is GC'd.
          * - All scripts returned by JSAPI functions (JS_CompileScript,
          *   JS_CompileFile, etc.) have these objects.
          * - Function scripts never have script objects; such scripts are owned
          *   by their function objects.
@@ -248,16 +275,20 @@ struct JSScript {
          * script object is garbage collected.
          */
         JSObject    *object;
         JSScript    *nextToGC;  /* next to GC in rt->scriptsToGC list */
     } u;
 #ifdef CHECK_SCRIPT_OWNER
     JSThread        *owner;     /* for thread-safe life-cycle assertions */
 #endif
+
+    uint32          *closedSlots; /* vector of closed slots; args first, then vars. */
+
+  public:
 #ifdef JS_METHODJIT
     // Note: the other pointers in this group may be non-NULL only if 
     // |execPool| is non-NULL.
     void            *ncode;     /* native code compiled by the method JIT */
     void            **nmap;     /* maps PCs to native code */
     js::mjit::JITScript *jit;   /* Extra JIT info */
 # if defined JS_POLYIC
     js::mjit::ic::PICInfo *pics; /* PICs in this script */
@@ -368,16 +399,28 @@ struct JSScript {
      */
     void *pcToNative(jsbytecode *pc) {
         JS_ASSERT(nmap);
         JS_ASSERT(nmap[pc - code]);
         return nmap[pc - code];
     }
 #endif
 
+    uint32 getClosedArg(uint32 index) {
+        JS_ASSERT(index < nClosedArgs);
+        return closedSlots[index];
+    }
+
+    uint32 getClosedVar(uint32 index) {
+        JS_ASSERT(index < nClosedVars);
+        return closedSlots[nClosedArgs + index];
+    }
+
+    void copyClosedSlotsTo(JSScript *other);
+
   private:
     /*
      * Use const to put this in read-only memory if possible. We are stuck with
      * non-const JSScript * and jsbytecode * by legacy code (back in the 1990s,
      * const wasn't supported correctly on all target platforms). The debugger
      * does mutate bytecode, and script->u.object may be set after construction
      * in some cases, so making JSScript pointers const will be "hard".
      */
@@ -444,41 +487,16 @@ js_MarkScriptFilename(const char *filena
 
 extern void
 js_MarkScriptFilenames(JSRuntime *rt);
 
 extern void
 js_SweepScriptFilenames(JSRuntime *rt);
 
 /*
- * Two successively less primitive ways to make a new JSScript.  The first
- * does *not* call a non-null cx->runtime->newScriptHook -- only the second,
- * js_NewScriptFromCG, calls this optional debugger hook.
- *
- * The js_NewScript function can't know whether the script it creates belongs
- * to a function, or is top-level or eval code, but the debugger wants access
- * to the newly made script's function, if any -- so callers of js_NewScript
- * are responsible for notifying the debugger after successfully creating any
- * kind (function or other) of new JSScript.
- *
- * NB: js_NewScript always creates a new script; it never returns the empty
- * script singleton (JSScript::emptyScript()). Callers who know they can use
- * that read-only singleton are responsible for choosing it instead of calling
- * js_NewScript with length and nsrcnotes equal to 1 and other parameters save
- * cx all zero.
- */
-extern JSScript *
-js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
-             uint32 nobjects, uint32 nupvars, uint32 nregexps,
-             uint32 ntrynotes, uint32 nconsts, uint32 nglobals);
-
-extern JSScript *
-js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg);
-
-/*
  * New-script-hook calling is factored from js_NewScriptFromCG so that it
  * and callers of js_XDRScript can share this code.  In the case of callers
  * of js_XDRScript, the hook should be invoked only after successful decode
  * of any owning function (the fun parameter) or script object (null fun).
  */
 extern JS_FRIEND_API(void)
 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun);
 
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -15628,22 +15628,16 @@ TraceRecorder::record_JSOP_GETTHISPROP()
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETARGPROP()
 {
     return getProp(argval(GET_ARGNO(cx->regs->pc)));
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_DEFUPVAR()
-{
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETLOCALPROP()
 {
     return getProp(varval(GET_SLOTNO(cx->regs->pc)));
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_INDEXBASE1()
 {
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -200,17 +200,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
  * Bytecode version number. Increment the subtrahend whenever JS bytecode
  * changes incompatibly.
  *
  * This version number should be XDR'ed once near the front of any file or
  * larger storage unit containing XDR'ed bytecode and other data, and checked
  * before deserialization of bytecode.  If the saved version does not match
  * the current version, abort deserialization and invalidate the file.
  */
-#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 70)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 71)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -47,16 +47,17 @@
 #include "MonoIC.h"
 #include "PolyIC.h"
 #include "Retcon.h"
 #include "assembler/jit/ExecutableAllocator.h"
 #include "assembler/assembler/LinkBuffer.h"
 #include "FrameState-inl.h"
 #include "jsscriptinlines.h"
 #include "InlineFrameAssembler.h"
+#include "jsobjinlines.h"
 
 #include "jsautooplen.h"
 
 using namespace js;
 using namespace js::mjit;
 #if defined JS_POLYIC
 using namespace js::mjit::ic;
 #endif
@@ -79,17 +80,16 @@ mjit::Compiler::Compiler(JSContext *cx, 
     mics(ContextAllocPolicy(cx)),
     callICs(ContextAllocPolicy(cx)),
 #endif
 #if defined JS_POLYIC
     pics(ContextAllocPolicy(cx)), 
 #endif
     callSites(ContextAllocPolicy(cx)), 
     doubleList(ContextAllocPolicy(cx)),
-    escapingList(ContextAllocPolicy(cx)),
     stubcc(cx, *this, frame, script)
 #if defined JS_TRACER
     ,addTraceHints(cx->jitEnabled)
 #endif
 {
 }
 
 #define CHECK_STATUS(expr)              \
@@ -134,16 +134,19 @@ mjit::Compiler::Compile()
 
     /* Initialize PC early so stub calls in the prologue can be fallible. */
     PC = script->code;
 
 #ifdef JS_METHODJIT
     script->debugMode = cx->compartment->debugMode;
 #endif
 
+    for (uint32 i = 0; i < script->nClosedVars; i++)
+        frame.setClosedVar(script->getClosedVar(i));
+
     CHECK_STATUS(generatePrologue());
     CHECK_STATUS(generateMethod());
     CHECK_STATUS(generateEpilogue());
     CHECK_STATUS(finishThisUp());
 
 #ifdef JS_METHODJIT_SPEW
     prof.stop();
     JaegerSpew(JSpew_Prof, "compilation took %d us\n", prof.time_us());
@@ -324,17 +327,16 @@ mjit::Compiler::finishThisUp()
     JSC::ExecutableAllocator::makeWritable(result, totalSize);
     masm.executableCopy(result);
     stubcc.masm.executableCopy(result + masm.size());
 
     JSC::LinkBuffer fullCode(result, totalSize);
     JSC::LinkBuffer stubCode(result + masm.size(), stubcc.size());
 
     size_t totalBytes = sizeof(JITScript) +
-                        sizeof(uint32) * escapingList.length() +
                         sizeof(void *) * script->length +
 #if defined JS_MONOIC
                         sizeof(ic::MICInfo) * mics.length() +
                         sizeof(ic::CallICInfo) * callICs.length() +
 #endif
 #if defined JS_POLYIC
                         sizeof(ic::PICInfo) * pics.length() +
 #endif
@@ -350,26 +352,16 @@ mjit::Compiler::finishThisUp()
     cursor += sizeof(JITScript);
 
     script->jit->execPool = execPool;
     script->jit->inlineLength = masm.size();
     script->jit->outOfLineLength = stubcc.size();
     script->jit->nCallSites = callSites.length();
     script->jit->invoke = result;
 
-    script->jit->nescaping = escapingList.length();
-    if (escapingList.length()) {
-        script->jit->escaping = (uint32 *)cursor;
-        cursor += sizeof(uint32) * escapingList.length();
-        for (uint32 i = 0; i < escapingList.length(); i++)
-            script->jit->escaping[i] = escapingList[i];
-    } else {
-        script->jit->escaping = NULL;
-    }
-
     /* Build the pc -> ncode mapping. */
     void **nmap = (void **)cursor;
     script->nmap = nmap;
     cursor += sizeof(void *) * script->length;
 
     for (size_t i = 0; i < script->length; i++) {
         Label L = jumpMap[i];
         if (analysis[i].safePoint) {
@@ -1481,41 +1473,21 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_STOP)
             /* Safe point! */
             emitReturn();
             goto done;
           END_CASE(JSOP_STOP)
 
           BEGIN_CASE(JSOP_ENTERBLOCK)
-          {
-            // If this is an exception entry point, then jsl_InternalThrow has set
-            // VMFrame::fp to the correct fp for the entry point. We need to copy
-            // that value here to FpReg so that FpReg also has the correct sp.
-            // Otherwise, we would simply be using a stale FpReg value.
-            if (analysis[PC].exceptionEntry)
-                restoreFrameRegs(masm);
-
-            /* For now, don't bother doing anything for this opcode. */
-            JSObject *obj = script->getObject(fullAtomIndex(PC));
-            frame.forgetEverything();
-            masm.move(ImmPtr(obj), Registers::ArgReg1);
-            uint32 n = js_GetEnterBlockStackDefs(cx, script, PC);
-            stubCall(stubs::EnterBlock);
-            frame.enterBlock(n);
-          }
-          END_CASE(JSOP_ENTERBLOCK)
+            enterBlock(script->getObject(fullAtomIndex(PC)));
+          END_CASE(JSOP_ENTERBLOCK);
 
           BEGIN_CASE(JSOP_LEAVEBLOCK)
-          {
-            uint32 n = js_GetVariableStackUses(op, PC);
-            prepareStubCall(Uses(n));
-            stubCall(stubs::LeaveBlock);
-            frame.leaveBlock(n);
-          }
+            leaveBlock();
           END_CASE(JSOP_LEAVEBLOCK)
 
           BEGIN_CASE(JSOP_CALLLOCAL)
             frame.pushLocal(GET_SLOTNO(PC));
             frame.push(NullValue());
           END_CASE(JSOP_CALLLOCAL)
 
           BEGIN_CASE(JSOP_INT8)
@@ -1597,26 +1569,16 @@ mjit::Compiler::generateMethod()
           BEGIN_CASE(JSOP_DECGLOBAL)
           BEGIN_CASE(JSOP_GLOBALINC)
           BEGIN_CASE(JSOP_GLOBALDEC)
             /* Advances PC automatically. */
             jsop_globalinc(op, GET_SLOTNO(PC));
             break;
           END_CASE(JSOP_GLOBALINC)
 
-          BEGIN_CASE(JSOP_DEFUPVAR)
-          {
-            uint32 slot = GET_SLOTNO(PC);
-            if (frame.addEscaping(slot) && slot < script->nfixed) {
-                if (!escapingList.append(slot))
-                    return Compile_Error;
-            }
-          }
-          END_CASE(JSOP_DEFUPVAR)
-
           default:
            /* Sorry, this opcode isn't implemented yet. */
 #ifdef JS_METHODJIT_SPEW
             JaegerSpew(JSpew_Abort, "opcode %s not handled yet (%s line %d)\n", OpcodeNames[op],
                        script->filename, js_PCToLineNumber(cx, script, PC));
 #endif
             return Compile_Abort;
         }
@@ -4116,8 +4078,51 @@ mjit::Compiler::jumpAndTrace(Jump j, jsb
     stubcc.jumpInScript(stubcc.masm.jump(), target);
 
 # if JS_MONOIC
     mics.append(mic);
 # endif
 #endif
 }
 
+void
+mjit::Compiler::enterBlock(JSObject *obj)
+{
+    // If this is an exception entry point, then jsl_InternalThrow has set
+    // VMFrame::fp to the correct fp for the entry point. We need to copy
+    // that value here to FpReg so that FpReg also has the correct sp.
+    // Otherwise, we would simply be using a stale FpReg value.
+    if (analysis[PC].exceptionEntry)
+        restoreFrameRegs(masm);
+
+    uint32 oldFrameDepth = frame.frameDepth();
+
+    /* For now, don't bother doing anything for this opcode. */
+    frame.forgetEverything();
+    masm.move(ImmPtr(obj), Registers::ArgReg1);
+    uint32 n = js_GetEnterBlockStackDefs(cx, script, PC);
+    stubCall(stubs::EnterBlock);
+    frame.enterBlock(n);
+
+    uint32 slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
+    uintN count = OBJ_BLOCK_COUNT(cx, obj);
+    uintN flen = JS_MIN(count, JS_INITIAL_NSLOTS - slot);
+    uintN stop = slot + flen;
+    for (uint32 i = 0; slot < stop; slot++, i++) {
+        const Value &v = obj->getSlotRef(slot);
+        if (v.isBoolean() && v.toBoolean())
+            frame.setClosedVar(oldFrameDepth + i);
+    }
+}
+
+void
+mjit::Compiler::leaveBlock()
+{
+    /*
+     * Note: After bug 535912, we can pass the block obj directly, inline
+     * PutBlockObject, and do away with the muckiness in PutBlockObject.
+     */
+    uint32 n = js_GetVariableStackUses(JSOP_LEAVEBLOCK, PC);
+    prepareStubCall(Uses(n));
+    stubCall(stubs::LeaveBlock);
+    frame.leaveBlock(n);
+}
+
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -210,17 +210,16 @@ class Compiler
     js::Vector<MICGenInfo, 64> mics;
     js::Vector<CallGenInfo, 64> callICs;
 #endif
 #if defined JS_POLYIC
     js::Vector<PICGenInfo, 64> pics;
 #endif
     js::Vector<InternalCallSite, 64> callSites;
     js::Vector<DoublePatch, 16> doubleList;
-    js::Vector<uint32, 16> escapingList;
     StubCompiler stubcc;
     Label invokeLabel;
     Label arityLabel;
     bool addTraceHints;
 
   public:
     // Special atom index used to indicate that the atom is 'length'. This
     // follows interpreter usage in JSOP_LENGTH.
@@ -293,16 +292,18 @@ class Compiler
     void jsop_setprop_slow(JSAtom *atom);
     bool jsop_callprop_slow(JSAtom *atom);
     bool jsop_callprop(JSAtom *atom);
     bool jsop_callprop_obj(JSAtom *atom);
     bool jsop_callprop_str(JSAtom *atom);
     bool jsop_callprop_generic(JSAtom *atom);
     void jsop_instanceof();
     void jsop_name(JSAtom *atom);
+    void enterBlock(JSObject *obj);
+    void leaveBlock();
 
     /* Fast arithmetic. */
     void jsop_binary(JSOp op, VoidStub stub);
     void jsop_binary_full(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub);
     void jsop_binary_full_simple(FrameEntry *fe, JSOp op, VoidStub stub);
     void jsop_binary_double(FrameEntry *lhs, FrameEntry *rhs, JSOp op, VoidStub stub);
     void slowLoadConstantDouble(Assembler &masm, FrameEntry *fe,
                                 FPRegisterID fpreg);
--- a/js/src/methodjit/FrameState-inl.h
+++ b/js/src/methodjit/FrameState-inl.h
@@ -697,17 +697,17 @@ FrameState::dupAt(int32 n)
     JS_ASSERT(n < 0);
     FrameEntry *fe = peek(n);
     pushCopyOf(indexOfFe(fe));
 }
 
 inline void
 FrameState::pushLocal(uint32 n)
 {
-    if (!eval && !escaping[n]) {
+    if (!eval && !isClosedVar(n)) {
         pushCopyOf(indexOfFe(getLocal(n)));
     } else {
 #ifdef DEBUG
         /*
          * We really want to assert on local variables, but in the presence of
          * SETLOCAL equivocation of stack slots, and let expressions, just
          * weakly assert on the fixed local vars.
          */
@@ -729,38 +729,36 @@ FrameState::leaveBlock(uint32 n)
 
 inline void
 FrameState::enterBlock(uint32 n)
 {
     /* expect that tracker has 0 entries, for now. */
     JS_ASSERT(!tracker.nentries);
     JS_ASSERT(uint32(sp + n - locals) <= script->nslots);
 
+    if (!eval)
+        memset(&closedVars[uint32(sp - locals)], 0, n * sizeof(*closedVars));
     sp += n;
 }
 
 inline void
 FrameState::eviscerate(FrameEntry *fe)
 {
     forgetAllRegs(fe);
     fe->type.invalidate();
     fe->data.invalidate();
     fe->setNotCopied();
     fe->setCopyOf(NULL);
 }
 
-inline bool
-FrameState::addEscaping(uint32 local)
+inline void
+FrameState::setClosedVar(uint32 slot)
 {
-    if (!eval) {
-        uint32 already = escaping[local];
-        escaping[local] = 1;
-        return !already;
-    }
-    return false;
+    if (!eval)
+        closedVars[slot] = true;
 }
 
 inline StateRemat
 FrameState::dataRematInfo(const FrameEntry *fe) const
 {
     if (fe->isCopy())
         fe = fe->copyOf();
     StateRemat remat;
@@ -850,13 +848,19 @@ FrameState::loadDouble(FrameEntry *fe, F
         }
         if (!fe->type.synced())
             syncType(fe, address, masm);
     } while (0);
 
     masm.loadDouble(address, fpReg);
 }
 
+inline bool
+FrameState::isClosedVar(uint32 slot)
+{
+    return closedVars[slot];
+}
+
 } /* namspace mjit */
 } /* namspace js */
 
 #endif /* include */
 
--- a/js/src/methodjit/FrameState.cpp
+++ b/js/src/methodjit/FrameState.cpp
@@ -70,17 +70,17 @@ FrameState::init(uint32 nargs)
 
     uint32 nlocals = script->nslots;
     if ((eval = script->usesEval || cx->compartment->debugMode))
         nlocals = 0;
 
     uint8 *cursor = (uint8 *)cx->malloc(sizeof(FrameEntry) * nslots +       // entries[]
                                         sizeof(FrameEntry *) * nslots +     // base[]
                                         sizeof(FrameEntry *) * nslots +     // tracker.entries[]
-                                        sizeof(uint32) * nlocals            // escaping[]
+                                        sizeof(bool) * nslots               // closedVars[]
                                         );
     if (!cursor)
         return false;
 
     if (!reifier.init(nslots))
         return false;
 
     entries = (FrameEntry *)cursor;
@@ -93,18 +93,18 @@ FrameState::init(uint32 nargs)
     sp = spBase;
     memset(base, 0, sizeof(FrameEntry *) * nslots);
     cursor += sizeof(FrameEntry *) * nslots;
 
     tracker.entries = (FrameEntry **)cursor;
     cursor += sizeof(FrameEntry *) * nslots;
 
     if (nlocals) {
-        escaping = (uint32 *)cursor;
-        memset(escaping, 0, sizeof(uint32) * nlocals);
+        closedVars = (bool *)cursor;
+        memset(closedVars, 0, sizeof(*closedVars) * nslots);
     }
 
     return true;
 }
 
 void
 FrameState::takeReg(RegisterID reg)
 {
@@ -855,17 +855,17 @@ FrameState::uncopy(FrameEntry *original)
 
     return fe;
 }
 
 void
 FrameState::storeLocal(uint32 n, bool popGuaranteed, bool typeChange)
 {
     FrameEntry *localFe = getLocal(n);
-    bool cacheable = !eval && !escaping[n];
+    bool cacheable = !eval && !isClosedVar(n);
 
     if (!popGuaranteed && !cacheable) {
         JS_ASSERT_IF(base[localIndex(n)] && (!eval || n < script->nfixed),
                      entries[localIndex(n)].type.inMemory() &&
                      entries[localIndex(n)].data.inMemory());
         Address local(JSFrameReg, sizeof(JSStackFrame) + n * sizeof(Value));
         storeTo(peek(-1), local, false);
         forgetAllRegs(getLocal(n));
--- a/js/src/methodjit/FrameState.h
+++ b/js/src/methodjit/FrameState.h
@@ -667,20 +667,19 @@ class FrameState
     /*
      * Stores the top item on the stack to a stack slot, count down from the
      * current stack depth. For example, to move the top (-1) to -3, you would
      * call shift(-2).
      */
     void shift(int32 n);
 
     /*
-     * Notifies the frame of a slot that can escape. Returns whether or not
-     * the slot was added.
+     * Notifies the frame of a slot that can escape.
      */
-    inline bool addEscaping(uint32 local);
+    inline void setClosedVar(uint32 slot);
 
     inline void setInTryBlock(bool inTryBlock) {
         this->inTryBlock = inTryBlock;
     }
 
   private:
     inline RegisterID allocReg(FrameEntry *fe, RematInfo::RematType type);
     inline void forgetReg(RegisterID reg);
@@ -725,16 +724,18 @@ class FrameState
     uint32 indexOf(int32 depth) {
         return uint32((sp + depth) - base);
     }
 
     uint32 indexOfFe(FrameEntry *fe) {
         return uint32(fe - entries);
     }
 
+    inline bool isClosedVar(uint32 slot);
+
   private:
     JSContext *cx;
     JSScript *script;
     uint32 nargs;
     Assembler &masm;
 
     /* All allocated registers. */
     Registers freeRegs;
@@ -763,17 +764,17 @@ class FrameState
     /*
      * Register ownership state. This can't be used alone; to find whether an
      * entry is active, you must check the allocated registers.
      */
     RegisterState regstate[Assembler::TotalRegisters];
 
     mutable ImmutableSync reifier;
 
-    uint32 *escaping;
+    bool *closedVars;
     bool eval;
     bool inTryBlock;
 };
 
 } /* namespace mjit */
 } /* namespace js */
 
 #endif /* jsjaeger_framestate_h__ */
--- a/js/src/methodjit/MethodJIT.h
+++ b/js/src/methodjit/MethodJIT.h
@@ -173,18 +173,16 @@ struct JITScript {
     uint32          nMICs;           /* number of MonoICs */
     uint32          nCallICs;        /* number of call ICs */
 #endif
 #ifdef JS_POLYIC
     uint32          nPICs;           /* number of PolyICs */
 #endif
     void            *invoke;         /* invoke address */
     void            *arityCheck;     /* arity check address */
-    uint32          *escaping;       /* list of escaping slots */
-    uint32          nescaping;       /* number of escaping slots */
 };
 
 /* Execute a method that has been JIT compiled. */
 JSBool JaegerShot(JSContext *cx);
 
 /* Drop into the middle of a method at an arbitrary point, and execute. */
 JSBool JaegerShotAtSafePoint(JSContext *cx, void *safePoint);
 
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/jaeger/bug592973.js
@@ -0,0 +1,12 @@
+// vim: set ts=4 sw=4 tw=99 et:
+function f(x) {
+    if (x) {
+        let y;
+        y = 12;
+        (function () {
+          assertEq(y, 12);
+        })();
+    }
+}
+f(1);
+