Release branch landing of all changesets between 3.5b99build1 and the desired 3.5b99build2. Specifically: 692408b49524, 2e78581523b2, db8cd9984bcc, b43debb766e0, 6224a80ebf99, b94f6e65e1e8, f473446affc1, f706df925e9f, abd967e2173b, d047305a9004, bb49f28e12ad, a4629227dcc0, 8b234cf1270c, 3f1e6876d253 GECKO191b99_20090604_RELBRANCH FIREFOX_3_5b99_BUILD2 FIREFOX_3_5b99_RELEASE
authorBen Hearsum <bhearsum@mozilla.com>
Fri, 05 Jun 2009 16:13:14 -0400
branchGECKO191b99_20090604_RELBRANCH
changeset 25899 3f775c3bb24a8b6cce6dd0a50aefc4d21f27a35f
parent 25884 3a76fd7f3f65412c6889035db982bc4319a4ef66 (current diff)
parent 25898 3f1e6876d253133fab846bfac793310e1986330c (diff)
child 25901 35432788a56e465a1581099e71af0d675469bb5b
push id1658
push userbhearsum@mozilla.com
push dateFri, 05 Jun 2009 20:20:06 +0000
milestone1.9.1b99
Release branch landing of all changesets between 3.5b99build1 and the desired 3.5b99build2. Specifically: 692408b49524, 2e78581523b2, db8cd9984bcc, b43debb766e0, 6224a80ebf99, b94f6e65e1e8, f473446affc1, f706df925e9f, abd967e2173b, d047305a9004, bb49f28e12ad, a4629227dcc0, 8b234cf1270c, 3f1e6876d253
browser/base/Makefile.in
browser/config/version.txt
browser/installer/windows/Makefile.in
config/milestone.txt
js/src/config/milestone.txt
--- a/js/src/imacros.c.out
+++ b/js/src/imacros.c.out
@@ -944,16 +944,21 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
     0,  /* JSOP_LENGTH */
     0,  /* JSOP_NEWARRAY */
     0,  /* JSOP_HOLE */
     0,  /* JSOP_DEFFUN_FC */
     0,  /* JSOP_DEFLOCALFUN_FC */
     0,  /* JSOP_LAMBDA_FC */
     0,  /* JSOP_OBJTOP */
     0,  /* JSOP_LOOP */
+    0,  /* JSOP_GETUPVAR_DBG */
+    0,  /* JSOP_CALLUPVAR_DBG */
+    0,  /* JSOP_DEFFUN_DBGFC */
+    0,  /* JSOP_DEFLOCALFUN_DBGFC */
+    0,  /* JSOP_LAMBDA_DBGFC */
 };
 #define JSOP_IS_IMACOP(x) (0 \
  || x == JSOP_BITOR \
  || x == JSOP_BITXOR \
  || x == JSOP_BITAND \
  || x == JSOP_EQ \
  || x == JSOP_NE \
  || x == JSOP_LT \
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -187,17 +187,17 @@ MSG_DEF(JSMSG_PAREN_AFTER_ARGS,       10
 MSG_DEF(JSMSG_BRACKET_AFTER_LIST,     105, 0, JSEXN_SYNTAXERR, "missing ] after element list")
 MSG_DEF(JSMSG_COLON_AFTER_ID,         106, 0, JSEXN_SYNTAXERR, "missing : after property id")
 MSG_DEF(JSMSG_CURLY_AFTER_LIST,       107, 0, JSEXN_SYNTAXERR, "missing } after property list")
 MSG_DEF(JSMSG_PAREN_IN_PAREN,         108, 0, JSEXN_SYNTAXERR, "missing ) in parenthetical")
 MSG_DEF(JSMSG_SEMI_BEFORE_STMNT,      109, 0, JSEXN_SYNTAXERR, "missing ; before statement")
 MSG_DEF(JSMSG_NO_RETURN_VALUE,        110, 1, JSEXN_TYPEERR, "function {0} does not always return a value")
 MSG_DEF(JSMSG_DUPLICATE_FORMAL,       111, 1, JSEXN_TYPEERR, "duplicate formal argument {0}")
 MSG_DEF(JSMSG_EQUAL_AS_ASSIGN,        112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}")
-MSG_DEF(JSMSG_UNUSED113,              113, 0, JSEXN_NONE, "unused113")
+MSG_DEF(JSMSG_OPTIMIZED_CLOSURE_LEAK, 113, 0, JSEXN_INTERNALERR, "cannot access optimized closure")
 MSG_DEF(JSMSG_TOO_MANY_DEFAULTS,      114, 0, JSEXN_SYNTAXERR, "more than one switch default")
 MSG_DEF(JSMSG_TOO_MANY_CASES,         115, 0, JSEXN_INTERNALERR, "too many switch cases")
 MSG_DEF(JSMSG_BAD_SWITCH,             116, 0, JSEXN_SYNTAXERR, "invalid switch statement")
 MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,       117, 0, JSEXN_SYNTAXERR, "invalid for/in left-hand side")
 MSG_DEF(JSMSG_CATCH_AFTER_GENERAL,    118, 0, JSEXN_SYNTAXERR, "catch after unconditional catch")
 MSG_DEF(JSMSG_CATCH_WITHOUT_TRY,      119, 0, JSEXN_SYNTAXERR, "catch without try")
 MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,    120, 0, JSEXN_SYNTAXERR, "finally without try")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,        121, 0, JSEXN_SYNTAXERR, "label not found")
@@ -304,11 +304,9 @@ MSG_DEF(JSMSG_BAD_INCOP_OPERAND,      22
 MSG_DEF(JSMSG_NULL_OR_UNDEFINED,      222, 2, JSEXN_TYPEERR, "{0} is {1}")
 MSG_DEF(JSMSG_LET_DECL_NOT_IN_BLOCK,  223, 0, JSEXN_SYNTAXERR, "let declaration not directly within block")
 MSG_DEF(JSMSG_BAD_OBJECT_INIT,        224, 0, JSEXN_SYNTAXERR, "invalid object initializer")
 MSG_DEF(JSMSG_CANT_SET_ARRAY_ATTRS,   225, 0, JSEXN_INTERNALERR, "can't set attributes on indexed array properties")
 MSG_DEF(JSMSG_EVAL_ARITY,             226, 0, JSEXN_TYPEERR, "eval accepts only one parameter")
 MSG_DEF(JSMSG_MISSING_FUN_ARG,        227, 2, JSEXN_TYPEERR, "missing argument {0} when calling function {1}")
 MSG_DEF(JSMSG_JSON_BAD_PARSE,         228, 0, JSEXN_SYNTAXERR, "JSON.parse")
 MSG_DEF(JSMSG_JSON_BAD_STRINGIFY,     229, 0, JSEXN_ERR, "JSON.stringify")
-
-
-
+MSG_DEF(JSMSG_XDR_CLOSURE_WRAPPER,    230, 1, JSEXN_INTERNALERR, "can't XDR closure wrapper for function {0}")
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -641,21 +641,17 @@ JS_TypeOfValue(JSContext *cx, jsval v)
     JSObjectOps *ops;
     JSClass *clasp;
 
     CHECK_REQUEST(cx);
     if (JSVAL_IS_OBJECT(v)) {
         type = JSTYPE_OBJECT;           /* XXXbe JSTYPE_NULL for JS2 */
         obj = JSVAL_TO_OBJECT(v);
         if (obj) {
-            JSObject *wrapped;
-
-            wrapped = js_GetWrappedObject(cx, obj);
-            if (wrapped)
-                obj = wrapped;
+            obj = js_GetWrappedObject(cx, obj);
 
             ops = obj->map->ops;
 #if JS_HAS_XML_SUPPORT
             if (ops == &js_XMLObjectOps) {
                 type = JSTYPE_XML;
             } else
 #endif
             {
@@ -1351,21 +1347,21 @@ JS_InitStandardClasses(JSContext *cx, JS
 #endif
 #if JS_HAS_GENERATORS
            js_InitIteratorClasses(cx, obj) &&
 #endif
            js_InitDateClass(cx, obj);
 }
 
 #define CLASP(name)                 (&js_##name##Class)
-#define EXT_CLASP(name)             (&js_##name##Class.base)
+#define XCLASP(name)                (&js_##name##Class.base)
 #define EAGER_ATOM(name)            ATOM_OFFSET(name), NULL
 #define EAGER_CLASS_ATOM(name)      CLASS_ATOM_OFFSET(name), NULL
 #define EAGER_ATOM_AND_CLASP(name)  EAGER_CLASS_ATOM(name), CLASP(name)
-#define EAGER_ATOM_AND_EXT_CLASP(name) EAGER_CLASS_ATOM(name), EXT_CLASP(name)
+#define EAGER_ATOM_AND_XCLASP(name) EAGER_CLASS_ATOM(name), XCLASP(name)
 #define LAZY_ATOM(name)             ATOM_OFFSET(lazy.name), js_##name##_str
 
 typedef struct JSStdName {
     JSObjectOp  init;
     size_t      atomOffset;     /* offset of atom pointer in JSAtomState */
     const char  *name;          /* null if atom is pre-pinned, else name */
     JSClass     *clasp;
 } JSStdName;
@@ -1404,18 +1400,18 @@ static JSStdName standard_class_atoms[] 
     {js_InitStringClass,                EAGER_ATOM_AND_CLASP(String)},
     {js_InitExceptionClasses,           EAGER_ATOM_AND_CLASP(Error)},
     {js_InitRegExpClass,                EAGER_ATOM_AND_CLASP(RegExp)},
 #if JS_HAS_SCRIPT_OBJECT
     {js_InitScriptClass,                EAGER_ATOM_AND_CLASP(Script)},
 #endif
 #if JS_HAS_XML_SUPPORT
     {js_InitXMLClass,                   EAGER_ATOM_AND_CLASP(XML)},
-    {js_InitNamespaceClass,             EAGER_ATOM_AND_EXT_CLASP(Namespace)},
-    {js_InitQNameClass,                 EAGER_ATOM_AND_EXT_CLASP(QName)},
+    {js_InitNamespaceClass,             EAGER_ATOM_AND_XCLASP(Namespace)},
+    {js_InitQNameClass,                 EAGER_ATOM_AND_XCLASP(QName)},
 #endif
 #if JS_HAS_FILE_OBJECT
     {js_InitFileClass,                  EAGER_ATOM_AND_CLASP(File)},
 #endif
 #if JS_HAS_GENERATORS
     {js_InitIteratorClasses,            EAGER_ATOM_AND_CLASP(StopIteration)},
 #endif
     {js_InitJSONClass,                  EAGER_ATOM_AND_CLASP(JSON)},
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -679,17 +679,16 @@ JS_REQUIRES_STACK JSBool
 js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
                      jsval *rval)
 {
     JSObject *funobj;
     JSFunction *wrapper;
     jsval userid;
 
     funobj = JSVAL_TO_OBJECT(argv[-2]);
-    JS_ASSERT(OBJ_GET_CLASS(cx, funobj) == &js_FunctionClass);
     wrapper = GET_FUNCTION_PRIVATE(cx, funobj);
     userid = ATOM_KEY(wrapper->atom);
     *rval = argv[0];
     return js_watch_set(cx, obj, userid, rval);
 }
 
 JSPropertyOp
 js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
@@ -1151,17 +1150,17 @@ JS_GetFrameFunction(JSContext *cx, JSSta
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetFrameFunctionObject(JSContext *cx, JSStackFrame *fp)
 {
     if (!fp->fun)
         return NULL;
 
-    JS_ASSERT(OBJ_GET_CLASS(cx, fp->callee) == &js_FunctionClass);
+    JS_ASSERT(HAS_FUNCTION_CLASS(fp->callee));
     JS_ASSERT(OBJ_GET_PRIVATE(cx, fp->callee) == fp->fun);
     return fp->callee;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsConstructorFrame(JSContext *cx, JSStackFrame *fp)
 {
     return (fp->flags & JSFRAME_CONSTRUCTING) != 0;
@@ -1256,21 +1255,38 @@ JS_EvaluateUCInStackFrame(JSContext *cx,
      * we use a static level that will cause us not to attempt to optimize
      * variable references made by this frame.
      */
     script = JSCompiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
                                        TCF_COMPILE_N_GO |
                                        TCF_PUT_STATIC_LEVEL(JS_DISPLAY_SIZE),
                                        chars, length, NULL,
                                        filename, lineno);
+
     if (!script)
         return JS_FALSE;
 
+    JSStackFrame *displayCopy[JS_DISPLAY_SIZE];
+    if (cx->fp != fp) {
+        memcpy(displayCopy, cx->display, sizeof displayCopy);
+
+        /* This API requires an active fp on cx, so fp2 can't go null here. */
+        for (JSStackFrame *fp2 = cx->fp; fp2 != fp; fp2 = fp2->down) {
+            if (fp2->displaySave) {
+                JS_ASSERT(fp2->script->staticLevel < JS_DISPLAY_SIZE);
+                cx->display[fp2->script->staticLevel] = fp2->displaySave;
+            }
+        }
+    }
+
     ok = js_Execute(cx, scobj, script, fp, JSFRAME_DEBUGGER | JSFRAME_EVAL,
                     rval);
+
+    if (cx->fp != fp)
+        memcpy(cx->display, displayCopy, sizeof cx->display);
     js_DestroyScript(cx, script);
     return ok;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_EvaluateInStackFrame(JSContext *cx, JSStackFrame *fp,
                         const char *bytes, uintN length,
                         const char *filename, uintN lineno,
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -2122,16 +2122,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
         return MakeUpvarForEval(pn, cg);
     }
 
     uintN skip = cg->staticLevel - level;
     if (skip != 0) {
         JS_ASSERT(cg->flags & TCF_IN_FUNCTION);
         JS_ASSERT(cg->lexdeps.lookup(atom));
         JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
+        JS_ASSERT(cg->fun->u.i.skipmin <= skip);
 
         /*
          * If op is a mutating opcode, this upvar's static level is too big to
          * index into the display, or the function is heavyweight, we fall back
          * on JSOP_*NAME*.
          */
         if (op != JSOP_NAME)
             return JS_TRUE;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -240,17 +240,29 @@ JSObject *
 js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject *argsobj, *global, *parent;
 
     /*
      * We must be in a function activation; the function must be lightweight
      * or else fp must have a variable object.
      */
-    JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
+    JSFunction *fun = fp->fun;
+    JS_ASSERT(fun && (!(fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
+
+    /*
+     * Unlike FUN_ESCAPE_HAZARD(fun), we test here only for null closures, not
+     * flat closures -- flat ones are inherently escaping, so arguments.callee
+     * references are fine.
+     */
+    if (FUN_NULL_CLOSURE(fun) && fun->u.i.skipmin != 0) {
+        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                             JSMSG_OPTIMIZED_CLOSURE_LEAK);
+        return JS_FALSE;
+    }
 
     /* Skip eval and debugger frames. */
     while (fp->flags & JSFRAME_SPECIAL)
         fp = fp->down;
 
     /* Create an arguments object for fp only if it lacks one. */
     argsobj = fp->argsobj;
     if (argsobj)
@@ -360,30 +372,206 @@ args_delProperty(JSContext *cx, JSObject
       default:
         if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot))
             return JS_FALSE;
         break;
     }
     return JS_TRUE;
 }
 
+static JS_REQUIRES_STACK JSObject *
+WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunction *fun)
+{
+    JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
+    JS_ASSERT(FUN_OPTIMIZED_CLOSURE(fun));
+    JS_ASSERT(!fun->u.i.wrapper);
+
+    /*
+     * We do not attempt to reify Call and Block objects on demand for outer
+     * scopes. This could be done (see the "v8" patch in bug 494235) but it is
+     * fragile in the face of ongoing compile-time optimization. Instead, the
+     * _DBG* opcodes used by wrappers created here must cope with unresolved
+     * upvars and throw them as reference errors. Caveat debuggers!
+     */
+    JSObject *scopeChain = js_GetScopeChain(cx, fp);
+    if (!scopeChain)
+        return NULL;
+
+    JSObject *wfunobj = js_NewObjectWithGivenProto(cx, &js_FunctionClass,
+                                                   funobj, scopeChain, 0);
+    if (!wfunobj)
+        return NULL;
+    JSAutoTempValueRooter tvr(cx, wfunobj);
+
+    JSFunction *wfun = (JSFunction *) wfunobj;
+    wfunobj->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(wfun);
+    wfun->nargs = 0;
+    wfun->flags = fun->flags | JSFUN_HEAVYWEIGHT;
+    wfun->u.i.nvars = 0;
+    wfun->u.i.nupvars = 0;
+    wfun->u.i.skipmin = fun->u.i.skipmin;
+    wfun->u.i.wrapper = true;
+    wfun->u.i.script = NULL;
+    wfun->u.i.names.taggedAtom = NULL;
+    wfun->atom = fun->atom;
+
+    if (fun->hasLocalNames()) {
+        void *mark = JS_ARENA_MARK(&cx->tempPool);
+        jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
+        if (!names)
+            return NULL;
+
+        JSBool ok = true;
+        for (uintN i = 0, n = fun->countLocalNames(); i != n; i++) {
+            jsuword name = names[i];
+            JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(name);
+            JSLocalKind localKind = (i < fun->nargs)
+                                    ? JSLOCAL_ARG
+                                    : (i < fun->countArgsAndVars())
+                                    ? (JS_LOCAL_NAME_IS_CONST(name)
+                                       ? JSLOCAL_CONST
+                                       : JSLOCAL_VAR)
+                                    : JSLOCAL_UPVAR;
+
+            ok = js_AddLocal(cx, wfun, atom, localKind);
+            if (!ok)
+                break;
+        }
+
+        JS_ARENA_RELEASE(&cx->tempPool, mark);
+        if (!ok)
+            return NULL;
+        JS_ASSERT(wfun->nargs == fun->nargs);
+        JS_ASSERT(wfun->u.i.nvars == fun->u.i.nvars);
+        JS_ASSERT(wfun->u.i.nupvars == fun->u.i.nupvars);
+        js_FreezeLocalNames(cx, wfun);
+    }
+
+    JSScript *script = fun->u.i.script;
+    jssrcnote *snbase = SCRIPT_NOTES(script);
+    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)
+                                     ? JS_SCRIPT_OBJECTS(script)->length
+                                     : 0,
+                                     fun->u.i.nupvars,
+                                     (script->regexpsOffset != 0)
+                                     ? JS_SCRIPT_REGEXPS(script)->length
+                                     : 0,
+                                     (script->trynotesOffset != 0)
+                                     ? JS_SCRIPT_TRYNOTES(script)->length
+                                     : 0);
+    if (!wscript)
+        return NULL;
+
+    memcpy(wscript->code, script->code, script->length);
+    wscript->main = wscript->code + (script->main - script->code);
+
+    memcpy(SCRIPT_NOTES(wscript), snbase, nsrcnotes);
+    memcpy(wscript->atomMap.vector, script->atomMap.vector,
+           wscript->atomMap.length * sizeof(JSAtom *));
+    if (script->objectsOffset != 0) {
+        memcpy(JS_SCRIPT_OBJECTS(wscript)->vector, JS_SCRIPT_OBJECTS(script)->vector,
+               JS_SCRIPT_OBJECTS(wscript)->length * sizeof(JSObject *));
+    }
+    if (script->regexpsOffset != 0) {
+        memcpy(JS_SCRIPT_REGEXPS(wscript)->vector, JS_SCRIPT_REGEXPS(script)->vector,
+               JS_SCRIPT_REGEXPS(wscript)->length * sizeof(JSObject *));
+    }
+    if (script->trynotesOffset != 0) {
+        memcpy(JS_SCRIPT_TRYNOTES(wscript)->vector, JS_SCRIPT_TRYNOTES(script)->vector,
+               JS_SCRIPT_TRYNOTES(wscript)->length * sizeof(JSTryNote));
+    }
+
+    if (wfun->u.i.nupvars != 0) {
+        JS_ASSERT(wfun->u.i.nupvars == JS_SCRIPT_UPVARS(wscript)->length);
+        memcpy(JS_SCRIPT_UPVARS(wscript)->vector, JS_SCRIPT_UPVARS(script)->vector,
+               wfun->u.i.nupvars * sizeof(uint32));
+    }
+
+    jsbytecode *pc = wscript->code;
+    while (*pc != JSOP_STOP) {
+        /* XYZZYbe should copy JSOP_TRAP? */
+        JSOp op = js_GetOpcode(cx, wscript, pc);
+        const JSCodeSpec *cs = &js_CodeSpec[op];
+        ptrdiff_t oplen = cs->length;
+        if (oplen < 0)
+            oplen = js_GetVariableBytecodeLength(pc);
+
+        /*
+         * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
+         * case where fun is an escaping flat closure. This works because the
+         * UPVAR and DSLOT ops by design have the same format: an upvar index
+         * immediate operand.
+         */
+        switch (op) {
+          case JSOP_GETUPVAR:       *pc = JSOP_GETUPVAR_DBG; break;
+          case JSOP_CALLUPVAR:      *pc = JSOP_CALLUPVAR_DBG; break;
+          case JSOP_GETDSLOT:       *pc = JSOP_GETUPVAR_DBG; break;
+          case JSOP_CALLDSLOT:      *pc = JSOP_CALLUPVAR_DBG; break;
+          case JSOP_DEFFUN_FC:      *pc = JSOP_DEFFUN_DBGFC; break;
+          case JSOP_DEFLOCALFUN_FC: *pc = JSOP_DEFLOCALFUN_DBGFC; break;
+          case JSOP_LAMBDA_FC:      *pc = JSOP_LAMBDA_DBGFC; break;
+          default:;
+        }
+        pc += oplen;
+    }
+
+    /*
+     * Flag the wrapper's script as hazardous, because it could host debugger
+     * or indirect eval calls that leak closures. Wrappers thus transitively
+     * wrap all contained closures that escape.
+     */
+    wscript->flags = script->flags | JSSF_ESCAPE_HAZARD;
+    wscript->version = script->version;
+    wscript->nfixed = script->nfixed;
+    wscript->filename = script->filename;
+    wscript->lineno = script->lineno;
+    wscript->nslots = script->nslots;
+    wscript->staticLevel = script->staticLevel;
+    wscript->principals = script->principals;
+    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);
+    wfun->u.i.script = wscript;
+    return wfunobj;
+}
+
 static JSBool
 args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
     jsint slot;
     JSStackFrame *fp;
 
     if (!JSVAL_IS_INT(id))
         return JS_TRUE;
     fp = (JSStackFrame *)
          JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
     if (!fp)
         return JS_TRUE;
     JS_ASSERT(fp->argsobj);
 
+    /*
+     * If this function or one in it needs upvars that reach above it in the
+     * scope chain, it must not be a null closure (it could be a flat closure,
+     * or an unoptimized closure -- the latter not necessarily heavyweight).
+     */
+    JS_ASSERT_IF(fp->fun->u.i.skipmin != 0, !FUN_NULL_CLOSURE(fp->fun));
+
     slot = JSVAL_TO_INT(id);
     switch (slot) {
       case ARGS_CALLEE:
         if (!TEST_OVERRIDE_BIT(fp, slot))
             *vp = OBJECT_TO_JSVAL(fp->callee);
         break;
 
       case ARGS_LENGTH:
@@ -585,24 +773,72 @@ JSClass js_ArgumentsClass = {
     NULL,               NULL,
     JS_CLASS_TRACE(args_or_call_trace), NULL
 };
 
 #define JSSLOT_CALLEE                    (JSSLOT_PRIVATE + 1)
 #define JSSLOT_CALL_ARGUMENTS            (JSSLOT_PRIVATE + 2)
 #define CALL_CLASS_FIXED_RESERVED_SLOTS  2
 
+/*
+ * A Declarative Environment object stores its active JSStackFrame pointer in
+ * its private slot, just as Call and Arguments objects do.
+ */
 JSClass js_DeclEnvClass = {
     js_Object_str,
-    JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
+    JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   JS_FinalizeStub,
     JSCLASS_NO_OPTIONAL_MEMBERS
 };
 
+static JS_REQUIRES_STACK JSBool
+CheckForEscapingClosure(JSContext *cx, JSObject *obj, jsval *vp)
+{
+    JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass ||
+              STOBJ_GET_CLASS(obj) == &js_DeclEnvClass);
+
+    if (cx->fp && cx->fp->script && (cx->fp->script->flags & JSSF_ESCAPE_HAZARD)) {
+        jsval v = *vp;
+
+        if (VALUE_IS_FUNCTION(cx, v)) {
+            JSObject *funobj = JSVAL_TO_OBJECT(v);
+            JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
+
+            /*
+             * Any escaping null or flat closure that reaches above itself or
+             * contains nested functions that reach above it must be wrapped.
+             * We can wrap only when this Call or Declarative Environment obj
+             * still has an active stack frame associated with it.
+             */
+            if (FUN_ESCAPE_HAZARD(fun)) {
+                JSStackFrame *fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
+                if (fp) {
+                    JSObject *wrapper = WrapEscapingClosure(cx, fp, funobj, fun);
+                    if (!wrapper)
+                        return false;
+                    *vp = OBJECT_TO_JSVAL(wrapper);
+                    return true;
+                }
+
+                JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                                     JSMSG_OPTIMIZED_CLOSURE_LEAK);
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static JS_REQUIRES_STACK JSBool
+CalleeGetter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+    return CheckForEscapingClosure(cx, obj, vp);
+}
+
 JSObject *
 js_GetCallObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject *callobj;
 
     /* Create a call object for fp only if it lacks one. */
     JS_ASSERT(fp->fun);
     callobj = fp->callobj;
@@ -623,21 +859,23 @@ js_GetCallObject(JSContext *cx, JSStackF
      * function's name.
      */
     JSAtom *lambdaName = (fp->fun->flags & JSFUN_LAMBDA) ? fp->fun->atom : NULL;
     if (lambdaName) {
         JSObject *env = js_NewObjectWithGivenProto(cx, &js_DeclEnvClass, NULL,
                                                    fp->scopeChain, 0);
         if (!env)
             return NULL;
+        env->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fp);
 
         /* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */
         fp->scopeChain = env;
         if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
-                                     OBJECT_TO_JSVAL(fp->callee), NULL, NULL,
+                                     OBJECT_TO_JSVAL(fp->callee),
+                                     CalleeGetter, NULL,
                                      JSPROP_PERMANENT | JSPROP_READONLY,
                                      0, 0, NULL)) {
             return NULL;
         }
     }
 
     callobj = js_NewObjectWithGivenProto(cx, &js_CallClass, NULL,
                                          fp->scopeChain, 0);
@@ -720,20 +958,28 @@ js_PutCallObject(JSContext *cx, JSStackF
                    fun->u.i.nvars * sizeof(jsval));
             if (scope->object == callobj && n > scope->freeslot)
                 scope->freeslot = n;
         }
         JS_UNLOCK_SCOPE(cx, scope);
     }
 
     /*
-     * Clear the private pointer to fp, which is about to go away (js_Invoke).
+     * Clear private pointers to fp, which is about to go away (js_Invoke).
      * Do this last because js_GetProperty calls above need to follow the
-     * private slot to find fp.
+     * call object's private slot to find fp.
      */
+    if ((fun->flags & JSFUN_LAMBDA) && fun->atom) {
+        JSObject *env = STOBJ_GET_PARENT(callobj);
+
+        JS_ASSERT(STOBJ_GET_CLASS(env) == &js_DeclEnvClass);
+        JS_ASSERT(STOBJ_GET_PRIVATE(env) == fp);
+        JS_SetPrivate(cx, env, NULL);
+    }
+
     JS_SetPrivate(cx, callobj, NULL);
     fp->callobj = NULL;
     return ok;
 }
 
 static JSBool
 call_enumerate(JSContext *cx, JSObject *obj)
 {
@@ -883,17 +1129,20 @@ static JSBool
 SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE);
 }
 
 JSBool
 js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
-    return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE);
+    if (!CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE))
+        return JS_FALSE;
+
+    return CheckForEscapingClosure(cx, obj, vp);
 }
 
 static JSBool
 SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
 {
     return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE);
 }
 
@@ -1094,20 +1343,30 @@ fun_getProperty(JSContext *cx, JSObject 
 
       case FUN_NAME:
         *vp = fun->atom
               ? ATOM_KEY(fun->atom)
               : STRING_TO_JSVAL(cx->runtime->emptyString);
         break;
 
       case FUN_CALLER:
-        if (fp && fp->down && fp->down->fun)
+        if (fp && fp->down && fp->down->fun) {
+            /*
+             * See the equivalent assertion in args_getProperty. Here we assert
+             * against a closure leak that goes through foo.caller. Wrapping an
+             * escaping optimized closure guarantees that its function cannot
+             * match the unwrapped stack frame's function (fp->fun) in the loop
+             * commented "Find fun's top-most activation record" above.
+             */
+            JS_ASSERT_IF(fp->down->fun->u.i.skipmin != 0,
+                         !FUN_NULL_CLOSURE(fp->down->fun));
             *vp = OBJECT_TO_JSVAL(fp->down->callee);
-        else
+        } else {
             *vp = JSVAL_NULL;
+        }
         if (!JSVAL_IS_PRIMITIVE(*vp)) {
             callbacks = JS_GetSecurityCallbacks(cx);
             if (callbacks && callbacks->checkObjectAccess) {
                 id = ATOM_KEY(cx->runtime->atomState.callerAtom);
                 if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
                     return JS_FALSE;
             }
         }
@@ -1267,33 +1526,42 @@ fun_convert(JSContext *cx, JSObject *obj
 #if JS_HAS_XDR
 
 /* XXX store parent and proto, if defined */
 JSBool
 js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
 {
     JSContext *cx;
     JSFunction *fun;
-    uint32 nullAtom;            /* flag to indicate if fun->atom is NULL */
+    uint32 firstword;           /* flag telling whether fun->atom is non-null,
+                                   plus for fun->u.i.skipmin, fun->u.i.wrapper,
+                                   and 14 bits reserved for future use */
     uintN nargs, nvars, nupvars, n;
-    uint32 localsword;          /* word to xdr argument and variable counts */
-    uint32 flagsword;           /* word to xdr upvars count and fun->flags */
+    uint32 localsword;          /* word for argument and variable counts */
+    uint32 flagsword;           /* word for fun->u.i.nupvars and fun->flags */
     JSTempValueRooter tvr;
     JSBool ok;
 
     cx = xdr->cx;
     if (xdr->mode == JSXDR_ENCODE) {
         fun = GET_FUNCTION_PRIVATE(cx, *objp);
         if (!FUN_INTERPRETED(fun)) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                  JSMSG_NOT_SCRIPTED_FUNCTION,
                                  JS_GetFunctionName(fun));
             return JS_FALSE;
         }
-        nullAtom = !fun->atom;
+        if (fun->u.i.wrapper) {
+            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+                                 JSMSG_XDR_CLOSURE_WRAPPER,
+                                 JS_GetFunctionName(fun));
+            return JS_FALSE;
+        }
+        JS_ASSERT((fun->u.i.wrapper & ~1U) == 0);
+        firstword = (fun->u.i.skipmin << 2) | (fun->u.i.wrapper << 1) | !!fun->atom;
         nargs = fun->nargs;
         nvars = fun->u.i.nvars;
         nupvars = fun->u.i.nupvars;
         localsword = (nargs << 16) | nvars;
         flagsword = (nupvars << 16) | fun->flags;
     } else {
         fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
         if (!fun)
@@ -1305,31 +1573,33 @@ js_XDRFunctionObject(JSXDRState *xdr, JS
 #endif
     }
 
     /* From here on, control flow must flow through label out. */
     MUST_FLOW_THROUGH("out");
     JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
     ok = JS_TRUE;
 
-    if (!JS_XDRUint32(xdr, &nullAtom))
+    if (!JS_XDRUint32(xdr, &firstword))
         goto bad;
-    if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom))
+    if ((firstword & 1U) && !js_XDRStringAtom(xdr, &fun->atom))
         goto bad;
     if (!JS_XDRUint32(xdr, &localsword) ||
         !JS_XDRUint32(xdr, &flagsword)) {
         goto bad;
     }
 
     if (xdr->mode == JSXDR_DECODE) {
         nargs = localsword >> 16;
         nvars = uint16(localsword);
         JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
         nupvars = flagsword >> 16;
         fun->flags = uint16(flagsword);
+        fun->u.i.skipmin = uint16(firstword >> 2);
+        fun->u.i.wrapper = (firstword >> 1) & 1;
     }
 
     /* do arguments and local vars */
     n = nargs + nvars + nupvars;
     if (n != 0) {
         void *mark;
         uintN i;
         uintN bitmapLength;
@@ -2129,16 +2399,18 @@ js_NewFunction(JSContext *cx, JSObject *
     /* Initialize all function members. */
     fun->nargs = nargs;
     fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRACEABLE);
     if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
         JS_ASSERT(!native);
         JS_ASSERT(nargs == 0);
         fun->u.i.nvars = 0;
         fun->u.i.nupvars = 0;
+        fun->u.i.skipmin = 0;
+        fun->u.i.wrapper = false;
         fun->u.i.script = NULL;
 #ifdef DEBUG
         fun->u.i.names.taggedAtom = 0;
 #endif
     } else {
         fun->u.n.extra = 0;
         fun->u.n.spare = 0;
         fun->u.n.clasp = NULL;
@@ -2199,16 +2471,26 @@ js_NewFlatClosure(JSContext *cx, JSFunct
 
     uintN level = fun->u.i.script->staticLevel;
     for (uint32 i = 0, n = uva->length; i < n; i++)
         closure->dslots[i] = js_GetUpvar(cx, level, uva->vector[i]);
 
     return closure;
 }
 
+JSObject *
+js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun)
+{
+    JS_ASSERT(cx->fp->script->flags & JSSF_ESCAPE_HAZARD);
+    JS_ASSERT(cx->fp->fun->flags & JSFUN_HEAVYWEIGHT);
+    JS_ASSERT(!FUN_OPTIMIZED_CLOSURE(cx->fp->fun));
+
+    return WrapEscapingClosure(cx, cx->fp, FUN_OBJECT(fun), fun);
+}
+
 JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
                   uintN nargs, uintN attrs)
 {
     JSPropertyOp gsop;
     JSFunction *fun;
 
     if (attrs & JSFUN_STUB_GSOPS) {
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -108,16 +108,18 @@ typedef union JSLocalNames {
                                        optimization level -- see above */
 
 #define FUN_OBJECT(fun)      (&(fun)->object)
 #define FUN_KIND(fun)        ((fun)->flags & JSFUN_KINDMASK)
 #define FUN_SET_KIND(fun,k)  ((fun)->flags = ((fun)->flags & ~JSFUN_KINDMASK) | (k))
 #define FUN_INTERPRETED(fun) (FUN_KIND(fun) >= JSFUN_INTERPRETED)
 #define FUN_FLAT_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_FLAT_CLOSURE)
 #define FUN_NULL_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_NULL_CLOSURE)
+#define FUN_OPTIMIZED_CLOSURE(fun) (FUN_KIND(fun) > JSFUN_INTERPRETED)
+#define FUN_ESCAPE_HAZARD(fun)     (FUN_OPTIMIZED_CLOSURE(fun) && (fun)->u.i.skipmin != 0)
 #define FUN_SLOW_NATIVE(fun) (!FUN_INTERPRETED(fun) && !((fun)->flags & JSFUN_FAST_NATIVE))
 #define FUN_SCRIPT(fun)      (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL)
 #define FUN_NATIVE(fun)      (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL)
 #define FUN_FAST_NATIVE(fun) (((fun)->flags & JSFUN_FAST_NATIVE)              \
                               ? (JSFastNative) (fun)->u.n.native              \
                               : NULL)
 #define FUN_MINARGS(fun)     (((fun)->flags & JSFUN_FAST_NATIVE)              \
                               ? 0                                             \
@@ -142,16 +144,25 @@ struct JSFunction {
                                      by this function */
             JSTraceableNative *trcinfo;  /* tracer metadata; can be first
                                             element of array */
         } n;
         struct {
             uint16      nvars;    /* number of local variables */
             uint16      nupvars;  /* number of upvars (computable from script
                                      but here for faster access) */
+            uint16       skipmin; /* net skip amount up (toward zero) from
+                                     script->staticLevel to nearest upvar,
+                                     including upvars in nested functions */
+            JSPackedBool wrapper; /* true if this function is a wrapper that
+                                     rewrites bytecode optimized for a function
+                                     judged non-escaping by the compiler, which
+                                     then escaped via the debugger or a rogue
+                                     indirect eval; if true, then this function
+                                     object's proto is the wrapped object */
             JSScript    *script;  /* interpreted bytecode descriptor or null */
             JSLocalNames names;   /* argument and variable names */
         } i;
     } u;
     JSAtom          *atom;        /* name for diagnostics and decompiling */
 
 #ifdef __cplusplus
 
@@ -226,16 +237,19 @@ extern void
 js_FinalizeFunction(JSContext *cx, JSFunction *fun);
 
 extern JSObject *
 js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent);
 
 extern JS_REQUIRES_STACK JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun);
 
+extern JS_REQUIRES_STACK JSObject *
+js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun);
+
 extern JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
                   uintN nargs, uintN flags);
 
 /*
  * Flags for js_ValueToFunction and js_ReportIsNotFunction.  We depend on the
  * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that
  * with #if/#error in jsfun.c.
@@ -260,17 +274,17 @@ extern JSObject *
 js_GetCallObject(JSContext *cx, JSStackFrame *fp);
 
 extern JS_FRIEND_API(JSBool)
 js_PutCallObject(JSContext *cx, JSStackFrame *fp);
 
 extern JSBool
 js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 
-extern JSBool
+extern JS_REQUIRES_STACK JSBool
 js_GetCallVar(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
 
 extern JSBool
 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp);
 
 extern JSBool
 js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp);
 
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2584,22 +2584,30 @@ AssertValidPropertyCacheHit(JSContext *c
 
 /*
  * Ensure that the intrepreter switch can close call-bytecode cases in the
  * same way as non-call bytecodes.
  */
 JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
+JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_CALLUPVAR_DBG_LENGTH);
+JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETUPVAR_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
 JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
 JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
 
 /*
+ * Same for debuggable flat closures defined at top level in another function
+ * or program fragment.
+ */
+JS_STATIC_ASSERT(JSOP_DEFFUN_FC_LENGTH == JSOP_DEFFUN_DBGFC_LENGTH);
+
+/*
  * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
  * remain distinct for the decompiler.
  */
 JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
 
 /* See TRY_BRANCH_AFTER_COND. */
 JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
 JS_STATIC_ASSERT(JSOP_IFNE == JSOP_IFEQ + 1);
@@ -5728,16 +5736,53 @@ js_Interpret(JSContext *cx)
             rval = js_GetUpvar(cx, script->staticLevel, uva->vector[index]);
             PUSH_OPND(rval);
 
             if (op == JSOP_CALLUPVAR)
                 PUSH_OPND(JSVAL_NULL);
           }
           END_CASE(JSOP_GETUPVAR)
 
+          BEGIN_CASE(JSOP_GETUPVAR_DBG)
+          BEGIN_CASE(JSOP_CALLUPVAR_DBG)
+            fun = fp->fun;
+            JS_ASSERT(FUN_KIND(fun) == JSFUN_INTERPRETED);
+            JS_ASSERT(fun->u.i.wrapper);
+
+            /* Scope for tempPool mark and local names allocation in it. */
+            {
+                void *mark = JS_ARENA_MARK(&cx->tempPool);
+                jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
+                if (!names)
+                    goto error;
+
+                index = fun->countArgsAndVars() + GET_UINT16(regs.pc);
+                atom = JS_LOCAL_NAME_TO_ATOM(names[index]);
+                id = ATOM_TO_JSID(atom);
+
+                ok = js_FindProperty(cx, id, &obj, &obj2, &prop);
+                JS_ARENA_RELEASE(&cx->tempPool, mark);
+                if (!ok)
+                    goto error;
+            }
+
+            if (!prop)
+                goto atom_not_defined;
+
+            /* Minimize footprint with generic code instead of NATIVE_GET. */
+            OBJ_DROP_PROPERTY(cx, obj2, prop);
+            vp = regs.sp;
+            PUSH_OPND(JSVAL_NULL);
+            if (!OBJ_GET_PROPERTY(cx, obj, id, vp))
+                goto error;
+
+            if (op == JSOP_CALLUPVAR_DBG)
+                PUSH_OPND(JSVAL_NULL);
+          END_CASE(JSOP_GETUPVAR_DBG)
+
           BEGIN_CASE(JSOP_GETDSLOT)
           BEGIN_CASE(JSOP_CALLDSLOT)
             obj = fp->callee;
             JS_ASSERT(obj);
             JS_ASSERT(obj->dslots);
 
             index = GET_UINT16(regs.pc);
             JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
@@ -6013,19 +6058,22 @@ js_Interpret(JSContext *cx)
             if (!ok) {
                 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
                 goto error;
             }
           }
           END_CASE(JSOP_DEFFUN)
 
           BEGIN_CASE(JSOP_DEFFUN_FC)
+          BEGIN_CASE(JSOP_DEFFUN_DBGFC)
             LOAD_FUNCTION(0);
 
-            obj = js_NewFlatClosure(cx, fun);
+            obj = (op == JSOP_DEFFUN_FC)
+                  ? js_NewFlatClosure(cx, fun)
+                  : js_NewDebuggableFlatClosure(cx, fun);
             if (!obj)
                 goto error;
             rval = OBJECT_TO_JSVAL(obj);
 
             attrs = (fp->flags & JSFRAME_EVAL)
                     ? JSPROP_ENUMERATE
                     : JSPROP_ENUMERATE | JSPROP_PERMANENT;
 
@@ -6112,16 +6160,27 @@ js_Interpret(JSContext *cx)
                 goto error;
 
             slot = GET_SLOTNO(regs.pc);
             TRACE_2(DefLocalFunSetSlot, slot, obj);
 
             fp->slots[slot] = OBJECT_TO_JSVAL(obj);
           END_CASE(JSOP_DEFLOCALFUN_FC)
 
+          BEGIN_CASE(JSOP_DEFLOCALFUN_DBGFC)
+            LOAD_FUNCTION(SLOTNO_LEN);
+
+            obj = js_NewDebuggableFlatClosure(cx, fun);
+            if (!obj)
+                goto error;
+
+            slot = GET_SLOTNO(regs.pc);
+            fp->slots[slot] = OBJECT_TO_JSVAL(obj);
+          END_CASE(JSOP_DEFLOCALFUN_DBGFC)
+
           BEGIN_CASE(JSOP_LAMBDA)
             /* Load the specified function object literal. */
             LOAD_FUNCTION(0);
             obj = FUN_OBJECT(fun);
 
             if (FUN_NULL_CLOSURE(fun)) {
                 obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
                 if (!obj)
@@ -6147,16 +6206,26 @@ js_Interpret(JSContext *cx)
 
             obj = js_NewFlatClosure(cx, fun);
             if (!obj)
                 goto error;
 
             PUSH_OPND(OBJECT_TO_JSVAL(obj));
           END_CASE(JSOP_LAMBDA_FC)
 
+          BEGIN_CASE(JSOP_LAMBDA_DBGFC)
+            LOAD_FUNCTION(0);
+
+            obj = js_NewDebuggableFlatClosure(cx, fun);
+            if (!obj)
+                goto error;
+
+            PUSH_OPND(OBJECT_TO_JSVAL(obj));
+          END_CASE(JSOP_LAMBDA_DBGFC)
+
           BEGIN_CASE(JSOP_CALLEE)
             PUSH_OPND(OBJECT_TO_JSVAL(fp->callee));
           END_CASE(JSOP_CALLEE)
 
 #if JS_HAS_GETTER_SETTER
           BEGIN_CASE(JSOP_GETTER)
           BEGIN_CASE(JSOP_SETTER)
           do_getter_setter:
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -294,17 +294,17 @@ js_SetProtoOrParent(JSContext *cx, JSObj
                 return JS_FALSE;
         }
 
         /*
          * Regenerate property cache shape ids for all of the scopes along the
          * old prototype chain to invalidate their property cache entries, in
          * case any entries were filled by looking up starting from obj.
          */
-        JSObject *oldproto = STOBJ_GET_PROTO(obj);
+        JSObject *oldproto = obj;
         while (oldproto && OBJ_IS_NATIVE(oldproto)) {
             JS_LOCK_OBJ(cx, oldproto);
             JSScope *scope = OBJ_SCOPE(oldproto);
             js_MakeScopeShapeUnique(cx, scope);
             JSObject *tmp = STOBJ_GET_PROTO(scope->object);
             JS_UNLOCK_OBJ(cx, oldproto);
             oldproto = tmp;
         }
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2793,16 +2793,18 @@ Decompile(SprintStack *ss, jsbytecode *p
                 ss->sprinter.offset = GetOff(ss, top);
                 if (op == JSOP_LEAVEBLOCKEXPR)
                     todo = SprintCString(&ss->sprinter, rval);
                 break;
               }
 
               case JSOP_GETUPVAR:
               case JSOP_CALLUPVAR:
+              case JSOP_GETUPVAR_DBG:
+              case JSOP_CALLUPVAR_DBG:
               case JSOP_GETDSLOT:
               case JSOP_CALLDSLOT:
               {
                 if (!jp->fun) {
                     JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN);
                     JS_GET_SCRIPT_FUNCTION(jp->script, 0, jp->fun);
                 }
 
@@ -3963,16 +3965,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                                    inXML ? DONT_ESCAPE : '"');
                 if (!rval)
                     return NULL;
                 todo = STR2OFF(&ss->sprinter, rval);
                 break;
 
               case JSOP_LAMBDA:
               case JSOP_LAMBDA_FC:
+              case JSOP_LAMBDA_DBGFC:
 #if JS_HAS_GENERATOR_EXPRS
                 sn = js_GetSrcNote(jp->script, pc);
                 if (sn && SN_TYPE(sn) == SRC_GENEXP) {
                     void *mark;
                     jsuword *innerLocalNames, *outerLocalNames;
                     JSScript *inner, *outer;
                     SprintStack ss2;
                     JSFunction *outerfun;
@@ -4328,16 +4331,17 @@ Decompile(SprintStack *ss, jsbytecode *p
                     return NULL;
                 js_printf(jp, "\tcase %s:\n", lval);
                 todo = -2;
                 break;
               }
 
               case JSOP_DEFFUN:
               case JSOP_DEFFUN_FC:
+              case JSOP_DEFFUN_DBGFC:
                 LOAD_FUNCTION(0);
                 todo = -2;
                 goto do_function;
                 break;
 
               case JSOP_TRAP:
                 saveop = op = JS_GetTrapOpcode(cx, jp->script, pc);
                 *pc = op;
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -575,8 +575,17 @@ OPDEF(JSOP_LAMBDA_FC,     226,"lambda_fc
 /*
  * Ensure that the value on the top of the stack is an object. The one
  * argument is an error message, defined in js.msg, that takes one parameter
  * (the decompilation of the primitive value).
  */
 OPDEF(JSOP_OBJTOP,        227,"objtop",        NULL,  3,  0,  0,  0,  JOF_UINT16)
 
 OPDEF(JSOP_LOOP,          228, "loop",         NULL,  1,  0,  0,  0,  JOF_BYTE)
+
+/*
+ * Debugger versions of JSOP_{GET,CALL}UPVAR.
+ */
+OPDEF(JSOP_GETUPVAR_DBG,  229,"getupvar_dbg",  NULL,  3,  0,  1, 19,  JOF_UINT16|JOF_NAME)
+OPDEF(JSOP_CALLUPVAR_DBG, 230,"callupvar_dbg", NULL,  3,  0,  2, 19,  JOF_UINT16|JOF_NAME|JOF_CALLOP)
+OPDEF(JSOP_DEFFUN_DBGFC,     231,"deffun_dbgfc",     NULL,  3,  0,  0,  0,  JOF_OBJECT|JOF_DECLARING)
+OPDEF(JSOP_DEFLOCALFUN_DBGFC,232,"deflocalfun_dbgfc",NULL,  5,  0,  0,  0,  JOF_SLOTOBJECT|JOF_DECLARING)
+OPDEF(JSOP_LAMBDA_DBGFC,     233,"lambda_dbgfc",     NULL,  3,  0,  1, 19,  JOF_OBJECT)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -814,17 +814,17 @@ JSCompiler::compileScript(JSContext *cx,
 
     JSCodeGenerator cg(&jsc, &codePool, &notePool, jsc.tokenStream.lineno);
 
     MUST_FLOW_THROUGH("out");
 
     /* Null script early in case of error, to reduce our code footprint. */
     script = NULL;
 
-    cg.flags |= (uint16) tcflags;
+    cg.flags |= uint16(tcflags);
     cg.scopeChain = scopeChain;
     if (!SetStaticLevel(&cg, TCF_GET_STATIC_LEVEL(tcflags)))
         goto out;
 
     /*
      * If funbox is non-null after we create the new script, callerFrame->fun
      * was saved in the 0th object table entry.
      */
@@ -1683,66 +1683,118 @@ JSCompiler::analyzeFunctions(JSFunctionB
  * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
  * nor uses an upvar "above" o_m's level.
  *
  * If function g itself contained lambdas that contained non-lambdas that reach
  * up above its level, then those non-lambdas would have to be marked too. This
  * process is potentially exponential in the number of functions, but generally
  * not so complex. But it can't be done during a single recursive traversal of
  * the funbox tree, so we must use a work queue.
+ *
+ * Return the minimal "skipmin" for funbox and its siblings. This is the delta
+ * between the static level of the bodies of funbox and its peers (which must
+ * be funbox->level + 1), and the static level of the nearest upvar among all
+ * the upvars contained by funbox and its peers. If there are no upvars, return
+ * FREE_STATIC_LEVEL. Thus this function never returns 0.
  */
-static void
+static uintN
 FindFunArgs(JSFunctionBox *funbox, int level, JSFunctionBoxQueue *queue)
 {
+    uintN allskipmin = FREE_STATIC_LEVEL;
+
     do {
         JSParseNode *fn = funbox->node;
+        JSFunction *fun = (JSFunction *) funbox->object;
         int fnlevel = level;
 
         /*
          * An eval can leak funbox, functions along its ancestor line, and its
          * immediate kids. Since FindFunArgs uses DFS and the parser propagates
          * TCF_FUN_HEAVYWEIGHT bottom up, funbox's ancestor function nodes have
          * already been marked as funargs by this point. Therefore we have to
          * flag only funbox->node and funbox->kids' nodes here.
          */
         if (funbox->tcflags & TCF_FUN_HEAVYWEIGHT) {
             fn->setFunArg();
             for (JSFunctionBox *kid = funbox->kids; kid; kid = kid->siblings)
                 kid->node->setFunArg();
         }
 
+        /*
+         * Compute in skipmin the least distance from fun's static level up to
+         * an upvar, whether used directly by fun, or indirectly by a function
+         * nested in fun.
+         */
+        uintN skipmin = FREE_STATIC_LEVEL;
+        JSParseNode *pn = fn->pn_body;
+
+        if (pn->pn_type == TOK_UPVARS) {
+            JSAtomList upvars(pn->pn_names);
+            JS_ASSERT(upvars.count != 0);
+
+            JSAtomListIterator iter(&upvars);
+            JSAtomListElement *ale;
+
+            while ((ale = iter()) != NULL) {
+                JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
+
+                if (!lexdep->isFreeVar()) {
+                    uintN upvarLevel = lexdep->frameLevel();
+
+                    if (int(upvarLevel) <= fnlevel)
+                        fn->setFunArg();
+
+                    uintN skip = (funbox->level + 1) - upvarLevel;
+                    if (skip < skipmin)
+                        skipmin = skip;
+                }
+            }
+        }
+
+        /*
+         * If this function escapes, whether directly (the parser detects such
+         * escapes) or indirectly (because this non-escaping function uses an
+         * upvar that reaches across an outer function boundary where the outer
+         * function escapes), enqueue it for further analysis, and bump fnlevel
+         * to trap any non-escaping children.
+         */
         if (fn->isFunArg()) {
             queue->push(funbox);
             fnlevel = int(funbox->level);
-        } else {
-            JSParseNode *pn = fn->pn_body;
-
-            if (pn->pn_type == TOK_UPVARS) {
-                JSAtomList upvars(pn->pn_names);
-                JS_ASSERT(upvars.count != 0);
-
-                JSAtomListIterator iter(&upvars);
-                JSAtomListElement *ale;
-
-                while ((ale = iter()) != NULL) {
-                    JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
-
-                    if (!lexdep->isFreeVar() && int(lexdep->frameLevel()) <= fnlevel) {
-                        fn->setFunArg();
-                        queue->push(funbox);
-                        fnlevel = int(funbox->level);
-                        break;
-                    }
-                }
-            }
-        }
-
-        if (funbox->kids)
-            FindFunArgs(funbox->kids, fnlevel, queue);
+        }
+
+        /*
+         * Now process the current function's children, and recalibrate their
+         * cumulative skipmin to be relative to the current static level.
+         */
+        if (funbox->kids) {
+            uintN kidskipmin = FindFunArgs(funbox->kids, fnlevel, queue);
+
+            JS_ASSERT(kidskipmin != 0);
+            if (kidskipmin != FREE_STATIC_LEVEL) {
+                --kidskipmin;
+                if (kidskipmin != 0 && kidskipmin < skipmin)
+                    skipmin = kidskipmin;
+            }
+        }
+
+        /*
+         * Finally, after we've traversed all of the current function's kids,
+         * minimize fun's skipmin against our accumulated skipmin. Do likewise
+         * with allskipmin, but minimize across funbox and all of its siblings,
+         * to compute our return value.
+         */
+        if (skipmin != FREE_STATIC_LEVEL) {
+            fun->u.i.skipmin = skipmin;
+            if (skipmin < allskipmin)
+                allskipmin = skipmin;
+        }
     } while ((funbox = funbox->siblings) != NULL);
+
+    return allskipmin;
 }
 
 bool
 JSCompiler::markFunArgs(JSFunctionBox *funbox, uintN tcflags)
 {
     JSFunctionBoxQueue queue;
     if (!queue.init(functionCount))
         return false;
@@ -1762,23 +1814,24 @@ JSCompiler::markFunArgs(JSFunctionBox *f
 
             while ((ale = iter()) != NULL) {
                 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
 
                 if (!lexdep->isFreeVar() &&
                     !lexdep->isFunArg() &&
                     lexdep->kind() == JSDefinition::FUNCTION) {
                     /*
-                     * Mark this formerly-Algol-like function as a funarg,
-                     * since it is referenced from a funarg and can no longer
-                     * use JSOP_{GET,CALL}UPVAR to access upvars.
+                     * Mark this formerly-Algol-like function as an escaping
+                     * function (i.e., as a funarg), because it is used from a
+                     * funarg and therefore can not use JSOP_{GET,CALL}UPVAR to
+                     * access upvars.
                      *
-                     * Progress is guaranteed since we set PND_FUNARG here,
-                     * which suppresses revisiting this function (namely the
-                     * !lexdep->isFunArg() test just above).
+                     * Progress is guaranteed because we set the funarg flag
+                     * here, which suppresses revisiting this function (thanks
+                     * to the !lexdep->isFunArg() test just above).
                      */
                     lexdep->setFunArg();
 
                     JSFunctionBox *afunbox = lexdep->pn_funbox;
                     queue.push(afunbox);
 
                     /*
                      * Walk over nested functions again, now that we have
@@ -7013,18 +7066,21 @@ QualifiedSuffix(JSContext *cx, JSTokenSt
 static JSParseNode *
 QualifiedIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn;
 
     pn = PropertySelector(cx, ts, tc);
     if (!pn)
         return NULL;
-    if (js_MatchToken(cx, ts, TOK_DBLCOLON))
+    if (js_MatchToken(cx, ts, TOK_DBLCOLON)) {
+        /* Hack for bug 496316. Slowing down E4X won't make it go away, alas. */
+        tc->flags |= TCF_FUN_HEAVYWEIGHT;
         pn = QualifiedSuffix(cx, ts, pn, tc);
+    }
     return pn;
 }
 
 static JSParseNode *
 AttributeIdentifier(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
 {
     JSParseNode *pn, *pn2;
     JSTokenType tt;
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -362,16 +362,24 @@ js_GetSprop(JSContext* cx, JSScopeProper
 {
     JS_ASSERT(!SPROP_HAS_STUB_GETTER(sprop));
     if (sprop->attrs & JSPROP_GETTER) {
         jsval fval = js_CastAsObjectJSVal(sprop->getter);
         return js_InternalGetOrSet(cx, obj, sprop->id, fval, JSACC_READ,
                                    0, 0, vp);
     }
 
+    /*
+     * JSObjectOps is private, so we know there are only two implementations
+     * of the thisObject hook: with objects and XPConnect wrapped native
+     * objects.  XPConnect objects don't expect the hook to be called here,
+     * but with objects do.
+     */
+    if (STOBJ_GET_CLASS(obj) == &js_WithClass)
+        obj = obj->map->ops->thisObject(cx, obj);
     return sprop->getter(cx, obj, SPROP_USERID(sprop), vp);
 }
 
 static JS_INLINE JSBool
 js_SetSprop(JSContext* cx, JSScopeProperty* sprop, JSObject* obj, jsval* vp)
 {
     JS_ASSERT(!(SPROP_HAS_STUB_SETTER(sprop) &&
                 !(sprop->attrs & JSPROP_GETTER)));
@@ -382,16 +390,19 @@ js_SetSprop(JSContext* cx, JSScopeProper
                                    1, vp, vp);
     }
 
     if (sprop->attrs & JSPROP_GETTER) {
         js_ReportGetterOnlyAssignment(cx);
         return JS_FALSE;
     }
 
+    /* See the comment in js_GetSprop as to why we can check for 'with'. */
+    if (STOBJ_GET_CLASS(obj) == &js_WithClass)
+        obj = obj->map->ops->thisObject(cx, obj);
     return sprop->setter(cx, obj, SPROP_USERID(sprop), vp);
 }
 
 /*
  * NB: SPROP_SET must not be called if (SPROP_HAS_STUB_SETTER(sprop) &&
  * !(sprop->attrs & JSPROP_GETTER)).
  */
 #define SPROP_SET(cx,sprop,obj,obj2,vp)                                       \
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1525,16 +1525,18 @@ js_NewScriptFromCG(JSContext *cx, JSCode
     if (cg->ntrynotes != 0)
         js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script));
     if (cg->objectList.length != 0)
         cg->objectList.finish(JS_SCRIPT_OBJECTS(script));
     if (cg->regexpList.length != 0)
         cg->regexpList.finish(JS_SCRIPT_REGEXPS(script));
     if (cg->flags & TCF_NO_SCRIPT_RVAL)
         script->flags |= JSSF_NO_SCRIPT_RVAL;
+    if ((cg->flags & TCF_COMPILE_N_GO) && cg->compiler->callerFrame)
+        script->flags |= JSSF_ESCAPE_HAZARD;
 
     if (cg->upvarList.count != 0) {
         JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
         memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector,
                cg->upvarList.count * sizeof(uint32));
         cg->upvarList.clear();
         JS_free(cx, cg->upvarMap.vector);
         cg->upvarMap.vector = NULL;
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -130,16 +130,19 @@ struct JSScript {
 #ifdef CHECK_SCRIPT_OWNER
     JSThread        *owner;     /* for thread-safe life-cycle assertions */
 #endif
 };
 
 #define JSSF_NO_SCRIPT_RVAL     0x01    /* no need for result value of last
                                            expression statement */
 #define JSSF_SAVED_CALLER_FUN   0x02    /* object 0 is caller function */
+#define JSSF_ESCAPE_HAZARD      0x04    /* script (including functions) was
+                                           created by dynamically scoped eval
+                                           or debugger eval-in-frame API */
 
 static JS_INLINE uintN
 StackDepth(JSScript *script)
 {
     return script->nslots - script->nfixed;
 }
 
 /* No need to store script->notes now that it is allocated right after code. */
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -1313,16 +1313,29 @@ TypeMap::captureMissingGlobalTypes(JSCon
 bool
 TypeMap::matches(TypeMap& other) const
 {
     if (length() != other.length())
         return false;
     return !memcmp(data(), other.data(), length());
 }
 
+/* Use the provided storage area to create a new type map that contains the partial type map
+   with the rest of it filled up from the complete type map. */
+static void
+mergeTypeMaps(uint8** partial, unsigned* plength, uint8* complete, unsigned clength, uint8* mem)
+{
+    unsigned l = *plength;
+    JS_ASSERT(l < clength);
+    memcpy(mem, *partial, l * sizeof(uint8));
+    memcpy(mem + l, complete + l, (clength - l) * sizeof(uint8));
+    *partial = mem;
+    *plength = clength;
+}
+
 /* Specializes a tree to any missing globals, including any dependent trees. */
 static JS_REQUIRES_STACK void
 specializeTreesToMissingGlobals(JSContext* cx, TreeInfo* root)
 {
     TreeInfo* ti = root;
 
     ti->typeMap.captureMissingGlobalTypes(cx, *ti->globalSlots, ti->nStackTypes);
     JS_ASSERT(ti->globalSlots->length() == ti->typeMap.length() - ti->nStackTypes);
@@ -1855,17 +1868,17 @@ FlushNativeGlobalFrame(JSContext* cx, un
     return mp - mp_base;
 }
 
 /*
  * Helper for js_GetUpvarOnTrace.
  */
 static uint32 
 GetUpvarOnTraceTail(InterpState* state, uint32 cookie,
-                    uint32 nativeStackFramePos, uint8* typemap, double* result)
+                    int32 nativeStackFramePos, uint8* typemap, double* result)
 {
     uintN slot = UPVAR_FRAME_SLOT(cookie);
     slot = (slot == CALLEE_UPVAR_SLOT) ? 0 : 2/*callee,this*/ + slot;
     *result = state->stackBase[nativeStackFramePos + slot];
     return typemap[slot];
 }
 
 /*
@@ -1891,20 +1904,20 @@ js_GetUpvarOnTrace(JSContext* cx, uint32
         uintN calleeLevel = fun->u.i.script->staticLevel;
         if (calleeLevel == upvarLevel) {
             /*
              * Now find the upvar's value in the native stack.
              * nativeStackFramePos is the offset of the start of the 
              * activation record corresponding to *fip in the native
              * stack.
              */
-            uintN nativeStackFramePos = state->callstackBase[0]->spoffset;
+            int32 nativeStackFramePos = state->callstackBase[0]->spoffset;
             for (FrameInfo** fip2 = state->callstackBase; fip2 <= fip; fip2++)
                 nativeStackFramePos += (*fip2)->spdist;
-            nativeStackFramePos -= (2 + (*fip)->argc);
+            nativeStackFramePos -= (2 + (*fip)->get_argc());
             uint8* typemap = (uint8*) (fi+1);
             return GetUpvarOnTraceTail(state, cookie, nativeStackFramePos,
                                        typemap, result);
         }
     }
 
     if (state->outermostTree->script->staticLevel == upvarLevel) {
         return GetUpvarOnTraceTail(state, cookie, 0, 
@@ -2082,26 +2095,39 @@ TraceRecorder::import(LIns* base, ptrdif
                         (void*)p, name, typestr[t & 7], t >> 3);)
 #endif
 }
 
 JS_REQUIRES_STACK void
 TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigned ngslots,
                       unsigned callDepth, uint8* typeMap)
 {
+    /* If we get a partial list that doesn't have all the types (i.e. recording from a side
+       exit that was recorded but we added more global slots later), merge the missing types
+       from the entry type map. This is safe because at the loop edge we verify that we
+       have compatible types for all globals (entry type and loop edge type match). While
+       a different trace of the tree might have had a guard with a different type map for
+       these slots we just filled in here (the guard we continue from didn't know about them),
+       since we didn't take that particular guard the only way we could have ended up here
+       is if that other trace had at its end a compatible type distribution with the entry
+       map. Since thats exactly what we used to fill in the types our current side exit
+       didn't provide, this is always safe to do. */
+
+    uint8* globalTypeMap = typeMap + stackSlots;
+    unsigned length = treeInfo->nGlobalTypes();
+
     /*
-     * If we get a partial list that doesn't have all the types, capture the missing types
-     * from the current environment.
+     * This is potentially the typemap of the side exit and thus shorter than the tree's
+     * global type map.
      */
-    TypeMap fullTypeMap(typeMap, stackSlots + ngslots);
-    if (ngslots < treeInfo->globalSlots->length()) {
-        fullTypeMap.captureMissingGlobalTypes(cx, *treeInfo->globalSlots, stackSlots);
-        ngslots = treeInfo->globalSlots->length();
-    }
-    uint8* globalTypeMap = fullTypeMap.data() + stackSlots;
+    if (ngslots < length) {
+        mergeTypeMaps(&globalTypeMap/*out param*/, &ngslots/*out param*/,
+                      treeInfo->globalTypeMap(), length,
+                      (uint8*)alloca(sizeof(uint8) * length));
+    }
     JS_ASSERT(ngslots == treeInfo->nGlobalTypes());
 
     /*
      * Check whether there are any values on the stack we have to unbox and do that first
      * before we waste any time fetching the state from the stack.
      */
     ptrdiff_t offset = -treeInfo->nativeStackBase;
     uint8* m = typeMap;
@@ -3546,17 +3572,17 @@ js_SynthesizeFrame(JSContext* cx, const 
 
     uintN nframeslots = JS_HOWMANY(sizeof(JSInlineFrame), sizeof(jsval));
     JSScript* script = fun->u.i.script;
     size_t nbytes = (nframeslots + script->nslots) * sizeof(jsval);
 
     /* Code duplicated from inline_call: case in js_Interpret (FIXME). */
     JSArena* a = cx->stackPool.current;
     void* newmark = (void*) a->avail;
-    uintN argc = fi.argc & 0x7fff;
+    uintN argc = fi.get_argc();
     jsval* vp = fp->slots + fi.spdist - (2 + argc);
     uintN missing = 0;
     jsval* newsp;
 
     if (fun->nargs > argc) {
         const JSFrameRegs& regs = *fp->regs;
 
         newsp = vp + 2 + fun->nargs;
@@ -3605,17 +3631,17 @@ js_SynthesizeFrame(JSContext* cx, const 
 
     newifp->frame.callobj = NULL;
     newifp->frame.argsobj = NULL;
     newifp->frame.varobj = NULL;
     newifp->frame.script = script;
     newifp->frame.callee = fi.callee; // Roll with a potentially stale callee for now.
     newifp->frame.fun = fun;
 
-    bool constructing = (fi.argc & 0x8000) != 0;
+    bool constructing = fi.is_constructing();
     newifp->frame.argc = argc;
     newifp->callerRegs.pc = fi.pc;
     newifp->callerRegs.sp = fp->slots + fi.spdist;
     fp->imacpc = fi.imacpc;
 
 #ifdef DEBUG
     if (fi.block != fp->blockChain) {
         for (JSObject* obj = fi.block; obj != fp->blockChain; obj = STOBJ_GET_PARENT(obj))
@@ -3990,20 +4016,21 @@ js_AttemptToExtendTree(JSContext* cx, VM
                guard (anchor) has the type information for everything below the current scope,
                and the actual guard we exited from has the types for everything in the current
                scope (and whatever it inlined). We have to merge those maps here. */
             VMSideExit* e1 = anchor;
             VMSideExit* e2 = exitedFrom;
             fullMap.add(getStackTypeMap(e1), e1->numStackSlotsBelowCurrentFrame);
             fullMap.add(getStackTypeMap(e2), e2->numStackSlots);
             stackSlots = fullMap.length();
-            fullMap.add(getGlobalTypeMap(e2), e2->numGlobalSlots);
-            ngslots = e2->numGlobalSlots;
+            fullMap.add(getGlobalTypeMap(e1), e1->numGlobalSlots);
+            ngslots = e1->numGlobalSlots;
             typeMap = fullMap.data();
         }
+        JS_ASSERT(ngslots >= anchor->numGlobalSlots);
         return js_StartRecorder(cx, anchor, c, (TreeInfo*)f->vmprivate, stackSlots,
                                 ngslots, typeMap, exitedFrom, outer, cx->fp->argc);
     }
     return false;
 }
 
 static JS_REQUIRES_STACK VMSideExit*
 js_ExecuteTree(JSContext* cx, Fragment* f, uintN& inlineCallCount,
@@ -5245,43 +5272,68 @@ js_PurgeScriptRecordingAttempts(JSDHashT
     JSScript *script = (JSScript *)arg;
     jsbytecode *pc = (jsbytecode *)e->key;
 
     if (JS_UPTRDIFF(pc, script->code) < script->length)
         return JS_DHASH_REMOVE;
     return JS_DHASH_NEXT;
 }
 
-JS_REQUIRES_STACK void
-js_PurgeScriptFragments(JSContext* cx, JSScript* script)
-{
-    if (!TRACING_ENABLED(cx))
-        return;
-    debug_only_v(printf("Purging fragments for JSScript %p.\n", (void*)script);)
+/*
+ * Call 'action' for each root fragment created for 'script'.
+ */
+template<typename FragmentAction>
+static void
+js_IterateScriptFragments(JSContext* cx, JSScript* script, FragmentAction action)
+{
     JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
         for (VMFragment **f = &(tm->vmfragments[i]); *f; ) {
             VMFragment* frag = *f;
             /* Disable future use of any script-associated VMFragment.*/
             if (JS_UPTRDIFF(frag->ip, script->code) < script->length) {
                 JS_ASSERT(frag->root == frag);
                 debug_only_v(printf("Disconnecting VMFragment %p "
                                     "with ip %p, in range [%p,%p).\n",
                                     (void*)frag, frag->ip, script->code,
                                     script->code + script->length));
                 VMFragment* next = frag->next;
-                for (Fragment *p = frag; p; p = p->peer)
-                    js_TrashTree(cx, p);
-                tm->fragmento->clearFragment(frag);
+                action(cx, tm, frag);
                 *f = next;
             } else {
                 f = &((*f)->next);
             }
         }
     }
+}
+
+static void trashTreeAction(JSContext* cx, JSTraceMonitor* tm, Fragment* frag)
+{
+    for (Fragment *p = frag; p; p = p->peer)
+        js_TrashTree(cx, p);
+}
+
+static void clearFragmentAction(JSContext* cx, JSTraceMonitor* tm, Fragment* frag)
+{
+    tm->fragmento->clearFragment(frag);
+}
+
+JS_REQUIRES_STACK void
+js_PurgeScriptFragments(JSContext* cx, JSScript* script)
+{
+    if (!TRACING_ENABLED(cx))
+        return;
+    debug_only_v(printf("Purging fragments for JSScript %p.\n", (void*)script);)
+    /*
+     * js_TrashTree trashes dependent trees recursively, so we must do all the trashing
+     * before clearing in order to avoid calling js_TrashTree with a deleted fragment.
+     */
+    js_IterateScriptFragments(cx, script, trashTreeAction);
+    js_IterateScriptFragments(cx, script, clearFragmentAction);
+    JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
     JS_DHashTableEnumerate(&(tm->recordAttempts),
                            js_PurgeScriptRecordingAttempts, script);
 
 }
 
 bool
 js_OverfullFragmento(JSTraceMonitor* tm, Fragmento *fragmento)
 {
@@ -6653,39 +6705,44 @@ TraceRecorder::getThis(LIns*& this_ins)
 
         /*
          * We don't have argv[-1] in global code, so we don't update the tracker here.
          */
         return JSRS_CONTINUE;
     }
 
     jsval& thisv = cx->fp->argv[-1];
+    JS_ASSERT(JSVAL_IS_OBJECT(thisv));
 
     /*
      * Traces type-specialize between null and objects, so if we currently see a null
      * value in argv[-1], this trace will only match if we see null at runtime as well.
      * Bake in the global object as 'this' object, updating the tracker as well. We
      * can only detect this condition prior to calling js_ComputeThisForFrame, since it
      * updates the interpreter's copy of argv[-1].
      */
-    if (JSVAL_IS_NULL(original)) {
+    JSClass* clasp = NULL;;
+    if (JSVAL_IS_NULL(original) ||
+        (((clasp = STOBJ_GET_CLASS(JSVAL_TO_OBJECT(original))) == &js_CallClass) ||
+         (clasp == &js_BlockClass))) {
+        if (clasp)
+            guardClass(JSVAL_TO_OBJECT(original), get(&thisv), clasp, snapshot(BRANCH_EXIT));
         JS_ASSERT(!JSVAL_IS_PRIMITIVE(thisv));
         if (thisObj != globalObj)
             ABORT_TRACE("global object was wrapped while recording");
         this_ins = INS_CONSTPTR(thisObj);
         set(&thisv, this_ins);
         return JSRS_CONTINUE;
     }
     this_ins = get(&thisv);
 
     /*
      * The only unwrapped object that needs to be wrapped that we can get here is the
      * global object obtained throught the scope chain.
      */
-    JS_ASSERT(JSVAL_IS_OBJECT(thisv));
     JSObject* obj = js_GetWrappedObject(cx, JSVAL_TO_OBJECT(thisv));
     OBJ_TO_INNER_OBJECT(cx, obj);
     if (!obj)
         return JSRS_ERROR;
 
     JS_ASSERT(original == thisv || original == OBJECT_TO_JSVAL(obj));
     this_ins = lir->ins_choose(lir->ins2(LIR_eq,
                                          this_ins,
@@ -8502,17 +8559,17 @@ TraceRecorder::interpretedFunctionCall(j
     if (argc >= 0x8000)
         ABORT_TRACE("too many arguments");
 
     fi->callee = JSVAL_TO_OBJECT(fval);
     fi->block = fp->blockChain;
     fi->pc = fp->regs->pc;
     fi->imacpc = fp->imacpc;
     fi->spdist = fp->regs->sp - fp->slots;
-    fi->argc = argc | (constructing ? 0x8000 : 0);
+    fi->set_argc(argc, constructing);
     fi->spoffset = 2 /*callee,this*/ + fp->argc;
 
     unsigned callDepth = getCallDepth();
     if (callDepth >= treeInfo->maxCallDepth)
         treeInfo->maxCallDepth = callDepth + 1;
     if (callDepth == 0)
         fi->spoffset = 2 /*callee,this*/ + argc - fi->spdist;
 
@@ -10653,16 +10710,29 @@ TraceRecorder::record_JSOP_HOLE()
 }
 
 JSRecordingStatus
 TraceRecorder::record_JSOP_LOOP()
 {
     return JSRS_CONTINUE;
 }
 
+#define DBG_STUB(OP)                                                          \
+    JS_REQUIRES_STACK JSRecordingStatus                                       \
+    TraceRecorder::record_##OP()                                              \
+    {                                                                         \
+        ABORT_TRACE("can't trace " #OP);                                      \
+    }
+
+DBG_STUB(JSOP_GETUPVAR_DBG)
+DBG_STUB(JSOP_CALLUPVAR_DBG)
+DBG_STUB(JSOP_DEFFUN_DBGFC)
+DBG_STUB(JSOP_DEFLOCALFUN_DBGFC)
+DBG_STUB(JSOP_LAMBDA_DBGFC)
+
 #ifdef JS_JIT_SPEW
 /* Prints information about entry typemaps and unstable exits for all peers at a PC */
 void
 js_DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape,
                      uint32 argc)
 {
     Fragment* f;
     TreeInfo* ti;
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -72,23 +72,16 @@ class Queue : public avmplus::GCObject {
     }
 public:
     Queue(unsigned max = 16) {
         this->_max = max;
         this->_len = 0;
         this->_data = (T*)malloc(max * sizeof(T));
     }
 
-    Queue(T* data, unsigned length) {
-        this->_max =
-        this->_len = length;
-        this->_data = (T*)malloc(length * sizeof(T));
-        memcpy(this->_data, data, length);
-    }
-
     ~Queue() {
         free(_data);
     }
 
     bool contains(T a) {
         for (unsigned n = 0; n < _len; ++n) {
             if (_data[n] == a)
                 return true;
@@ -192,22 +185,16 @@ public:
         clearDemotability();
     }
 };
 
 typedef Queue<uint16> SlotList;
 
 class TypeMap : public Queue<uint8> {
 public:
-    TypeMap() : Queue<uint8>() {
-    }
-
-    TypeMap(uint8* partial, unsigned length) : Queue<uint8>(partial, length) {
-    }
-
     JS_REQUIRES_STACK void captureTypes(JSContext* cx, SlotList& slots, unsigned callDepth);
     JS_REQUIRES_STACK void captureMissingGlobalTypes(JSContext* cx,
                                                      SlotList& slots,
                                                      unsigned stackSlots);
     bool matches(TypeMap& other) const;
 };
 
 enum ExitType {
@@ -301,28 +288,41 @@ static inline uint8* getFullTypeMap(nano
 }
 
 struct FrameInfo {
     JSObject*       callee;     // callee function object
     JSObject*       block;      // caller block chain head
     jsbytecode*     pc;         // caller fp->regs->pc
     jsbytecode*     imacpc;     // caller fp->imacpc
     uint16          spdist;     // distance from fp->slots to fp->regs->sp at JSOP_CALL
-    uint16          argc;       // actual argument count, may be < fun->nargs
+
+    /*
+     * Bit  15 (0x8000) is a flag that is set if constructing (called through new).
+     * Bits 0-14 are the actual argument count. This may be less than fun->nargs.
+     */
+    uint16          argc;
 
     /*
      * Stack pointer adjustment needed for navigation of native stack in
      * js_GetUpvarOnTrace. spoffset is the number of slots in the native
      * stack frame for the caller *before* the slots covered by spdist.
      * This may be negative if the caller is the top level script.
      * The key fact is that if we let 'cpos' be the start of the caller's
      * native stack frame, then (cpos + spoffset) points to the first 
      * non-argument slot in the callee's native stack frame.
      */
     int32          spoffset;
+
+    // Safer accessors for argc.
+    enum { CONSTRUCTING_MASK = 0x8000 };
+    void   set_argc(uint16 argc, bool constructing) {
+        this->argc = argc | (constructing ? CONSTRUCTING_MASK : 0);
+    }
+    uint16 get_argc() const { return argc & ~CONSTRUCTING_MASK; }
+    bool   is_constructing() const { return (argc & CONSTRUCTING_MASK) != 0; }
 };
 
 struct UnstableExit
 {
     nanojit::Fragment* fragment;
     VMSideExit* exit;
     UnstableExit* next;
 };
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -199,17 +199,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 - 48)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 49)
 
 /*
  * Library-private functions.
  */
 extern JSBool
 js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
 
 extern JSBool
--- a/js/src/trace-test.js
+++ b/js/src/trace-test.js
@@ -5113,16 +5113,58 @@ function testUndemoteLateGlobalSlots() {
     }
     delete aaa;
     delete bbb;
     return "ok";
 }
 testUndemoteLateGlobalSlots.expected = "ok";
 test(testUndemoteLateGlobalSlots);
 
+function testSetProtoRegeneratesObjectShape()
+{
+  var f = function() {};
+  var g = function() {};
+  g.prototype.__proto__ = {};
+
+  function iq(obj)
+  {
+    for (var i = 0; i < 10; ++i)
+      "" + obj.prototype;
+  }
+
+  iq(f);
+  iq(f);
+  iq(f);
+  iq(f);
+  iq(g);
+
+  if (shapeOf(f.prototype) === shapeOf(g.prototype))
+    return "object shapes same after proto of one is changed";
+
+  return true;
+}
+testSetProtoRegeneratesObjectShape.expected = true;
+test(testSetProtoRegeneratesObjectShape);
+
+function testFewerGlobalsInInnerTree() {
+    for each (a in [new Number(1), new Number(1), {}, {}, new Number(1)]) {
+        for each (b in [2, "", 2, "", "", ""]) {
+		    for each (c in [{}, {}, 3, 3, 3, 3, {}, {}]) {
+                4 + a;
+			}
+		}
+	}
+    delete a;
+    delete b;
+    delete c;
+    return "ok";
+}
+testFewerGlobalsInInnerTree.expected = "ok";
+test(testFewerGlobalsInInnerTree);
+
 /*****************************************************************************
  *                                                                           *
  *  _____ _   _  _____ ______ _____ _______                                  *
  * |_   _| \ | |/ ____|  ____|  __ \__   __|                                 *
  *   | | |  \| | (___ | |__  | |__) | | |                                    *
  *   | | | . ` |\___ \|  __| |  _  /  | |                                    *
  *  _| |_| |\  |____) | |____| | \ \  | |                                    *
  * |_____|_| \_|_____/|______|_|  \_\ |_|                                    *