Merge tracemonkey into mozilla-central. (a=blockers)
authorChris Leary <cdleary@mozilla.com>
Sat, 08 Jan 2011 00:48:44 -0800
changeset 60257 df3c1150dd7ace3fb6799fefc7718d1bbfe95c72
parent 60237 8f1c39add00f29c2e9d4ce65b015cea4d3c28ffd (current diff)
parent 60256 d59c08ab217121a12386e7572ea20e330e2519cf (diff)
child 60258 8e0b1aaf4acbecf60324179d49d95294f5730dc6
push id17896
push usercleary@mozilla.com
push dateSat, 08 Jan 2011 08:51:06 +0000
treeherdermozilla-central@df3c1150dd7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblockers
milestone2.0b9pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge tracemonkey into mozilla-central. (a=blockers)
js/src/jscntxt.h
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug576837-regexp.js
@@ -0,0 +1,45 @@
+/*
+ * Check that builtin character classes within ranges produce syntax
+ * errors.
+ *
+ * Note that, per the extension in bug 351463, SpiderMonkey permits hyphens
+ * adjacent to character class escapes in character classes, treating them as a
+ * hyphen pattern character. Therefore /[\d-\s]/ is okay
+ *
+ * Note: /\b/ is the backspace escape, which is a single pattern character,
+ * though it looks deceptively like a character class.
+ */
+
+function isRegExpSyntaxError(pattern) {
+    try {
+        var re = new RegExp(pattern);
+    } catch (e) {
+        if (e instanceof SyntaxError)
+            return true;
+    }
+    return false;
+}
+
+assertEq(isRegExpSyntaxError('[C-\\s]'), true);
+assertEq(isRegExpSyntaxError('[C-\\d]'), true);
+assertEq(isRegExpSyntaxError('[C-\\W]'), true);
+assertEq(isRegExpSyntaxError('[C-]'), false);
+assertEq(isRegExpSyntaxError('[-C]'), false);
+assertEq(isRegExpSyntaxError('[C-C]'), false);
+assertEq(isRegExpSyntaxError('[C-C]'), false);
+assertEq(isRegExpSyntaxError('[\\b-\\b]'), false);
+assertEq(isRegExpSyntaxError('[\\B-\\B]'), false);
+assertEq(isRegExpSyntaxError('[\\b-\\B]'), false);
+assertEq(isRegExpSyntaxError('[\\B-\\b]'), true);
+assertEq(isRegExpSyntaxError('[\\b-\\w]'), true);
+assertEq(isRegExpSyntaxError('[\\B-\\w]'), true);
+
+/* Extension. */
+assertEq(isRegExpSyntaxError('[\\s-\\s]'), false);
+assertEq(isRegExpSyntaxError('[\\W-\\s]'), false);
+assertEq(isRegExpSyntaxError('[\\s-\\W]'), false);
+assertEq(isRegExpSyntaxError('[\\W-C]'), false);
+assertEq(isRegExpSyntaxError('[\\d-C]'), false);
+assertEq(isRegExpSyntaxError('[\\s-C]'), false);
+assertEq(isRegExpSyntaxError('[\\w-\\b]'), false);
+assertEq(isRegExpSyntaxError('[\\w-\\B]'), false);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug623859.js
@@ -0,0 +1,16 @@
+// |jit-test| allow-oom
+
+(function() {
+   Iterator((function() {
+           switch ((7)) {
+               default:
+                     return (Float32Array).call([], 4300018)
+                         case Proxy.create((function() {
+                                     return {
+                                               e:
+                                                         function() {}
+                                                                 }
+                                                                       })):
+                             }
+                               })())
+   })()
--- a/js/src/jit-test/tests/basic/testChangingTypeDuringRecording.js
+++ b/js/src/jit-test/tests/basic/testChangingTypeDuringRecording.js
@@ -1,19 +1,6 @@
-/* Touch/init early so global shape doesn't change in loop */
-var SetOnIter = HOTLOOP - 1;
-var x = 3;
-var i = 0;
-assertEq(true, true);
-
-for (i = 0; i < SetOnIter + 10; ++i) {
-    x = 3;
-    setGlobalPropIf(i == SetOnIter, 'x', 'pretty');
-    assertEq(x == 'pretty', i == SetOnIter);
-    x = 3;
+X = { value: "" }
+z = 0;
+for (var i = 0; i < 20; i++) {
+    Object.defineProperty(this, "z", X);
+    z = 1;
 }
-
-for (i = 0; i < SetOnIter + 10; ++i) {
-    x = 3;
-    defGlobalPropIf(i == SetOnIter, 'x', { value:'pretty' });
-    assertEq(x == 'pretty', i == SetOnIter);
-    x = 3;
-}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testInnerTreeMutatingUpvars.js
@@ -0,0 +1,17 @@
+function f() {
+    for (var a = 0; a < HOTLOOP; ++a) {
+        (function () {
+            var x;
+            for (var b = 0; b < HOTLOOP; ++b) {
+                x = 0;
+                (function () {
+                    for (var i = 0; i < 1; ++i) {
+                        x = 1;
+                    }
+                })();
+                x++;
+            }
+        })();
+    }
+}
+f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug623474.js
@@ -0,0 +1,10 @@
+for (var j=0;j<2;++j) { (function(o){o.length})(String.prototype); }
+
+for each(let y in [Number, Number]) {
+    try {
+        "".length()
+    } catch(e) {}
+}
+
+/* Don't crash. */
+
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -341,8 +341,9 @@ MSG_DEF(JSMSG_STRICT_CODE_LET_EXPR_STMT,
 MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 259, 0, JSEXN_TYPEERR, "can't change object's extensibility")
 MSG_DEF(JSMSG_SC_BAD_SERIALIZED_DATA, 260, 1, JSEXN_INTERNALERR, "bad serialized structured data ({0})")
 MSG_DEF(JSMSG_SC_UNSUPPORTED_TYPE,    261, 0, JSEXN_TYPEERR, "unsupported type for structured data")
 MSG_DEF(JSMSG_SC_RECURSION,           262, 0, JSEXN_INTERNALERR, "recursive object")
 MSG_DEF(JSMSG_CANT_WRAP_XML_OBJECT,   263, 0, JSEXN_TYPEERR, "can't wrap XML objects")
 MSG_DEF(JSMSG_BAD_CLONE_VERSION,      264, 0, JSEXN_ERR, "unsupported structured clone version")
 MSG_DEF(JSMSG_CANT_CLONE_OBJECT,      265, 0, JSEXN_TYPEERR, "can't clone object")
 MSG_DEF(JSMSG_NON_NATIVE_SCOPE,       266, 0, JSEXN_TYPEERR, "non-native scope object")
+MSG_DEF(JSMSG_STRICT_FUNCTION_STATEMENT, 267, 0, JSEXN_SYNTAXERR, "in strict mode code, functions may be declared only at top level or immediately within another function")
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -2991,16 +2991,18 @@ namespace js {
 template<bool allocateCapacity>
 static JS_ALWAYS_INLINE JSObject *
 NewArray(JSContext *cx, jsuint length, JSObject *proto)
 {
     JS_ASSERT_IF(proto, proto->isArray());
 
     gc::FinalizeKind kind = GuessObjectGCKind(length, true);
     JSObject *obj = detail::NewObject<WithProto::Class, false>(cx, &js_ArrayClass, proto, NULL, kind);
+    if (!obj)
+        return NULL;
 
     obj->setArrayLength(length);
 
     if (allocateCapacity && !obj->ensureSlots(cx, length))
         return NULL;
 
     return obj;
 }
@@ -3022,16 +3024,19 @@ NewDenseUnallocatedArray(JSContext *cx, 
 {
     return NewArray<false>(cx, length, proto);
 }
 
 JSObject *
 NewDenseCopiedArray(JSContext *cx, uintN length, Value *vp, JSObject *proto)
 {
     JSObject* obj = NewArray<true>(cx, length, proto);
+    if (!obj)
+        return NULL;
+
     JS_ASSERT(obj->getDenseArrayCapacity() >= length);
 
     if (vp)
         memcpy(obj->getDenseArrayElements(), vp, length * sizeof(Value));
 
     return obj;
 }
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -2014,35 +2014,44 @@ JSContext::resetCompartment()
          */
         OBJ_TO_INNER_OBJECT(this, scopeobj);
         if (!scopeobj)
             goto error;
     }
 
     compartment = scopeobj->compartment();
 
-    /*
-     * If wrapException fails, it overrides this->exception and
-     * reports OOM. The upshot is that we silently turn the exception
-     * into an uncatchable OOM error. A bit surprising, but the
-     * caller is just going to return false either way.
-     */
     if (isExceptionPending())
-        (void) compartment->wrapException(this);
+        wrapPendingException();
     return;
 
 error:
 
     /*
      * If we try to use the context without a selected compartment,
      * we will crash.
      */
     compartment = NULL;
 }
 
+/*
+ * Since this function is only called in the context of a pending exception,
+ * the caller must subsequently take an error path. If wrapping fails, we leave
+ * the exception cleared, which, in the context of an error path, will be
+ * interpreted as an uncatchable exception.
+ */
+void
+JSContext::wrapPendingException()
+{
+    Value v = getPendingException();
+    clearPendingException();
+    if (compartment->wrap(this, &v))
+        setPendingException(v);
+}
+
 void
 JSContext::pushSegmentAndFrame(js::StackSegment *newseg, JSFrameRegs &newregs)
 {
     JS_ASSERT(regs != &newregs);
     if (hasActiveSegment())
         currentSegment->suspend(regs);
     newseg->setPreviousInContext(currentSegment);
     currentSegment = newseg;
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -891,17 +891,17 @@ private:
 # define JS_ON_TRACE(cx)            (cx->compartment && JS_TRACE_MONITOR(cx).ontrace())
 #else
 # define JS_ON_TRACE(cx)            false
 #endif
 
 #ifdef DEBUG
 # define FUNCTION_KIND_METER_LIST(_)                                          \
                         _(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar),  \
-                        _(display), _(flat), _(setupvar), _(badfunarg),       \
+                        _(flat), _(badfunarg),                                \
                         _(joinedsetmethod), _(joinedinitmethod),              \
                         _(joinedreplace), _(joinedsort), _(joinedmodulepat),  \
                         _(mreadbarrier), _(mwritebarrier), _(mwslotbarrier),  \
                         _(unjoined)
 # define identity(x)    x
 
 struct JSFunctionMeter {
     int32 FUNCTION_KIND_METER_LIST(identity);
@@ -1763,16 +1763,17 @@ struct JSContext
         return !!regs;
     }
 
   public:
     friend class js::StackSpace;
     friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
 
     void resetCompartment();
+    void wrapPendingException();
 
     /* 'regs' must only be changed by calling this function. */
     void setCurrentRegs(JSFrameRegs *regs) {
         JS_ASSERT_IF(regs, regs->fp);
         this->regs = regs;
         if (!regs)
             resetCompartment();
     }
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -203,27 +203,31 @@ JSCompartment::wrap(JSContext *cx, Value
 
         /* Don't unwrap an outer window proxy. */
         if (!obj->getClass()->ext.innerObject) {
             obj = vp->toObject().unwrap(&flags);
             vp->setObject(*obj);
             if (obj->getCompartment() == this)
                 return true;
 
-            if (cx->runtime->preWrapObjectCallback)
+            if (cx->runtime->preWrapObjectCallback) {
                 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
-            if (!obj)
-                return false;
+                if (!obj)
+                    return false;
+            }
 
             vp->setObject(*obj);
             if (obj->getCompartment() == this)
                 return true;
         } else {
-            if (cx->runtime->preWrapObjectCallback)
+            if (cx->runtime->preWrapObjectCallback) {
                 obj = cx->runtime->preWrapObjectCallback(cx, global, obj, flags);
+                if (!obj)
+                    return false;
+            }
 
             JS_ASSERT(!obj->isWrapper() || obj->getClass()->ext.innerObject);
             vp->setObject(*obj);
         }
 
 #ifdef DEBUG
         {
             JSObject *outer = obj;
@@ -348,31 +352,16 @@ JSCompartment::wrap(JSContext *cx, AutoI
     jsint length = props.length();
     for (size_t n = 0; n < size_t(length); ++n) {
         if (!wrapId(cx, &vector[n]))
             return false;
     }
     return true;
 }
 
-bool
-JSCompartment::wrapException(JSContext *cx)
-{
-    JS_ASSERT(cx->compartment == this);
-
-    if (cx->isExceptionPending()) {
-        Value v = cx->getPendingException();
-        cx->clearPendingException();
-        if (wrap(cx, &v))
-            cx->setPendingException(v);
-        return false;
-    }
-    return true;
-}
-
 #if defined JS_METHODJIT && defined JS_MONOIC
 /*
  * Check if the pool containing the code for jit should be destroyed, per the
  * heuristics in JSCompartment::sweep.
  */
 static inline bool
 ScriptPoolDestroyed(JSContext *cx, mjit::JITScript *jit,
                     uint32 releaseInterval, uint32 &counter)
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -322,17 +322,16 @@ struct JS_FRIEND_API(JSCompartment) {
 
     bool wrap(JSContext *cx, js::Value *vp);
     bool wrap(JSContext *cx, JSString **strp);
     bool wrap(JSContext *cx, JSObject **objp);
     bool wrapId(JSContext *cx, jsid *idp);
     bool wrap(JSContext *cx, js::PropertyOp *op);
     bool wrap(JSContext *cx, js::PropertyDescriptor *desc);
     bool wrap(JSContext *cx, js::AutoIdVector &props);
-    bool wrapException(JSContext *cx);
 
     void sweep(JSContext *cx, uint32 releaseInterval);
     void purge(JSContext *cx);
     void finishArenaLists();
     bool arenaListsAreEmpty();
 
   private:
     js::MathCache                *mathCache;
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -1970,109 +1970,16 @@ EmitLeaveBlock(JSContext *cx, JSCodeGene
     if (bigSuffix == JSOP_FALSE)
         return JS_FALSE;
     if (js_Emit5(cx, cg, op, count, box->index) < 0)
         return JS_FALSE;
     return bigSuffix == JSOP_NOP || js_Emit1(cx, cg, bigSuffix) >= 0;
 }
 
 /*
- * 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
- * args and vars) -- we could, but we'd take an avoidable penalty for each
- * function local not referenced by any upvar. Instead, we map such upvars
- * lazily, growing upvarMap.vector by powers of two.
- *
- * This function knows that it is called with pn pointing to a PN_NAME-arity
- * node, and cg->parser->callerFrame having a non-null fun member, and the
- * static level of cg at least one greater than the eval-calling function's
- * static level.
- */
-static bool
-MakeUpvarForEval(JSParseNode *pn, JSCodeGenerator *cg)
-{
-    JSContext *cx = cg->parser->context;
-    JSFunction *fun = cg->parser->callerFrame->fun();
-    uintN upvarLevel = fun->u.i.script->staticLevel;
-
-    JSFunctionBox *funbox = cg->funbox;
-    if (funbox) {
-        /*
-         * Treat top-level function definitions as escaping (i.e., as funargs),
-         * required since we compile each such top level function or statement
-         * and throw away the AST, so we can't yet see all funarg uses of this
-         * function being compiled (cg->funbox->object). See bug 493177.
-         */
-        if (funbox->level == fun->u.i.script->staticLevel + 1U &&
-            !(((JSFunction *) funbox->object)->flags & JSFUN_LAMBDA)) {
-            JS_ASSERT_IF(cx->options & JSOPTION_ANONFUNFIX,
-                         ((JSFunction *) funbox->object)->atom);
-            return true;
-        }
-
-        while (funbox->level >= upvarLevel) {
-            if (funbox->node->pn_dflags & PND_FUNARG)
-                return true;
-            funbox = funbox->parent;
-            if (!funbox)
-                break;
-        }
-    }
-
-    JSAtom *atom = pn->pn_atom;
-
-    uintN index;
-    BindingKind kind = fun->script()->bindings.lookup(cx, atom, &index);
-    if (kind == NONE)
-        return true;
-
-    JS_ASSERT(cg->staticLevel > upvarLevel);
-    if (cg->staticLevel >= UpvarCookie::UPVAR_LEVEL_LIMIT)
-        return true;
-
-    JSAtomListElement *ale = cg->upvarList.lookup(atom);
-    if (!ale) {
-        if (cg->inFunction() && !cg->bindings.addUpvar(cx, atom))
-            return false;
-
-        ale = cg->upvarList.add(cg->parser, atom);
-        if (!ale)
-            return false;
-        JS_ASSERT(ALE_INDEX(ale) == cg->upvarList.count - 1);
-
-        UpvarCookie *vector = cg->upvarMap.vector;
-        uint32 length = cg->upvarMap.length;
-
-        JS_ASSERT(ALE_INDEX(ale) <= length);
-        if (ALE_INDEX(ale) == length) {
-            length = 2 * JS_MAX(2, length);
-            vector = reinterpret_cast<UpvarCookie *>(cx->realloc(vector, length * sizeof *vector));
-            if (!vector)
-                return false;
-            cg->upvarMap.vector = vector;
-            cg->upvarMap.length = length;
-        }
-
-        if (kind != ARGUMENT)
-            index += fun->nargs;
-        JS_ASSERT(index < JS_BIT(16));
-
-        uintN skip = cg->staticLevel - upvarLevel;
-        vector[ALE_INDEX(ale)].set(skip, index);
-    }
-
-    pn->pn_op = JSOP_GETUPVAR;
-    pn->pn_cookie.set(cg->staticLevel, uint16(ALE_INDEX(ale)));
-    pn->pn_dflags |= PND_BOUND;
-    return true;
-}
-
-/*
  * Try to convert a *NAME op to a *GNAME op, which optimizes access to
  * undeclared globals. Return true if a conversion was made.
  *
  * This conversion is not made if we are in strict mode.  In eval code nested
  * within (strict mode) eval code, access to an undeclared "global" might
  * merely be to a binding local to that outer eval:
  *
  *   "use strict";
@@ -2227,73 +2134,21 @@ BindNameToSlot(JSContext *cx, JSCodeGene
                 if (!ale)
                     return JS_FALSE;
 
                 pn->pn_op = op;
                 pn->pn_dflags |= PND_BOUND;
                 return JS_TRUE;
             }
 
-            if (!caller->isFunctionFrame())
-                return JS_TRUE;
-
             /*
-             * Make sure the variable object used by the compiler to initialize
-             * parent links matches the caller's varobj. Compile-n-go compiler-
-             * created function objects have the top-level cg's scopeChain set
-             * as their parent by Parser::newFunction.
-             */
-            JSObject *scopeobj = cg->inFunction()
-                                 ? FUN_OBJECT(cg->fun())->getParent()
-                                 : cg->scopeChain();
-            if (scopeobj != cg->parser->callerVarObj)
-                return JS_TRUE;
-
-            /*
-             * We are compiling eval or debug script inside a function frame
-             * and the scope chain matches the function's variable object.
-             * Optimize access to function's arguments and variable and the
-             * arguments object.
+             * Out of tricks, so we must rely on PICs to optimize named
+             * accesses from direct eval called from function code.
              */
-            if (op != JSOP_NAME)
-                return JS_TRUE;
-
-            /*
-             * It is illegal to add upvars to heavyweight functions (and
-             * unnecessary, since the optimization avoids creating call
-             * objects). Take the following code as an eval string:
-             *
-             *   (function () {
-             *       $(init);
-             *       function init() {
-             *           $();
-             *       }
-             *    })();
-             *
-             * The first instance of "$" cannot be an upvar, because the
-             * outermost lambda is on "init"'s scope chain, which escapes.
-             *
-             * A similar restriction exists for upvars which do not cross
-             * eval (see the end of BindNameToSlot and bug 616762).
-             */
-            if (cg->flags & TCF_FUN_HEAVYWEIGHT)
-                return JS_TRUE;
-
-            /*
-             * Generator functions may be resumed from any call stack, which
-             * defeats the display optimization to static link searching used
-             * by JSOP_{GET,CALL}UPVAR.
-             */
-            JSFunction *fun = cg->parser->callerFrame->fun();
-            JS_ASSERT(cg->staticLevel >= fun->u.i.script->staticLevel);
-            unsigned skip = cg->staticLevel - fun->u.i.script->staticLevel;
-            if (cg->skipSpansGenerator(skip))
-                return JS_TRUE;
-
-            return MakeUpvarForEval(pn, cg);
+            return JS_TRUE;
         }
 
         /* Optimize accesses to undeclared globals. */
         if (!cg->mightAliasLocals() && !TryConvertToGname(cg, pn, &op))
             return JS_TRUE;
 
         ale = cg->atomList.add(cg->parser, atom);
         if (!ale)
@@ -2343,60 +2198,16 @@ BindNameToSlot(JSContext *cx, JSCodeGene
         pn->pn_cookie.set(cookie);
         pn->pn_dflags |= PND_BOUND;
         return JS_TRUE;
     }
 
     uint16 level = cookie.level();
     JS_ASSERT(cg->staticLevel >= level);
 
-    /*
-     * A JSDefinition witnessed as a declaration by the parser cannot be an
-     * upvar, unless it is the degenerate kind of upvar selected above (in the
-     * code before the PND_GVAR test) for the special case of compile-and-go
-     * code generated from eval called from a function, where the eval code
-     * uses local vars defined in the function. We detect this upvar-for-eval
-     * case by checking dn's op.
-     */
-    if (PN_OP(dn) == JSOP_GETUPVAR) {
-        JS_ASSERT(cg->staticLevel >= level);
-        if (op != JSOP_NAME)
-            return JS_TRUE;
-
-#ifdef DEBUG
-        JSStackFrame *caller = cg->parser->callerFrame;
-#endif
-        JS_ASSERT(caller->isScriptFrame());
-
-        JSTreeContext *tc = cg;
-        while (tc->staticLevel != level)
-            tc = tc->parent;
-
-        JSCodeGenerator *evalcg = tc->asCodeGenerator();
-        JS_ASSERT(evalcg->compileAndGo());
-        JS_ASSERT(caller->isFunctionFrame());
-        JS_ASSERT(cg->parser->callerVarObj == evalcg->scopeChain());
-
-        /*
-         * Don't generate upvars on the left side of a for loop. See
-         * bug 470758 and bug 520513.
-         */
-        if (evalcg->flags & TCF_IN_FOR_INIT)
-            return JS_TRUE;
-
-        if (cg->staticLevel == level) {
-            pn->pn_op = JSOP_GETUPVAR;
-            pn->pn_cookie = cookie;
-            pn->pn_dflags |= PND_BOUND;
-            return JS_TRUE;
-        }
-
-        return MakeUpvarForEval(pn, cg);
-    }
-
     const uintN skip = cg->staticLevel - level;
     if (skip != 0) {
         JS_ASSERT(cg->inFunction());
         JS_ASSERT_IF(cookie.slot() != UpvarCookie::CALLEE_SLOT, cg->lexdeps.lookup(atom));
         JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
         JS_ASSERT(cg->fun()->u.i.skipmin <= skip);
 
         /*
@@ -2405,39 +2216,18 @@ BindNameToSlot(JSContext *cx, JSCodeGene
          */
         if (op != JSOP_NAME)
             return JS_TRUE;
         if (level >= UpvarCookie::UPVAR_LEVEL_LIMIT)
             return JS_TRUE;
         if (cg->flags & TCF_FUN_HEAVYWEIGHT)
             return JS_TRUE;
 
-        if (cg->fun()->isFlatClosure()) {
-            op = JSOP_GETFCSLOT;
-        } else {
-            /*
-             * The function we're compiling may not be heavyweight, but if it
-             * escapes as a funarg, we can't use JSOP_GETUPVAR/JSOP_CALLUPVAR.
-             * Parser::analyzeFunctions has arranged for this function's
-             * enclosing functions to be heavyweight, so we can safely stick
-             * with JSOP_NAME/JSOP_CALLNAME.
-             */
-            if (cg->funbox->node->pn_dflags & PND_FUNARG)
-                return JS_TRUE;
-
-            /*
-             * Generator functions may be resumed from any call stack, which
-             * defeats the display optimization to static link searching used
-             * by JSOP_{GET,CALL}UPVAR.
-             */
-            if (cg->skipSpansGenerator(skip))
-                return JS_TRUE;
-
-            op = JSOP_GETUPVAR;
-        }
+        if (!cg->fun()->isFlatClosure())
+            return JS_TRUE;
 
         ale = cg->upvarList.lookup(atom);
         if (ale) {
             index = ALE_INDEX(ale);
         } else {
             if (!cg->bindings.addUpvar(cx, atom))
                 return JS_FALSE;
 
@@ -2468,17 +2258,17 @@ BindNameToSlot(JSContext *cx, JSCodeGene
                 if (tc->inFunction())
                     slot += tc->fun()->nargs;
             }
 
             JS_ASSERT(index < cg->upvarMap.length);
             vector[index].set(skip, slot);
         }
 
-        pn->pn_op = op;
+        pn->pn_op = JSOP_GETFCSLOT;
         JS_ASSERT((index & JS_BITMASK(16)) == index);
         pn->pn_cookie.set(0, index);
         pn->pn_dflags |= PND_BOUND;
         return JS_TRUE;
     }
 
     /*
      * We are compiling a function body and may be able to optimize name
@@ -2847,19 +2637,16 @@ EmitNameOp(JSContext *cx, JSCodeGenerato
             op = JSOP_CALLGLOBAL;
             break;
           case JSOP_GETARG:
             op = JSOP_CALLARG;
             break;
           case JSOP_GETLOCAL:
             op = JSOP_CALLLOCAL;
             break;
-          case JSOP_GETUPVAR:
-            op = JSOP_CALLUPVAR;
-            break;
           case JSOP_GETFCSLOT:
             op = JSOP_CALLFCSLOT;
             break;
           default:
             JS_ASSERT(op == JSOP_ARGUMENTS || op == JSOP_CALLEE);
             break;
         }
     }
@@ -6065,17 +5852,18 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
              * Top-level or called-from-a-native JS_Execute/EvaluateScript,
              * debugger, and eval frames may need the value of the ultimate
              * expression statement as the script's result, despite the fact
              * that it appears useless to the compiler.
              *
              * API users may also set the JSOPTION_NO_SCRIPT_RVAL option when
              * calling JS_Compile* to suppress JSOP_POPV.
              */
-            useful = wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
+            wantval = !(cg->flags & (TCF_IN_FUNCTION | TCF_NO_SCRIPT_RVAL));
+            useful = wantval || pn->isDirectivePrologueMember();
             if (!useful) {
                 if (!CheckSideEffects(cx, cg, pn2, &useful))
                     return JS_FALSE;
             }
 
             /*
              * Don't eliminate apparently useless expressions if they are
              * labeled expression statements.  The tc->topStmt->update test
@@ -6261,17 +6049,16 @@ js_EmitTree(JSContext *cx, JSCodeGenerat
                     } else {
                         EMIT_INDEX_OP(PN_OP(pn2), atomIndex);
                     }
                 } else if (PN_OP(pn2) == JSOP_SETNAME) {
                     if (js_Emit1(cx, cg, JSOP_DUP) < 0)
                         return JS_FALSE;
                     EMIT_INDEX_OP(JSOP_GETXPROP, atomIndex);
                 } else {
-                    JS_ASSERT(PN_OP(pn2) != JSOP_GETUPVAR);
                     EMIT_UINT16_IMM_OP((PN_OP(pn2) == JSOP_SETGNAME)
                                        ? JSOP_GETGNAME
                                        : (PN_OP(pn2) == JSOP_SETGLOBAL)
                                        ? JSOP_GETGLOBAL
                                        : (PN_OP(pn2) == JSOP_SETARG)
                                        ? JSOP_GETARG
                                        : JSOP_GETLOCAL,
                                        atomIndex);
--- a/js/src/jsemit.h
+++ b/js/src/jsemit.h
@@ -229,19 +229,19 @@ struct JSStmtInfo {
  * "Module pattern", i.e., a lambda that is immediately applied and the whole
  * of an expression statement.
  */
 #define TCF_FUN_MODULE_PATTERN 0x200000
 
 /*
  * Flag to prevent a non-escaping function from being optimized into a null
  * closure (i.e., a closure that needs only its global object for free variable
- * resolution, thanks to JSOP_{GET,CALL}UPVAR), because this function contains
- * a closure that needs one or more scope objects surrounding it (i.e., Call
- * object for a heavyweight outer function). See bug 560234.
+ * resolution), because this function contains a closure that needs one or more
+ * scope objects surrounding it (i.e., a Call object for an outer heavyweight
+ * function). See bug 560234.
  */
 #define TCF_FUN_ENTRAINS_SCOPES 0x400000
 
 /* The function calls 'eval'. */
 #define TCF_FUN_CALLS_EVAL       0x800000
 
 /* The function mutates a positional (non-destructuring) parameter. */
 #define TCF_FUN_MUTATES_PARAMETER 0x1000000
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -450,18 +450,16 @@ WrapEscapingClosure(JSContext *cx, JSSta
 
         /*
          * Rewrite JSOP_{GET,CALL}FCSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
          * case where fun is an escaping flat closure. This works because the
          * UPVAR and FCSLOT 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_GETFCSLOT:      *pc = JSOP_GETUPVAR_DBG; break;
           case JSOP_CALLFCSLOT:     *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;
@@ -1072,70 +1070,85 @@ CopyValuesToCallObject(JSObject &callobj
     memcpy(base + nargs, slots, nvars * sizeof(Value));
 }
 
 void
 js_PutCallObject(JSContext *cx, JSStackFrame *fp)
 {
     JSObject &callobj = fp->callObj();
 
+    /*
+     * Strict mode eval frames have Call objects to put.  Normal eval frames
+     * never put a Call object.
+     */
+    JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
+
     /* Get the arguments object to snapshot fp's actual argument values. */
     if (fp->hasArgsObj()) {
         if (!fp->hasOverriddenArgs())
             callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
         js_PutArgsObject(cx, fp);
     }
 
-    JSFunction *fun = fp->fun();
-    JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
-
-    Bindings &bindings = fun->script()->bindings;
-    uintN n = bindings.countArgsAndVars();
-
-    if (n != 0) {
-        JS_ASSERT(JSFunction::CLASS_RESERVED_SLOTS + n <= callobj.numSlots());
-
-        uint32 nvars = bindings.countVars();
-        uint32 nargs = bindings.countArgs();
-        JS_ASSERT(fun->nargs == nargs);
-        JS_ASSERT(nvars + nargs == n);
-
-        JSScript *script = fun->script();
-        if (script->usesEval
+    JSScript *script = fp->script();
+    Bindings &bindings = script->bindings;
+
+    if (callobj.callIsForEval()) {
+        JS_ASSERT(script->strictModeCode);
+        JS_ASSERT(bindings.countArgs() == 0);
+
+        /* This could be optimized as below, but keep it simple for now. */
+        CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
+    } else {
+        JSFunction *fun = fp->fun();
+        JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
+        JS_ASSERT(script == fun->script());
+
+        if (uintN n = bindings.countArgsAndVars()) {
+            JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
+
+            uint32 nvars = bindings.countVars();
+            uint32 nargs = bindings.countArgs();
+            JS_ASSERT(fun->nargs == nargs);
+            JS_ASSERT(nvars + nargs == n);
+
+            JSScript *script = fun->script();
+            if (script->usesEval
 #ifdef JS_METHODJIT
-            || script->debugMode
+                || script->debugMode
 #endif
-            ) {
-            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.
-             */
-            uint32 nclosed = script->nClosedArgs;
-            for (uint32 i = 0; i < nclosed; i++) {
-                uint32 e = script->getClosedArg(i);
-                callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
-            }
-
-            nclosed = script->nClosedVars;
-            for (uint32 i = 0; i < nclosed; i++) {
-                uint32 e = script->getClosedVar(i);
-                callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
+                ) {
+                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.
+                 */
+                uint32 nclosed = script->nClosedArgs;
+                for (uint32 i = 0; i < nclosed; i++) {
+                    uint32 e = script->getClosedArg(i);
+                    callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
+                }
+
+                nclosed = script->nClosedVars;
+                for (uint32 i = 0; i < nclosed; i++) {
+                    uint32 e = script->getClosedVar(i);
+                    callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
+                }
             }
         }
-    }
-
-    /* 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);
-        env->setPrivate(NULL);
+
+        /* 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);
+            env->setPrivate(NULL);
+        }
     }
 
     callobj.setPrivate(NULL);
     fp->clearCallObj();
 }
 
 JSBool JS_FASTCALL
 js_PutCallObjectOnTrace(JSContext *cx, JSObject *callobj, uint32 nargs, Value *argv,
@@ -1206,40 +1219,35 @@ SetCallArg(JSContext *cx, JSObject *obj,
         argp = &obj->callObjArg(i);
 
     GC_POKE(cx, *argp);
     *argp = *vp;
     return true;
 }
 
 JSBool
-GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
+GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    JSObject *callee = obj->getCallObjCallee();
-    JS_ASSERT(callee);
-
-    *vp = callee->getFlatClosureUpvar(i);
+    *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
     return true;
 }
 
 JSBool
-SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
+SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    JSObject *callee = obj->getCallObjCallee();
-    JS_ASSERT(callee);
-
-    Value *upvarp = &callee->getFlatClosureUpvar(i);
-    GC_POKE(cx, *upvarp);
-    *upvarp = *vp;
+    Value *up = &obj->getCallObjCallee()->getFlatClosureUpvar(i);
+
+    GC_POKE(cx, *up);
+    *up = *vp;
     return true;
 }
 
 JSBool
 GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
@@ -1355,27 +1363,26 @@ call_resolve(JSContext *cx, JSObject *ob
     /* Control flow reaches here only if id was not resolved. */
     return true;
 }
 
 static void
 call_trace(JSTracer *trc, JSObject *obj)
 {
     JS_ASSERT(obj->isCall());
-    JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
-    if (fp) {
+    if (JSStackFrame *fp = obj->maybeCallObjStackFrame()) {
         /*
          * FIXME: Hide copies of stack values rooted by fp from the Cycle
          * Collector, which currently lacks a non-stub Unlink implementation
          * for JS objects (including Call objects), so is unable to collect
          * cycles involving Call objects whose frames are active without this
          * hiding hack.
          */
         uintN first = JSObject::CALL_RESERVED_SLOTS;
-        uintN count = fp->fun()->script()->bindings.countArgsAndVars();
+        uintN count = fp->script()->bindings.countArgsAndVars();
 
         JS_ASSERT(obj->numSlots() >= first + count);
         SetValueRangeToUndefined(obj->getSlots() + first, count);
     }
 
     MaybeMarkGenerator(trc, obj);
 }
 
@@ -2775,26 +2782,30 @@ js_AllocFlatClosure(JSContext *cx, JSFun
 
     closure->setFlatClosureUpvars(upvars);
     return closure;
 }
 
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_AllocFlatClosure,
                      CONTEXT, FUNCTION, OBJECT, 0, nanojit::ACCSET_STORE_ANY)
 
-JS_REQUIRES_STACK JSObject *
+JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen)
 {
     /*
-     * Flat closures can be partial, they may need to search enclosing scope
-     * objects via JSOP_NAME, etc.
+     * Flat closures cannot yet be partial, that is, all upvars must be copied,
+     * or the closure won't be flattened. Therefore they do not need to search
+     * enclosing scope objects via JSOP_NAME, etc.
+     *
+     * FIXME: bug 545759 proposes to enable partial flat closures. Fixing this
+     * bug requires a GetScopeChainFast call here, along with JS_REQUIRES_STACK
+     * annotations on this function's prototype and definition.
      */
-    JSObject *scopeChain = GetScopeChainFast(cx, cx->fp(), op, oplen);
-    if (!scopeChain)
-        return NULL;
+    VOUCH_DOES_NOT_REQUIRE_STACK();
+    JSObject *scopeChain = &cx->fp()->scopeChain();
 
     JSObject *closure = js_AllocFlatClosure(cx, fun, scopeChain);
     if (!closure || !fun->script()->bindings.hasUpvars())
         return closure;
 
     Value *upvars = closure->getFlatClosureUpvars();
     uintN level = fun->u.i.script->staticLevel;
     JSUpvarArray *uva = fun->script()->upvars();
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -462,17 +462,17 @@ CloneFunctionObject(JSContext *cx, JSFun
     if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
         return NULL;
     return js_CloneFunctionObject(cx, fun, parent, proto);
 }
 
 extern JSObject * JS_FASTCALL
 js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);
 
-extern JS_REQUIRES_STACK JSObject *
+extern JSObject *
 js_NewFlatClosure(JSContext *cx, JSFunction *fun, JSOp op, size_t oplen);
 
 extern JS_REQUIRES_STACK JSObject *
 js_NewDebuggableFlatClosure(JSContext *cx, JSFunction *fun);
 
 extern JSFunction *
 js_DefineFunction(JSContext *cx, JSObject *obj, jsid id, js::Native native,
                   uintN nargs, uintN flags);
@@ -521,26 +521,26 @@ GetCallVar(JSContext *cx, JSObject *obj,
 /*
  * Slower version of js_GetCallVar used when call_resolve detects an attempt to
  * leak an optimized closure via indirect or debugger eval.
  */
 extern JSBool
 GetCallVarChecked(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
-GetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
+GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
 SetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
 SetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
-SetFlatUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
+SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 } // namespace js
 
 extern JSBool
 js_GetArgsValue(JSContext *cx, JSStackFrame *fp, js::Value *vp);
 
 extern JSBool
 js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, js::Value *vp);
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -961,33 +961,32 @@ Execute(JSContext *cx, JSObject *chain, 
         JSObject *thisp = chain->thisObject(cx);
         if (!thisp)
             return false;
         frame.fp()->globalThis().setObject(*thisp);
 
         initialVarObj = (cx->options & JSOPTION_VAROBJFIX) ? chain->getGlobal() : chain;
     }
 
-#if 0 /* to be reenabled shortly when this works */
     /*
      * Strict mode eval code receives its own, fresh lexical environment; thus
      * strict mode eval can't mutate its calling frame's binding set.
      */
-    if (script->strictModeCode) {
+    if ((flags & JSFRAME_EVAL) && script->strictModeCode) {
+        AutoScriptRooter root(cx, script);
         initialVarObj = NewCallObject(cx, &script->bindings, *initialVarObj, NULL);
         if (!initialVarObj)
             return false;
         initialVarObj->setPrivate(frame.fp());
 
         /* Clear the Call object propagated from the previous frame, if any. */
         if (frame.fp()->hasCallObj())
             frame.fp()->clearCallObj();
         frame.fp()->setScopeChainAndCallObj(*initialVarObj);
     }
-#endif
     JS_ASSERT(!initialVarObj->getOps()->defineProperty);
 
 #if JS_HAS_SHARP_VARS
     JS_STATIC_ASSERT(SHARP_NSLOTS == 2);
     if (script->hasSharps) {
         JS_ASSERT(script->nfixed >= SHARP_NSLOTS);
         Value *sharps = &frame.fp()->slots()[script->nfixed - SHARP_NSLOTS];
         if (prev && prev->script()->hasSharps) {
@@ -2127,19 +2126,18 @@ AssertValidPropertyCacheHit(JSContext *c
 # define ASSERT_VALID_PROPERTY_CACHE_HIT(pcoff,obj,pobj,entry) ((void) 0)
 #endif
 
 /*
  * 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_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
+JS_STATIC_ASSERT(JSOP_GETUPVAR_DBG_LENGTH == JSOP_GETFCSLOT_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_GETFCSLOT_LENGTH == JSOP_CALLFCSLOT_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.
@@ -5285,32 +5283,16 @@ END_CASE(JSOP_CALLLOCAL)
 BEGIN_CASE(JSOP_SETLOCAL)
 {
     uint32 slot = GET_SLOTNO(regs.pc);
     JS_ASSERT(slot < script->nslots);
     regs.fp->slots()[slot] = regs.sp[-1];
 }
 END_SET_CASE(JSOP_SETLOCAL)
 
-BEGIN_CASE(JSOP_GETUPVAR)
-BEGIN_CASE(JSOP_CALLUPVAR)
-{
-    JSUpvarArray *uva = script->upvars();
-
-    uintN index = GET_UINT16(regs.pc);
-    JS_ASSERT(index < uva->length);
-
-    const Value &rval = GetUpvar(cx, script->staticLevel, uva->vector[index]);
-    PUSH_COPY(rval);
-
-    if (op == JSOP_CALLUPVAR)
-        PUSH_UNDEFINED();
-}
-END_CASE(JSOP_GETUPVAR)
-
 BEGIN_CASE(JSOP_GETUPVAR_DBG)
 BEGIN_CASE(JSOP_CALLUPVAR_DBG)
 {
     JSFunction *fun = regs.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. */
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -717,23 +717,41 @@ ScriptEpilogue(JSContext *cx, JSStackFra
 
     JSInterpreterHook hook =
         fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook;
 
     void* hookData;
     if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
         hook(cx, fp, JS_FALSE, &ok, hookData);
 
-    /*
-     * An eval frame's parent owns its activation objects. A yielding frame's
-     * activation objects are transferred to the floating frame, stored in the
-     * generator.
-     */
-    if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
-        PutActivationObjects(cx, fp);
+    if (fp->isEvalFrame()) {
+        /*
+         * The parent (ancestor for nested eval) of a non-strict eval frame
+         * owns its activation objects. Strict mode eval frames own their own
+         * Call objects but never have an arguments object (the first non-eval
+         * parent frame has it).
+         */
+        if (fp->script()->strictModeCode) {
+            JS_ASSERT(!fp->isYielding());
+            JS_ASSERT(!fp->hasArgsObj());
+            JS_ASSERT(fp->hasCallObj());
+            JS_ASSERT(fp->callObj().callIsForEval());
+            js_PutCallObject(cx, fp);
+        }
+    } else {
+        /*
+         * Otherwise only function frames have activation objects. A yielding
+         * frame's activation objects are transferred to the floating frame,
+         * stored in the generator, and thus need not be synced.
+         */
+        if (fp->isFunctionFrame() && !fp->isYielding()) {
+            JS_ASSERT_IF(fp->hasCallObj(), !fp->callObj().callIsForEval());
+            PutActivationObjects(cx, fp);
+        }
+    }
 
     /*
      * If inline-constructing, replace primitive rval with the new object
      * passed in via |this|, and instrument this constructor invocation.
      */
     if (fp->isConstructing() && ok) {
         if (fp->returnValue().isPrimitive())
             fp->setReturnValue(ObjectValue(fp->constructorThis()));
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -892,16 +892,19 @@ struct JSObject : js::gc::Cell {
      */
     static const uint32 JSSLOT_CALL_CALLEE = 0;
     static const uint32 JSSLOT_CALL_ARGUMENTS = 1;
 
   public:
     /* Number of reserved slots. */
     static const uint32 CALL_RESERVED_SLOTS = 2;
 
+    /* True if this is for a strict mode eval frame or for a function call. */
+    inline bool callIsForEval() const;
+
     /* The stack frame for this Call object, if the frame is still active. */
     inline JSStackFrame *maybeCallObjStackFrame() const;
 
     /*
      * The callee function if this Call object was created for a function
      * invocation, or null if it was created for a strict mode eval frame.
      */
     inline JSObject *getCallObjCallee() const;
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -427,16 +427,26 @@ JSObject::addressOfArgsElement(uint32 i)
 inline void
 JSObject::setArgsElement(uint32 i, const js::Value &v)
 {
     JS_ASSERT(isArguments());
     JS_ASSERT(i < getArgsInitialLength());
     getArgsData()->slots[i] = v;
 }
 
+inline bool
+JSObject::callIsForEval() const
+{
+    JS_ASSERT(isCall());
+    JS_ASSERT(getSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
+    JS_ASSERT_IF(getSlot(JSSLOT_CALL_CALLEE).isObject(),
+                 getSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
+    return getSlot(JSSLOT_CALL_CALLEE).isNull();
+}
+
 inline JSStackFrame *
 JSObject::maybeCallObjStackFrame() const
 {
     JS_ASSERT(isCall());
     return reinterpret_cast<JSStackFrame *>(getPrivate());
 }
 
 inline void
@@ -460,25 +470,25 @@ JSObject::getCallObjCalleeFunction() con
     JS_ASSERT(isCall());
     return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
 }
 
 inline const js::Value &
 JSObject::getCallObjArguments() const
 {
     JS_ASSERT(isCall());
-    JS_ASSERT(getCallObjCallee() != NULL);
+    JS_ASSERT(!callIsForEval());
     return getSlot(JSSLOT_CALL_ARGUMENTS);
 }
 
 inline void
 JSObject::setCallObjArguments(const js::Value &v)
 {
     JS_ASSERT(isCall());
-    JS_ASSERT(getCallObjCallee() != NULL);
+    JS_ASSERT(!callIsForEval());
     setSlot(JSSLOT_CALL_ARGUMENTS, v);
 }
 
 inline const js::Value &
 JSObject::callObjArg(uintN i) const
 {
     JS_ASSERT(isCall());
     JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -2878,18 +2878,16 @@ Decompile(SprintStack *ss, jsbytecode *p
                 top -= depth;
                 ss->top = top;
                 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_GETFCSLOT:
               case JSOP_CALLFCSLOT:
               {
                 if (!jp->fun) {
                     JS_ASSERT(jp->script->savedCallerFun);
                     jp->fun = jp->script->getFunction(0);
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -438,25 +438,29 @@ OPDEF(JSOP_ENDFILTER,     177,"endfilter
 OPDEF(JSOP_TOXML,         178,"toxml",      NULL,     1,  1,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_TOXMLLIST,     179,"toxmllist",  NULL,     1,  1,  1, 19,  JOF_BYTE)
 OPDEF(JSOP_XMLTAGEXPR,    180,"xmltagexpr", NULL,     1,  1,  1,  0,  JOF_BYTE)
 OPDEF(JSOP_XMLELTEXPR,    181,"xmleltexpr", NULL,     1,  1,  1,  0,  JOF_BYTE)
 OPDEF(JSOP_NOTRACE,       182,"notrace",    NULL,     3,  0,  0,  0,  JOF_UINT16)
 OPDEF(JSOP_XMLCDATA,      183,"xmlcdata",   NULL,     3,  0,  1, 19,  JOF_ATOM)
 OPDEF(JSOP_XMLCOMMENT,    184,"xmlcomment", NULL,     3,  0,  1, 19,  JOF_ATOM)
 OPDEF(JSOP_XMLPI,         185,"xmlpi",      NULL,     3,  1,  1, 19,  JOF_ATOM)
-OPDEF(JSOP_CALLPROP,      186,"callprop",   NULL,     3,  1,  2, 18,  JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
+OPDEF(JSOP_DELDESC,       186,"deldesc",    NULL,     1,  2,  1, 15,  JOF_BYTE|JOF_ELEM|JOF_DEL)
+
+OPDEF(JSOP_CALLPROP,      187,"callprop",   NULL,     3,  1,  2, 18,  JOF_ATOM|JOF_PROP|JOF_CALLOP|JOF_TMPSLOT3)
 
 /*
- * Get a display (free) variable from the closure's reserved slots.
+ * These opcodes contain a reference to the current blockChain object.
+ * They are emitted directly after instructions, such as DEFFUN, that need fast access to
+ * the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
+ * does not permit NULL object references, since it stores an index into a table of
+ * objects.
  */
-OPDEF(JSOP_GETUPVAR,      187,"getupvar",   NULL,     3,  0,  1, 19,  JOF_UINT16|JOF_NAME)
-OPDEF(JSOP_CALLUPVAR,     188,"callupvar",  NULL,     3,  0,  2, 19,  JOF_UINT16|JOF_NAME|JOF_CALLOP)
-
-OPDEF(JSOP_DELDESC,       189,"deldesc",    NULL,     1,  2,  1, 15,  JOF_BYTE|JOF_ELEM|JOF_DEL)
+OPDEF(JSOP_BLOCKCHAIN,    188,"blockchain",    NULL,  3,  0,  0,  0, JOF_OBJECT)
+OPDEF(JSOP_NULLBLOCKCHAIN,189,"nullblockchain",NULL,  1,  0,  0,  0, JOF_BYTE)
 
 /*
  * Opcode to hold 24-bit immediate integer operands.
  */
 OPDEF(JSOP_UINT24,        190,"uint24",     NULL,     4,  0,  1, 16,  JOF_UINT24)
 
 /*
  * Opcodes to allow 24-bit atom or object indexes. Whenever an index exceeds
@@ -581,17 +585,17 @@ OPDEF(JSOP_LAMBDA_FC,     227,"lambda_fc
  * (the decompilation of the primitive value).
  */
 OPDEF(JSOP_OBJTOP,        228,"objtop",        NULL,  3,  0,  0,  0,  JOF_UINT16)
 
 /* This opcode stores an index that is unique to the given loop. */
 OPDEF(JSOP_TRACE,         229, "trace",         NULL,  3,  0,  0,  0,  JOF_UINT16)
 
 /*
- * Debugger versions of JSOP_{GET,CALL}UPVAR and the flat closure (_FC) ops.
+ * Debugger versions of the flat closure (_FC) ops.
  */
 OPDEF(JSOP_GETUPVAR_DBG,  230,"getupvar_dbg",  NULL,  3,  0,  1, 19,  JOF_UINT16|JOF_NAME)
 OPDEF(JSOP_CALLUPVAR_DBG, 231,"callupvar_dbg", NULL,  3,  0,  2, 19,  JOF_UINT16|JOF_NAME|JOF_CALLOP)
 OPDEF(JSOP_DEFFUN_DBGFC,     232,"deffun_dbgfc",     NULL,  3,  0,  0,  0,  JOF_OBJECT|JOF_DECLARING)
 OPDEF(JSOP_DEFLOCALFUN_DBGFC,233,"deflocalfun_dbgfc",NULL,  5,  0,  0,  0,  JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT)
 OPDEF(JSOP_LAMBDA_DBGFC,     234,"lambda_dbgfc",     NULL,  3,  0,  1, 19,  JOF_OBJECT)
 
 /*
@@ -609,20 +613,10 @@ OPDEF(JSOP_GETGLOBAL,     240,"getglobal
 OPDEF(JSOP_SETGLOBAL,     241,"setglobal",     NULL,  3,  1,  1,  3,  JOF_GLOBAL|JOF_NAME|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INCGLOBAL,     242,"incglobal",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_TMPSLOT2)
 OPDEF(JSOP_DECGLOBAL,     243,"decglobal",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_TMPSLOT2)
 OPDEF(JSOP_GLOBALINC,     244,"globalinc",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT2)
 OPDEF(JSOP_GLOBALDEC,     245,"globaldec",     NULL,  3,  0,  1, 15,  JOF_GLOBAL|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT2)
 OPDEF(JSOP_CALLGLOBAL,    246,"callglobal",    NULL,  3,  0,  2, 19,  JOF_GLOBAL|JOF_NAME|JOF_CALLOP)
 OPDEF(JSOP_FORGLOBAL,     247,"forglobal",     NULL,  3,  1,  1, 19,  JOF_GLOBAL|JOF_NAME|JOF_FOR|JOF_TMPSLOT)
 
-/*
- * These opcodes contain a reference to the current blockChain object.
- * They are emitted directly after instructions, such as DEFFUN, that need fast access to
- * the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
- * does not permit NULL object references, since it stores an index into a table of
- * objects.
- */
-OPDEF(JSOP_BLOCKCHAIN,    248,"blockchain",    NULL,  3,  0,  0,  0, JOF_OBJECT)
-OPDEF(JSOP_NULLBLOCKCHAIN,249,"nullblockchain",NULL,  1,  0,  0,  0, JOF_BYTE)
-
 /* Like JSOP_FUNAPPLY but for f.call instead of f.apply. */
-OPDEF(JSOP_FUNCALL,       250,"funcall",       NULL,  3, -1,  1, 18,  JOF_UINT16|JOF_INVOKE)
+OPDEF(JSOP_FUNCALL,       248,"funcall",       NULL,  3, -1,  1, 18,  JOF_UINT16|JOF_INVOKE)
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1904,18 +1904,18 @@ Parser::analyzeFunctions(JSFunctionBox *
  *           function h() { return a; }
  *           return g() + h();
  *       }
  *   }
  *
  * but without this extra marking phase, function g will not be marked as a
  * funarg since it is called from within its parent scope. But g reaches up to
  * f's parameter p, so if o_m escapes f's activation scope, g does too and
- * cannot use JSOP_GETUPVAR to reach p. In contast function h neither escapes
- * nor uses an upvar "above" o_m's level.
+ * cannot assume that p's stack slot is still alive. 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
@@ -2045,19 +2045,18 @@ Parser::markFunArgs(JSFunctionBox *funbo
                 JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
 
                 if (!lexdep->isFreeVar() &&
                     !lexdep->isFunArg() &&
                     (lexdep->kind() == JSDefinition::FUNCTION ||
                      PN_OP(lexdep) == JSOP_CALLEE)) {
                     /*
                      * 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.
+                     * function (i.e., as a funarg), because it is used from
+                     * another funarg.
                      *
                      * 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;
@@ -2367,72 +2366,37 @@ Parser::setFunctionKinds(JSFunctionBox *
             JSAtomList upvars(pn->pn_names);
             JS_ASSERT(upvars.count != 0);
 
             JSAtomListIterator iter(&upvars);
             JSAtomListElement *ale;
 
             if (!fn->isFunArg()) {
                 /*
-                 * This function is Algol-like, it never escapes. So long as it
-                 * does not assign to outer variables, it needs only an upvars
-                 * array in its script and JSOP_{GET,CALL}UPVAR opcodes in its
-                 * bytecode to reach up the frame stack at runtime based on
-                 * those upvars' cookies.
+                 * This function is Algol-like, it never escapes.
                  *
-                 * Any assignments to upvars from functions called by this one
-                 * will be coherent because of the JSOP_{GET,CALL}UPVAR ops,
-                 * which load from stack homes when interpreting or from native
-                 * stack slots when executing a trace.
-                 *
-                 * We could add JSOP_SETUPVAR, etc., but it is uncommon for a
-                 * nested function to assign to an outer lexical variable, so
-                 * we defer adding yet more code footprint in the absence of
-                 * evidence motivating these opcodes.
-                 */
-                bool mutation = !!(funbox->tcflags & TCF_FUN_SETS_OUTER_NAME);
-                uintN nupvars = 0;
-
-                /*
                  * Check that at least one outer lexical binding was assigned
                  * to (global variables don't count). This is conservative: we
                  * could limit assignments to those in the current function,
                  * but that's too much work. As with flat closures (handled
                  * below), we optimize for the case where outer bindings are
                  * not reassigned anywhere.
                  */
                 while ((ale = iter()) != NULL) {
                     JSDefinition *lexdep = ALE_DEFN(ale)->resolve();
 
                     if (!lexdep->isFreeVar()) {
                         JS_ASSERT(lexdep->frameLevel() <= funbox->level);
-                        ++nupvars;
-                        if (lexdep->isAssigned())
-                            break;
+                        break;
                     }
                 }
-                if (!ale)
-                    mutation = false;
-
-                if (nupvars == 0) {
+
+                if (!ale) {
                     FUN_METER(onlyfreevar);
                     FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
-                } else if (!mutation &&
-                           !(funbox->tcflags & (TCF_FUN_IS_GENERATOR | TCF_FUN_ENTRAINS_SCOPES))) {
-                    /*
-                     * Algol-like functions can read upvars using the dynamic
-                     * link (cx->fp/fp->down), optimized using the cx->display
-                     * lookup table indexed by static level. They do not need
-                     * to entrain and search their environment objects.
-                     */
-                    FUN_METER(display);
-                    FUN_SET_KIND(fun, JSFUN_NULL_CLOSURE);
-                } else {
-                    if (!(funbox->tcflags & TCF_FUN_IS_GENERATOR))
-                        FUN_METER(setupvar);
                 }
             } else {
                 uintN nupvars = 0, nflattened = 0;
 
                 /*
                  * For each lexical dependency from this closure to an outer
                  * binding, analyze whether it is safe to copy the binding's
                  * value into a flat closure slot when the closure is formed.
@@ -2897,18 +2861,18 @@ Parser::functionDef(JSAtom *funAtom, Fun
     tokenStream.mungeCurrentToken(TOK_FUNCTION, JSOP_NOP);
     JSParseNode *pn = FunctionNode::create(tc);
     if (!pn)
         return NULL;
     pn->pn_body = NULL;
     pn->pn_cookie.makeFree();
 
     /*
-     * If a lambda, give up on JSOP_{GET,CALL}UPVAR usage unless this function
-     * is immediately applied (we clear PND_FUNARG if so -- see memberExpr).
+     * If a lambda, mark this function as escaping (as a "funarg") unless it is
+     * immediately applied (we clear PND_FUNARG if so -- see memberExpr).
      *
      * Treat function sub-statements (non-lambda, non-body-level functions) as
      * escaping funargs, since we can't statically analyze their definitions
      * and uses.
      */
     bool bodyLevel = tc->atBodyLevel();
     pn->pn_dflags = (lambda || !bodyLevel) ? PND_FUNARG : 0;
 
@@ -3239,16 +3203,23 @@ Parser::functionStmt()
     } else {
         if (context->options & JSOPTION_ANONFUNFIX) {
             /* Extension: accept unnamed function expressions as statements. */
             reportErrorNumber(NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR);
             return NULL;
         }
         tokenStream.ungetToken();
     }
+
+    /* We forbid function statements in strict mode code. */
+    if (!tc->atBodyLevel() && tc->inStrictMode()) {
+        reportErrorNumber(NULL, JSREPORT_STRICT_MODE_ERROR, JSMSG_STRICT_FUNCTION_STATEMENT);
+        return NULL;
+    }
+
     return functionDef(name, GENERAL, 0);
 }
 
 JSParseNode *
 Parser::functionExpr()
 {
     JSAtom *name = NULL;
     if (tokenStream.getToken(TSF_KEYWORD_IS_NAME) == TOK_NAME)
--- a/js/src/jsparse.h
+++ b/js/src/jsparse.h
@@ -827,22 +827,18 @@ struct LexicalScopeNode : public JSParse
  * Another important subset dominance relation: ... { var x = ...; ... x ... }
  * where x is not assigned after initialization and not used outside the block.
  * This style is common in the absence of 'let'. Even though the var x is not
  * at top level, we can tell its initialization dominates all uses cheaply,
  * because the above one-pass algorithm sees the definition before any uses,
  * and because all uses are contained in the same block as the definition.
  *
  * We also analyze function uses to flag upward/downward funargs, optimizing
- * Algol-like (not passed as funargs, only ever called) lightweight functions
- * using cx->display. See JSOP_{GET,CALL}UPVAR.
- *
- * This means that closure optimizations may be frustrated by with, eval, or
- * assignment to an outer var. Such hard cases require heavyweight functions
- * and JSOP_NAME, etc.
+ * those lambdas that post-dominate their upvars inevitable only assignments or
+ * initializations as flat closures (after Chez Scheme's display closures).
  */
 #define dn_uses         pn_link
 
 struct JSDefinition : public JSParseNode
 {
     /*
      * We store definition pointers in PN_NAMESET JSAtomLists in the AST, but
      * due to redefinition these nodes may become uses of other definitions.
--- a/js/src/jsregexp.cpp
+++ b/js/src/jsregexp.cpp
@@ -53,16 +53,18 @@
 #include "jsobj.h"
 #include "jsregexp.h"
 #include "jsstr.h"
 #include "jsvector.h"
 
 #include "jsobjinlines.h"
 #include "jsregexpinlines.h"
 
+#include "yarr/RegexParser.h"
+
 #ifdef JS_TRACER
 #include "jstracer.h"
 using namespace avmplus;
 using namespace nanojit;
 #endif
 
 using namespace js;
 using namespace js::gc;
@@ -179,46 +181,33 @@ js_ObjectIsRegExp(JSObject *obj)
 
 /*
  * js::RegExp
  */
 
 void
 RegExp::handleYarrError(JSContext *cx, int error)
 {
-    /* Hack: duplicated from yarr/yarr/RegexParser.h */
-    enum ErrorCode {
-        NoError,
-        PatternTooLarge,
-        QuantifierOutOfOrder,
-        QuantifierWithoutAtom,
-        MissingParentheses,
-        ParenthesesUnmatched,
-        ParenthesesTypeInvalid,     /* "(?" with bad next char or end of pattern. */
-        CharacterClassUnmatched,
-        CharacterClassOutOfOrder,
-        QuantifierTooLarge,
-        EscapeUnterminated
-    };
     switch (error) {
-      case NoError:
+      case JSC::Yarr::NoError:
         JS_NOT_REACHED("Precondition violation: an error must have occurred.");
         return;
 #define COMPILE_EMSG(__code, __msg) \
-      case __code: \
+      case JSC::Yarr::__code: \
         JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
         return
       COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
       COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER);
       COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
       COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
       COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
-      COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER);
+      COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
       COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
       COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
+      COMPILE_EMSG(CharacterClassRangeSingleChar, JSMSG_BAD_CLASS_RANGE);
       COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
       COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
 #undef COMPILE_EMSG
       default:
         JS_NOT_REACHED("Precondition violation: unknown Yarr error code.");
     }
 }
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -480,20 +480,20 @@ Shape::getChild(JSContext *cx, const js:
             JS_ASSERT(oldShape == newShape->parent);
             if (table) {
                 /* Add newShape to the property table. */
                 METER(searches);
                 Shape **spp = table->search(newShape->id, true);
 
                 /*
                  * Beware duplicate formal parameters, allowed by ECMA-262 in
-                 * non-strict mode. Otherwise we know that JSFunction::addLocal
-                 * (our caller) won't pass an id already in the table to us. In
-                 * the case of duplicate formals, the last one wins, so while
-                 * we must not overcount entries, we must store newShape.
+                 * non-strict mode. Otherwise we know that Bindings::add (our
+                 * caller) won't pass an id already in the table to us. In the
+                 * case of duplicate formals, the last one wins, so while we
+                 * must not overcount entries, we must store newShape.
                  */
                 if (!SHAPE_FETCH(spp))
                     ++table->entryCount;
                 SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
 
                 /* Hand the table off from oldShape to newShape. */
                 oldShape->setTable(NULL);
                 newShape->setTable(table);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -86,17 +86,17 @@ Bindings::lookup(JSContext *cx, JSAtom *
     if (!shape)
         return NONE;
 
     if (indexp)
         *indexp = shape->shortid;
 
     if (shape->getter() == GetCallArg)
         return ARGUMENT;
-    if (shape->getter() == GetFlatUpvar)
+    if (shape->getter() == GetCallUpvar)
         return UPVAR;
 
     return shape->writable() ? VARIABLE : CONSTANT;
 }
 
 bool
 Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
 {
@@ -117,18 +117,18 @@ Bindings::add(JSContext *cx, JSAtom *nam
         JS_ASSERT(nvars == 0);
         JS_ASSERT(nupvars == 0);
         indexp = &nargs;
         getter = GetCallArg;
         setter = SetCallArg;
         slot += nargs;
     } else if (kind == UPVAR) {
         indexp = &nupvars;
-        getter = GetFlatUpvar;
-        setter = SetFlatUpvar;
+        getter = GetCallUpvar;
+        setter = SetCallUpvar;
         slot = SHAPE_INVALID_SLOT;
     } else {
         JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
         JS_ASSERT(nupvars == 0);
 
         indexp = &nvars;
         getter = GetCallVar;
         setter = SetCallVar;
@@ -213,17 +213,17 @@ Bindings::getLocalNameArray(JSContext *c
 
     for (Shape::Range r = lastBinding; !r.empty(); r.popFront()) {
         const Shape &shape = r.front();
         uintN index = uint16(shape.shortid);
         jsuword constFlag = 0;
 
         if (shape.getter() == GetCallArg) {
             JS_ASSERT(index < nargs);
-        } else if (shape.getter() == GetFlatUpvar) {
+        } else if (shape.getter() == GetCallUpvar) {
             JS_ASSERT(index < nupvars);
             index += nargs + nvars;
         } else {
             JS_ASSERT(index < nvars);
             index += nargs;
             if (!shape.writable())
                 constFlag = 1;
         }
@@ -262,17 +262,17 @@ Bindings::lastArgument() const
 
 const Shape *
 Bindings::lastVariable() const
 {
     JS_ASSERT(lastBinding);
 
     const js::Shape *shape = lastUpvar();
     if (nupvars > 0) {
-        while (shape->getter() == GetFlatUpvar)
+        while (shape->getter() == GetCallUpvar)
             shape = shape->previous();
     }
     return shape;
 }
 
 const Shape *
 Bindings::lastUpvar() const
 {
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -68,17 +68,17 @@ namespace js {
  * quantity. We could probably document that through use of more types at some
  * point in the future.
  *
  * Existing XDR code wants this to be backed by a 32b integer for serialization,
  * so we oblige.
  *
  * TODO: consider giving more bits to the slot value and takings ome from the level.
  */
-class UpvarCookie 
+class UpvarCookie
 {
     uint32 value;
 
     static const uint32 FREE_VALUE = 0xfffffffful;
 
     void checkInvariants() {
         JS_STATIC_ASSERT(sizeof(UpvarCookie) == sizeof(uint32));
         JS_STATIC_ASSERT(UPVAR_LEVEL_LIMIT < FREE_LEVEL);
@@ -201,39 +201,38 @@ class Bindings {
 
     bool hasUpvars() const { return nupvars > 0; }
     bool hasLocalNames() const { return countLocalNames() > 0; }
 
     /* Returns the shape lineage generated for these bindings. */
     inline const js::Shape *lastShape() const;
 
     enum {
-       /*
-        * A script may have no more than this many arguments, variables, or
-        * upvars.
-        */
-       BINDING_COUNT_LIMIT = 0xFFFF
+        /*
+         * A script may have no more than this many arguments, variables, or
+         * upvars.
+         */
+        BINDING_COUNT_LIMIT = 0xFFFF
     };
 
     /*
      * Add a local binding for the given name, of the given type, for the code
      * being compiled.  If fun is non-null, this binding set is being created
      * for that function, so adjust corresponding metadata in that function
      * while adding.  Otherwise this set must correspond to a top-level script.
      *
      * A binding may be added twice with different kinds; the last one for a
      * given name prevails.  (We preserve both bindings for the decompiler,
      * which must deal with such cases.)  Pass null for name when indicating a
      * destructuring argument.  Return true on success.
      *
-     *
      * The parser builds shape paths for functions, usable by Call objects at
-     * runtime, by calling addLocal. All ARGUMENT bindings must be added before
-     * before any VARIABLE or CONSTANT bindings, which themselves must be added
-     * before all UPVAR bindings.
+     * runtime, by calling an "add" method. All ARGUMENT bindings must be added
+     * before before any VARIABLE or CONSTANT bindings, which themselves must
+     * be added before all UPVAR bindings.
      */
     bool add(JSContext *cx, JSAtom *name, BindingKind kind);
 
     /* Convenience specializations. */
     bool addVariable(JSContext *cx, JSAtom *name) {
         return add(cx, name, VARIABLE);
     }
     bool addConstant(JSContext *cx, JSAtom *name) {
@@ -426,17 +425,17 @@ struct JSScript {
     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.
-         * - Temporary scripts created by obj_eval, JS_EvaluateScript, and 
+         * - Temporary scripts created by obj_eval, JS_EvaluateScript, and
          *   similar functions never have these objects; such scripts are
          *   explicitly destroyed by the code that created them.
          * Debugging API functions (JSDebugHooks::newScriptHook;
          * JS_GetFunctionScript) may reveal sans-script-object Function and
          * temporary scripts to clients, but clients must never call
          * JS_NewScriptObject on such scripts: doing so would double-free them,
          * once from the explicit call to js_DestroyScript, and once when the
          * script object is garbage collected.
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -295,17 +295,16 @@ TypeToChar(JSValueType type)
       case JSVAL_TYPE_NULL: return 'N';
       case JSVAL_TYPE_UNDEFINED: return 'U';
       case JSVAL_TYPE_MAGIC: return 'M';
       case JSVAL_TYPE_FUNOBJ: return 'F';
       case JSVAL_TYPE_NONFUNOBJ: return 'O';
       case JSVAL_TYPE_BOXED: return '#';
       case JSVAL_TYPE_STRORNULL: return 's';
       case JSVAL_TYPE_OBJORNULL: return 'o';
-      case JSVAL_TYPE_UNINITIALIZED: return '*';
     }
     return '?';
 }
 
 static char
 ValueToTypeChar(const Value &v)
 {
     if (v.isInt32()) return 'I';
@@ -3655,17 +3654,16 @@ TraceRecorder::importGlobalSlot(unsigned
             type = JSVAL_TYPE_DOUBLE;
         index = (int)tree->globalSlots->length();
         tree->globalSlots->add(uint16(slot));
         tree->typeMap.add(type);
         SpecializeTreesToMissingGlobals(cx, globalObj, tree);
         JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length());
     } else {
         type = importTypeMap[importStackSlots + index];
-        JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
     }
     import(EosAddress(eos_ins, slot * sizeof(double)), vp, type, "global", index, NULL);
 }
 
 /* Lazily import a global slot if we don't already have it in the tracker. */
 JS_REQUIRES_STACK bool
 TraceRecorder::lazilyImportGlobalSlot(unsigned slot)
 {
@@ -3804,17 +3802,16 @@ TraceRecorder::getImpl(const void *p)
         return x;
     if (isVoidPtrGlobal(p)) {
         unsigned slot = nativeGlobalSlot((const Value *)p);
         JS_ASSERT(tree->globalSlots->offsetOf(uint16(slot)) != -1);
         importGlobalSlot(slot);
     } else {
         unsigned slot = nativeStackSlotImpl(p);
         JSValueType type = importTypeMap[slot];
-        JS_ASSERT(type != JSVAL_TYPE_UNINITIALIZED);
         importImpl(StackAddress(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble)),
                    p, type, "stack", slot, cx->fp());
     }
     JS_ASSERT(knownImpl(p));
     return tracker.get(p);
 }
 
 JS_REQUIRES_STACK LIns*
@@ -4028,17 +4025,16 @@ TraceRecorder::determineSlotType(Value* 
             t = IsPromotedInt32(i) ? JSVAL_TYPE_INT32 : JSVAL_TYPE_DOUBLE;
         } else if (isGlobal(vp)) {
             int offset = tree->globalSlots->offsetOf(uint16(nativeGlobalSlot(vp)));
             JS_ASSERT(offset != -1);
             t = importTypeMap[importStackSlots + offset];
         } else {
             t = importTypeMap[nativeStackSlot(vp)];
         }
-        JS_ASSERT(t != JSVAL_TYPE_UNINITIALIZED);
         JS_ASSERT_IF(t == JSVAL_TYPE_INT32, hasInt32Repr(*vp));
         return t;
     }
 
     if (vp->isObject())
         return vp->toObject().isFunction() ? JSVAL_TYPE_FUNOBJ : JSVAL_TYPE_NONFUNOBJ;
     return vp->extractNonDoubleObjectTraceType();
 }
@@ -5189,16 +5185,38 @@ TraceRecorder::prepareTreeCall(TreeFragm
      * The inner tree will probably access stack slots. So tell nanojit not to
      * discard or defer stack writes before emitting the call tree code.
      *
      * (The ExitType of this snapshot is nugatory. The exit can't be taken.)
      */
     w.xbarrier(createGuardRecord(exit));
 }
 
+class ClearSlotsVisitor : public SlotVisitorBase
+{
+    Tracker &tracker;
+  public:
+    ClearSlotsVisitor(Tracker &tracker)
+      : tracker(tracker)
+    {}
+
+    JS_ALWAYS_INLINE bool
+    visitStackSlots(Value *vp, size_t count, JSStackFrame *) {
+        for (Value *vpend = vp + count; vp != vpend; ++vp)
+            tracker.set(vp, NULL);
+        return true;
+    }
+
+    JS_ALWAYS_INLINE bool
+    visitFrameObjPtr(void *p, JSStackFrame *) {
+        tracker.set(p, NULL);
+        return true;
+    }
+};
+
 static unsigned
 BuildGlobalTypeMapFromInnerTree(Queue<JSValueType>& typeMap, VMSideExit* inner)
 {
 #if defined DEBUG
     unsigned initialSlots = typeMap.length();
 #endif
     /* First, use the innermost exit's global typemap. */
     typeMap.add(inner->globalTypeMap(), inner->numGlobalSlots);
@@ -5270,55 +5288,28 @@ TraceRecorder::emitTreeCall(TreeFragment
     map = exit->globalTypeMap();
     for (i = 0; i < exit->numGlobalSlots; i++)
         JS_ASSERT(map[i] != JSVAL_TYPE_BOXED);
     map = exit->stackTypeMap();
     for (i = 0; i < exit->numStackSlots; i++)
         JS_ASSERT(map[i] != JSVAL_TYPE_BOXED);
 #endif
 
-    /*
-     * Clear anything from the tracker that the inner tree could have written so
-     * that it will be lazily reloaded from the native stack. The only portion
-     * of the native stack that may be written to by both the inner and outer
-     * tree are the slots associated with the inner tree's entry frame. We may
-     * be certain of this by the following argument:
-     *
-     *  0. The only way for the inner tree to mutate the outer tree's upvars
-     *     is by writing to the call object associated with cx->fp or the
-     *     callDepth frames below it.
-     *  1. To write to a given call object, the innerTree must be executing with
-     *     that call object on its scope chain.
-     *  2. Only function frames' scope chains may contain call objects.
-     *  3. An interpreted function frame's scope chain is set to be the parent
-     *     of the callee when the callee is called.
-     *  4. On trace, there is a guard on the identity of the callee's parent.
-     *  5. From 1, 2, 3 and 4, any call object written to on trace will be the
-     *     exact same call object as was observed at record time.
-     *  6. Inner trees are recorded before outer trees.
-     *  7. From 5 and 6, the only call object created by an outer tree that can
-     *     be written to by an inner tree before the call object's associated
-     *     stack frame is popped is cx->fp->callObj.
-     *  8. setCallProp has an explicit guard against writing to cx->fp->callObj.
-     *  9. From 0, 7 and 8, an inner tree never mutates its outer tree's upvars.
-     */
-    clearCurrentFrameSlotsFromTracker(tracker);
+    /* The inner tree may modify currently-tracked upvars, so flush everything. */
+    ClearSlotsVisitor visitor(tracker);
+    VisitStackSlots(visitor, cx, callDepth);
     SlotList& gslots = *tree->globalSlots;
     for (unsigned i = 0; i < gslots.length(); i++) {
         unsigned slot = gslots[i];
         Value* vp = &globalObj->getSlotRef(slot);
         tracker.set(vp, NULL);
     }
 
     /* Set stack slots from the innermost frame. */
     importTypeMap.setLength(NativeStackSlots(cx, callDepth));
-#ifdef DEBUG
-    for (unsigned i = importStackSlots; i < importTypeMap.length(); i++)
-        importTypeMap[i] = JSVAL_TYPE_UNINITIALIZED;
-#endif
     unsigned startOfInnerFrame = importTypeMap.length() - exit->numStackSlots;
     for (unsigned i = 0; i < exit->numStackSlots; i++)
         importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i];
     importStackSlots = importTypeMap.length();
     JS_ASSERT(importStackSlots == NativeStackSlots(cx, callDepth));
 
     /*
      * Bug 502604 - It is illegal to extend from the outer typemap without
@@ -10166,55 +10157,31 @@ TraceRecorder::guardNativeConversion(Val
     } else {
         // We could specialize to guard on just JSClass.convert, but a mere
         // class guard is simpler and slightly faster.
         guardClass(obj_ins, obj->getClass(), snapshot(MISMATCH_EXIT), LOAD_NORMAL);
     }
     return RECORD_CONTINUE;
 }
 
-/*
- * Clear out all slots of this frame in the nativeFrameTracker. Different
- * locations on the VM stack might map to different locations on the native
- * stack depending on the number of arguments (i.e.) of the next call, so we
- * have to make sure we map those in to the cache with the right offsets.
- */
 JS_REQUIRES_STACK void
-TraceRecorder::clearCurrentFrameSlotsFromTracker(Tracker& which)
-{
-    JSStackFrame *const fp = cx->fp();
-
-    /*
-     * Duplicate native stack layout computation: see VisitFrameSlots header comment.
-     * This doesn't do layout arithmetic, but it must clear out all the slots defined as
-     * imported by VisitFrameSlots.
-     */
-    if (fp->isGlobalFrame()) {
-        Value *vp = fp->slots() + fp->globalScript()->nfixed;
-        Value *vpend = fp->slots() + fp->globalScript()->nslots;
-        for (; vp < vpend; ++vp)
-            which.set(vp, (LIns*)0);
-        return;
-    }
-
-    if (!fp->isEvalFrame()) {
-        /* For simplicitly, flush 'em all, even non-canonical arg slots. */
-        Value *vp = fp->actualArgs() - 2 /* callee, this */;
-        Value *vpend = fp->formalArgsEnd();
-        for (; vp < vpend; ++vp)
-            which.set(vp, (LIns*)0);
-    }
-
-    which.set(fp->addressOfArgs(), (LIns*)0);
-    which.set(fp->addressOfScopeChain(), (LIns*)0);
-
-    Value *vp = fp->slots();
-    Value *vpend = fp->slots() + fp->functionScript()->nslots;
+TraceRecorder::clearReturningFrameFromNativeveTracker()
+{
+    /*
+     * Clear all tracker entries associated with the frame for the same reason
+     * described in record_EnterFrame. Reuse the generic visitor to avoid
+     * duplicating logic. The generic visitor stops at 'sp', whereas we need to
+     * clear up to script->nslots, so finish the job manually.
+     */
+    ClearSlotsVisitor visitor(nativeFrameTracker);
+    VisitStackSlots(visitor, cx, 0);
+    Value *vp = cx->regs->sp;
+    Value *vpend = cx->fp()->slots() + cx->fp()->script()->nslots;
     for (; vp < vpend; ++vp)
-        which.set(vp, (LIns*)0);
+        nativeFrameTracker.set(vp, NULL);
 }
 
 class BoxArg
 {
   public:
     BoxArg(TraceRecorder *tr, Address addr)
         : tr(tr), addr(addr) {}
     TraceRecorder *tr;
@@ -10497,17 +10464,17 @@ TraceRecorder::record_JSOP_RETURN()
         rval_ins = get(&rval);
     }
     debug_only_stmt(JSAutoByteString funBytes);
     debug_only_printf(LC_TMTracer,
                       "returning from %s\n",
                       fp->fun()->atom ?
                         js_AtomToPrintableString(cx, fp->fun()->atom, &funBytes) :
                         "<anonymous>");
-    clearCurrentFrameSlotsFromTracker(nativeFrameTracker);
+    clearReturningFrameFromNativeveTracker();
 
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GOTO()
 {
     /*
@@ -13285,40 +13252,16 @@ TraceRecorder::stackLoad(Address addr, u
       case JSVAL_TYPE_BOXED:
       default:
         JS_NOT_REACHED("found jsval type in an upvar type map entry");
         return NULL;
     }
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_GETUPVAR()
-{
-    uintN index = GET_UINT16(cx->regs->pc);
-    JSScript *script = cx->fp()->script();
-    JSUpvarArray* uva = script->upvars();
-    JS_ASSERT(index < uva->length);
-
-    Value v;
-    LIns* upvar_ins = upvar(script, uva, index, v);
-    if (!upvar_ins)
-        return ARECORD_STOP;
-    stack(0, upvar_ins);
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
-TraceRecorder::record_JSOP_CALLUPVAR()
-{
-    CHECK_STATUS_A(record_JSOP_GETUPVAR());
-    stack(1, w.immiUndefined());
-    return ARECORD_CONTINUE;
-}
-
-JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETFCSLOT()
 {
     JSObject& callee = cx->fp()->callee();
     LIns* callee_ins = get(&cx->fp()->calleeValue());
 
     LIns* upvars_ins = w.getObjPrivatizedSlot(callee_ins, JSObject::JSSLOT_FLAT_CLOSURE_UPVARS);
 
     unsigned index = GET_UINT16(cx->regs->pc);
@@ -15880,17 +15823,17 @@ TraceRecorder::record_JSOP_STOP()
      * NB: we do not support script rval (eval, API users who want the result
      * of the last expression-statement, debugger API calls).
      */
     if (fp->isConstructing()) {
         rval_ins = get(&fp->thisValue());
     } else {
         rval_ins = w.immiUndefined();
     }
-    clearCurrentFrameSlotsFromTracker(nativeFrameTracker);
+    clearReturningFrameFromNativeveTracker();
     return ARECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK AbortableRecordingStatus
 TraceRecorder::record_JSOP_GETXPROP()
 {
     Value& l = stackval(-1);
     if (l.isPrimitive())
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1467,17 +1467,17 @@ class TraceRecorder
     JS_REQUIRES_STACK void guardDenseArray(nanojit::LIns* obj_ins, VMSideExit* exit);
     JS_REQUIRES_STACK bool guardHasPrototype(JSObject* obj, nanojit::LIns* obj_ins,
                                              JSObject** pobj, nanojit::LIns** pobj_ins,
                                              VMSideExit* exit);
     JS_REQUIRES_STACK RecordingStatus guardPrototypeHasNoIndexedProperties(JSObject* obj,
                                                                            nanojit::LIns* obj_ins,
                                                                            VMSideExit *exit);
     JS_REQUIRES_STACK RecordingStatus guardNativeConversion(Value& v);
-    JS_REQUIRES_STACK void clearCurrentFrameSlotsFromTracker(Tracker& which);
+    JS_REQUIRES_STACK void clearReturningFrameFromNativeveTracker();
     JS_REQUIRES_STACK void putActivationObjects();
     JS_REQUIRES_STACK RecordingStatus guardCallee(Value& callee);
     JS_REQUIRES_STACK JSStackFrame      *guardArguments(JSObject *obj, nanojit::LIns* obj_ins,
                                                         unsigned *depthp);
     JS_REQUIRES_STACK nanojit::LIns* guardArgsLengthNotAssigned(nanojit::LIns* argsobj_ins);
     JS_REQUIRES_STACK void guardNotHole(nanojit::LIns *argsobj_ins, nanojit::LIns *ids_ins);
     JS_REQUIRES_STACK RecordingStatus getClassPrototype(JSObject* ctor,
                                                           nanojit::LIns*& proto_ins);
@@ -1589,16 +1589,17 @@ class TraceRecorder
                   VMSideExit* expectedInnerExit, JSScript* outerScript, jsbytecode* outerPC,
                   uint32 outerArgc, bool speculate);
 
     /* Accessors. */
     VMFragment*         getFragment() const { return fragment; }
     TreeFragment*       getTree() const { return tree; }
     bool                outOfMemory() const { return traceMonitor->outOfMemory(); }
     Oracle*             getOracle() const { return oracle; }
+    JSObject*           getGlobal() const { return globalObj; }
 
     /* Entry points / callbacks from the interpreter. */
     JS_REQUIRES_STACK AbortableRecordingStatus monitorRecording(JSOp op);
     JS_REQUIRES_STACK AbortableRecordingStatus record_EnterFrame();
     JS_REQUIRES_STACK AbortableRecordingStatus record_LeaveFrame();
     JS_REQUIRES_STACK AbortableRecordingStatus record_SetPropHit(PropertyCacheEntry* entry,
                                                                  const js::Shape* shape);
     JS_REQUIRES_STACK AbortableRecordingStatus record_DefLocalFunSetSlot(uint32 slot,
@@ -1607,17 +1608,30 @@ class TraceRecorder
     void forgetGuardedShapesForObject(JSObject* obj);
 
     bool globalSetExpected(unsigned slot) {
         if (pendingGlobalSlotToSet != (int)slot) {
             /*
              * Do slot arithmetic manually to avoid getSlotRef assertions which
              * do not need to be satisfied for this purpose.
              */
-            return !tracker.has(globalObj->getSlots() + slot);
+            Value *vp = globalObj->getSlots() + slot;
+
+            /* If this global is definitely being tracked, then the write is unexpected. */
+            if (tracker.has(vp))
+                return false;
+            
+            /*
+             * Otherwise, only abort if the global is not present in the
+             * import typemap. Just deep aborting false here is not acceptable,
+             * because the recorder does not guard on every operation that
+             * could lazily resolve. Since resolving adds properties to
+             * reserved slots, the tracer will never have imported them.
+             */
+            return tree->globalSlots->offsetOf(nativeGlobalSlot(vp)) == -1;
         }
         pendingGlobalSlotToSet = -1;
         return true;
     }
 
 #ifdef DEBUG
     /* Debug printing functionality to emit printf() on trace. */
     JS_REQUIRES_STACK void tprint(const char *format, int count, nanojit::LIns *insa[]);
@@ -1863,17 +1877,17 @@ namespace js {
  * that has not been whitelisted in this manner is therefore unexpected and, if
  * the global slot is actually being tracked, recording must be aborted.
  */
 static JS_INLINE void
 AbortRecordingIfUnexpectedGlobalWrite(JSContext *cx, JSObject *obj, unsigned slot)
 {
 #ifdef JS_TRACER
     if (TraceRecorder *tr = TRACE_RECORDER(cx)) {
-        if (!obj->parent && !tr->globalSetExpected(slot))
+        if (obj == tr->getGlobal() && !tr->globalSetExpected(slot))
             AbortRecording(cx, "Global slot written outside tracer supervision");
     }
 #endif
 }
 
 }  /* namespace js */
 
 #endif /* jstracer_h___ */
--- a/js/src/jsval.h
+++ b/js/src/jsval.h
@@ -103,18 +103,17 @@ JS_ENUM_HEADER(JSValueType, uint8)
     /* The below types never appear in a jsval; they are only used in tracing. */
 
     JSVAL_TYPE_NONFUNOBJ           = 0x57,
     JSVAL_TYPE_FUNOBJ              = 0x67,
 
     JSVAL_TYPE_STRORNULL           = 0x97,
     JSVAL_TYPE_OBJORNULL           = 0x98,
 
-    JSVAL_TYPE_BOXED               = 0x99,
-    JSVAL_TYPE_UNINITIALIZED       = 0xcd
+    JSVAL_TYPE_BOXED               = 0x99
 } JS_ENUM_FOOTER(JSValueType);
 
 JS_STATIC_ASSERT(sizeof(JSValueType) == 1);
 
 #if JS_BITS_PER_WORD == 32
 
 /* Remember to propagate changes to the C defines below. */
 JS_ENUM_HEADER(JSValueTag, uint32)
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -343,46 +343,40 @@ AutoCompartment::~AutoCompartment()
 
 bool
 AutoCompartment::enter()
 {
     JS_ASSERT(!entered);
     if (origin != destination) {
         LeaveTrace(context);
 
-#ifdef DEBUG
-        JSCompartment *oldCompartment = context->compartment;
-        context->resetCompartment();
-        wasSane = (context->compartment == oldCompartment);
-#endif
+        if (context->isExceptionPending())
+            return false;
 
         context->compartment = destination;
         JSObject *scopeChain = target->getGlobal();
         JS_ASSERT(scopeChain->isNative());
+
         frame.construct();
-        if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref()) ||
-            !destination->wrapException(context)) {
-            frame.destroy();
+        if (!context->stack().pushDummyFrame(context, *scopeChain, &frame.ref())) {
             context->compartment = origin;
             return false;
         }
     }
     entered = true;
     return true;
 }
 
 void
 AutoCompartment::leave()
 {
     JS_ASSERT(entered);
     if (origin != destination) {
         frame.destroy();
         context->resetCompartment();
-        JS_ASSERT_IF(wasSane && context->hasfp(), context->compartment == origin);
-        context->compartment->wrapException(context);
     }
     entered = false;
 }
 
 /* Cross compartment wrappers. */
 
 JSCrossCompartmentWrapper::JSCrossCompartmentWrapper(uintN flags)
   : JSWrapper(CROSS_COMPARTMENT | flags)
@@ -637,18 +631,17 @@ JSCrossCompartmentWrapper::construct(JSC
     for (size_t n = 0; n < argc; ++n) {
         if (!call.destination->wrap(cx, &argv[n]))
             return false;
     }
     if (!JSWrapper::construct(cx, wrapper, argc, argv, rval))
         return false;
 
     call.leave();
-    return call.origin->wrap(cx, rval) &&
-           call.origin->wrapException(cx);
+    return call.origin->wrap(cx, rval);
 }
 
 bool
 JSCrossCompartmentWrapper::hasInstance(JSContext *cx, JSObject *wrapper, const Value *vp, bool *bp)
 {
     AutoCompartment call(cx, wrappedObject(wrapper));
     if (!call.enter())
         return false;
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -163,19 +163,16 @@ class AutoCompartment
     JSCompartment * const origin;
     JSObject * const target;
     JSCompartment * const destination;
   private:
     LazilyConstructed<DummyFrameGuard> frame;
     JSFrameRegs regs;
     AutoStringRooter input;
     bool entered;
-#ifdef DEBUG
-    bool wasSane;
-#endif
 
   public:
     AutoCompartment(JSContext *cx, JSObject *target);
     ~AutoCompartment();
 
     bool enter();
     void leave();
 
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -201,17 +201,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 - 80)
+#define JSXDR_BYTECODE_VERSION      (0xb973c0de - 81)
 
 /*
  * 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
@@ -1949,32 +1949,16 @@ mjit::Compiler::generateMethod()
           }
           END_CASE(JSOP_OBJECT)
 
           BEGIN_CASE(JSOP_CALLPROP)
             if (!jsop_callprop(script->getAtom(fullAtomIndex(PC))))
                 return Compile_Error;
           END_CASE(JSOP_CALLPROP)
 
-          BEGIN_CASE(JSOP_GETUPVAR)
-          BEGIN_CASE(JSOP_CALLUPVAR)
-          {
-            uint32 index = GET_UINT16(PC);
-            JSUpvarArray *uva = script->upvars();
-            JS_ASSERT(index < uva->length);
-
-            prepareStubCall(Uses(0));
-            masm.move(Imm32(uva->vector[index].asInteger()), Registers::ArgReg1);
-            INLINE_STUBCALL(stubs::GetUpvar);
-            frame.pushSynced();
-            if (op == JSOP_CALLUPVAR)
-                frame.push(UndefinedValue());
-          }
-          END_CASE(JSOP_CALLUPVAR)
-
           BEGIN_CASE(JSOP_UINT24)
             frame.push(Value(Int32Value((int32_t) GET_UINT24(PC))));
           END_CASE(JSOP_UINT24)
 
           BEGIN_CASE(JSOP_CALLELEM)
             jsop_getelem(true);
           END_CASE(JSOP_CALLELEM)
 
@@ -2331,16 +2315,22 @@ mjit::Compiler::emitReturn(FrameEntry *f
             stubcc.linkExit(putObjs, Uses(frame.frameSlots()));
 
             stubcc.leave();
             OOL_STUBCALL(stubs::PutActivationObjects);
 
             emitReturnValue(&stubcc.masm, fe);
             emitFinalReturn(stubcc.masm);
         }
+    } else {
+        if (fp->isEvalFrame() && script->strictModeCode) {
+            /* There will always be a call object. */
+            prepareStubCall(Uses(fe ? 1 : 0));
+            INLINE_STUBCALL(stubs::PutStrictEvalCallObject);
+        }
     }
 
     emitReturnValue(&masm, fe);
     emitFinalReturn(masm);
     frame.discardFrame();
 }
 
 void
@@ -3289,19 +3279,24 @@ mjit::Compiler::jsop_callprop_generic(JS
 bool
 mjit::Compiler::jsop_callprop_str(JSAtom *atom)
 {
     if (!script->compileAndGo) {
         jsop_callprop_slow(atom);
         return true; 
     }
 
-    /* Bake in String.prototype. Is this safe? */
+    /*
+     * Bake in String.prototype. This is safe because of compileAndGo.
+     * We must pass an explicit scope chain only because JSD calls into
+     * here via the recompiler with a dummy context, and we need to use
+     * the global object for the script we are now compiling.
+     */
     JSObject *obj;
-    if (!js_GetClassPrototype(cx, NULL, JSProto_String, &obj))
+    if (!js_GetClassPrototype(cx, &fp->scopeChain(), JSProto_String, &obj))
         return false;
 
     /* Force into a register because getprop won't expect a constant. */
     RegisterID reg = frame.allocReg();
 
     masm.move(ImmPtr(obj), reg);
     frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
 
--- a/js/src/methodjit/FastOps.cpp
+++ b/js/src/methodjit/FastOps.cpp
@@ -918,17 +918,17 @@ mjit::Compiler::jsop_typeof()
 
     JSOp fused = JSOp(PC[JSOP_TYPEOF_LENGTH]);
     if (fused == JSOP_STRING && !fe->isTypeKnown()) {
         JSOp op = JSOp(PC[JSOP_TYPEOF_LENGTH + JSOP_STRING_LENGTH]);
 
         if (op == JSOP_STRICTEQ || op == JSOP_EQ || op == JSOP_STRICTNE || op == JSOP_NE) {
             JSAtom *atom = script->getAtom(fullAtomIndex(PC + JSOP_TYPEOF_LENGTH));
             JSRuntime *rt = cx->runtime;
-            JSValueType type = JSVAL_TYPE_UNINITIALIZED;
+            JSValueType type = JSVAL_TYPE_BOXED;
             Assembler::Condition cond = (op == JSOP_STRICTEQ || op == JSOP_EQ)
                                         ? Assembler::Equal
                                         : Assembler::NotEqual;
             
             if (atom == rt->atomState.typeAtoms[JSTYPE_VOID]) {
                 type = JSVAL_TYPE_UNDEFINED;
             } else if (atom == rt->atomState.typeAtoms[JSTYPE_STRING]) {
                 type = JSVAL_TYPE_STRING;
@@ -936,17 +936,17 @@ mjit::Compiler::jsop_typeof()
                 type = JSVAL_TYPE_BOOLEAN;
             } else if (atom == rt->atomState.typeAtoms[JSTYPE_NUMBER]) {
                 type = JSVAL_TYPE_INT32;
 
                 /* JSVAL_TYPE_DOUBLE is 0x0 and JSVAL_TYPE_INT32 is 0x1, use <= or > to match both */
                 cond = (cond == Assembler::Equal) ? Assembler::BelowOrEqual : Assembler::Above;
             }
 
-            if (type != JSVAL_TYPE_UNINITIALIZED) {
+            if (type != JSVAL_TYPE_BOXED) {
                 PC += JSOP_STRING_LENGTH;;
                 PC += JSOP_EQ_LENGTH;
 
                 RegisterID result = frame.allocReg(Registers::SingleByteRegs);
 
 #if defined JS_NUNBOX32
                 if (frame.shouldAvoidTypeRemat(fe))
                     masm.set32(cond, masm.tagOf(frame.addressOf(fe)), ImmType(type), result);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -475,18 +475,20 @@ stubs::UncachedCallHelper(VMFrame &f, ui
 
     if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
         THROW();
 
     return;
 }
 
 void JS_FASTCALL
-stubs::PutCallObject(VMFrame &f)
+stubs::PutStrictEvalCallObject(VMFrame &f)
 {
+    JS_ASSERT(f.fp()->isEvalFrame());
+    JS_ASSERT(f.fp()->script()->strictModeCode);
     JS_ASSERT(f.fp()->hasCallObj());
     js_PutCallObject(f.cx, f.fp());
 }
 
 void JS_FASTCALL
 stubs::PutActivationObjects(VMFrame &f)
 {
     JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj());
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -900,18 +900,19 @@ class GetPropCompiler : public PICStubCo
         return Lookup_Cacheable;
     }
 
     LookupStatus generateStringObjLengthStub()
     {
         Assembler masm;
 
         Jump notStringObj = masm.testObjClass(Assembler::NotEqual, pic.objReg, obj->getClass());
-        masm.loadPayload(Address(pic.objReg, JSObject::getFixedSlotOffset(
-                         JSObject::JSSLOT_PRIMITIVE_THIS)), pic.objReg);
+        masm.loadPtr(Address(pic.objReg, offsetof(JSObject, slots)), pic.objReg);
+        masm.loadPayload(Address(pic.objReg, JSObject::JSSLOT_PRIMITIVE_THIS * sizeof(Value)),
+                         pic.objReg);
         masm.loadPtr(Address(pic.objReg, JSString::offsetOfLengthAndFlags()), pic.objReg);
         masm.urshift32(Imm32(JSString::LENGTH_SHIFT), pic.objReg);
         masm.move(ImmType(JSVAL_TYPE_INT32), pic.shapeReg);
         Jump done = masm.jump();
 
         PICLinker buffer(masm, pic);
         if (!buffer.init(cx))
             return error();
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -102,17 +102,17 @@ struct UncachedCallResult {
  * These functions either execute the function, return a native code
  * pointer that can be used to call the function, or throw.
  */
 void UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
 void UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr);
 
 void JS_FASTCALL CreateThis(VMFrame &f, JSObject *proto);
 void JS_FASTCALL Throw(VMFrame &f);
-void JS_FASTCALL PutCallObject(VMFrame &f);
+void JS_FASTCALL PutStrictEvalCallObject(VMFrame &f);
 void JS_FASTCALL PutActivationObjects(VMFrame &f);
 void JS_FASTCALL GetCallObject(VMFrame &f);
 #if JS_MONOIC
 void * JS_FASTCALL InvokeTracer(VMFrame &f, ic::TraceICInfo *tic);
 #else
 void * JS_FASTCALL InvokeTracer(VMFrame &f);
 #endif
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -4242,69 +4242,16 @@ Deserialize(JSContext *cx, uintN argc, j
                                 JS_STRUCTURED_CLONE_VERSION, &v, NULL, NULL)) {
         return false;
     }
     JS_SET_RVAL(cx, vp, v);
     return true;
 }
 
 JSBool
-SetGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
-{
-    if (argc != 3) {
-        JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
-        return false;
-    }
-
-    jsval *argv = JS_ARGV(cx, vp);
-
-    JSBool doSet;
-    if (!JS_ValueToBoolean(cx, argv[0], &doSet))
-        return false;
-
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    if (!doSet)
-        return true;
-
-    jsid id;
-    if (!JS_ValueToId(cx, argv[1], &id))
-        return false;
-
-    JSObject *global = JS_GetGlobalForScopeChain(cx);
-    return global && JS_SetPropertyById(cx, global, id, &argv[2]);
-}
-
-JSBool
-DefGlobalPropIf(JSContext *cx, uintN argc, jsval *vp)
-{
-    if (argc != 3) {
-        JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL, JSSMSG_INVALID_ARGS, "setGlobalPropIf");
-        return false;
-    }
-
-    jsval *argv = JS_ARGV(cx, vp);
-
-    JSBool doSet;
-    if (!JS_ValueToBoolean(cx, argv[0], &doSet))
-        return false;
-
-    JS_SET_RVAL(cx, vp, JSVAL_VOID);
-    if (!doSet)
-        return true;
-
-    jsid id;
-    if (!JS_ValueToId(cx, argv[1], &id))
-        return false;
-
-    JSObject *global = JS_GetGlobalForScopeChain(cx);
-    JSBool ignore;
-    return global && JS_DefineOwnProperty(cx, global, id, argv[2], &ignore);
-}
-
-JSBool
 MJitStats(JSContext *cx, uintN argc, jsval *vp)
 {
     JS_SET_RVAL(cx, vp, INT_TO_JSVAL(cx->runtime->mjitMemoryUsed));
     return true;
 }
 
 JSBool
 StringStats(JSContext *cx, uintN argc, jsval *vp)
@@ -4405,18 +4352,16 @@ static JSFunctionSpec shell_functions[] 
     JS_FN("compile",        Compile,        1,0),
     JS_FN("parse",          Parse,          1,0),
     JS_FN("timeout",        Timeout,        1,0),
     JS_FN("elapsed",        Elapsed,        0,0),
     JS_FN("parent",         Parent,         1,0),
     JS_FN("wrap",           Wrap,           1,0),
     JS_FN("serialize",      Serialize,      1,0),
     JS_FN("deserialize",    Deserialize,    1,0),
-    JS_FN("setGlobalPropIf",SetGlobalPropIf,3,0),
-    JS_FN("defGlobalPropIf",DefGlobalPropIf,3,0),
 #ifdef JS_METHODJIT
     JS_FN("mjitstats",      MJitStats,      0,0),
 #endif
     JS_FN("stringstats",    StringStats,    0,0),
     JS_FS_END
 };
 
 static const char shell_help_header[] =
@@ -4540,19 +4485,16 @@ static const char *const shell_help_mess
 "timeout([seconds])\n"
 "  Get/Set the limit in seconds for the execution time for the current context.\n"
 "  A negative value (default) means that the execution time is unlimited.",
 "elapsed()                Execution time elapsed for the current context.",
 "parent(obj)              Returns the parent of obj.\n",
 "wrap(obj)                Wrap an object into a noop wrapper.\n",
 "serialize(sd)            Serialize sd using JS_WriteStructuredClone. Returns a TypedArray.\n",
 "deserialize(a)           Deserialize data generated by serialize.\n",
-"setGlobalPropIf(b,id,v)  If b, get the global object o and perform o[id] = v.\n",
-"defGlobalPropIf(b,id,dsc)If b, get the global object o and perform\n"
-"                         Object.defineProperty(o, id, dsc).\n",
 #ifdef JS_METHODJIT
 "mjitstats()             Return stats on mjit memory usage.\n",
 #endif
 "stringstats()           Return stats on string memory usage.\n"
 };
 
 /* Help messages must match shell functions. */
 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) + 1 ==
--- a/js/src/tests/ecma_5/eval/jstests.list
+++ b/js/src/tests/ecma_5/eval/jstests.list
@@ -11,32 +11,32 @@ url-prefix ../../jsreftest.html?test=ecm
 #  - Is the call direct or indirect?  (ES5 15.1.2.1)
 #  - Is the eval code itself strict mode?
 #
 # We test all of these in their basic configurations in an attempt to verify
 # correct general behavior.  These tests won't help any special-case
 # optimizations we might perform -- add new tests for such changes as needed.
 #
 
-# script exhaustive-fun-normalcaller-direct-normalcode.js
-# script exhaustive-fun-normalcaller-direct-strictcode.js
-# script exhaustive-fun-normalcaller-indirect-normalcode.js
-# script exhaustive-fun-normalcaller-indirect-strictcode.js
-# script exhaustive-fun-strictcaller-direct-normalcode.js
-# script exhaustive-fun-strictcaller-direct-strictcode.js
-# script exhaustive-fun-strictcaller-indirect-normalcode.js
-# script exhaustive-fun-strictcaller-indirect-strictcode.js
-# script exhaustive-global-normalcaller-direct-normalcode.js
-# script exhaustive-global-normalcaller-direct-strictcode.js
-# script exhaustive-global-normalcaller-indirect-normalcode.js
-# script exhaustive-global-normalcaller-indirect-strictcode.js
-# script exhaustive-global-strictcaller-direct-normalcode.js
-# script exhaustive-global-strictcaller-direct-strictcode.js
-# script exhaustive-global-strictcaller-indirect-normalcode.js
-# script exhaustive-global-strictcaller-indirect-strictcode.js
+script exhaustive-fun-normalcaller-direct-normalcode.js
+script exhaustive-fun-normalcaller-direct-strictcode.js
+script exhaustive-fun-normalcaller-indirect-normalcode.js
+script exhaustive-fun-normalcaller-indirect-strictcode.js
+script exhaustive-fun-strictcaller-direct-normalcode.js
+script exhaustive-fun-strictcaller-direct-strictcode.js
+script exhaustive-fun-strictcaller-indirect-normalcode.js
+script exhaustive-fun-strictcaller-indirect-strictcode.js
+script exhaustive-global-normalcaller-direct-normalcode.js
+script exhaustive-global-normalcaller-direct-strictcode.js
+script exhaustive-global-normalcaller-indirect-normalcode.js
+script exhaustive-global-normalcaller-indirect-strictcode.js
+script exhaustive-global-strictcaller-direct-normalcode.js
+script exhaustive-global-strictcaller-direct-strictcode.js
+script exhaustive-global-strictcaller-indirect-normalcode.js
+script exhaustive-global-strictcaller-indirect-strictcode.js
 
 # Not written yet!  These require a new shell primitive to work there, though
 # browser could probably use setTimeout for this.  Moreover, we haven't
 # implemented calling eval without a scripted frame yet (FIXME: bug 602994).
 # script exhaustive-nothing-normalcaller-direct-normalcode.js
 # script exhaustive-nothing-normalcaller-direct-strictcode.js
 # script exhaustive-nothing-normalcaller-indirect-normalcode.js
 # script exhaustive-nothing-normalcaller-indirect-strictcode.js
--- a/js/src/tests/ecma_5/extensions/jstests.list
+++ b/js/src/tests/ecma_5/extensions/jstests.list
@@ -16,8 +16,9 @@ script eval-native-callback-is-indirect.
 script regress-bug607284.js
 script Object-keys-and-object-ids.js
 fails script nested-delete-name-in-evalcode.js # bug 604301, at a minimum
 script bug352085.js
 script bug472534.js
 script bug496985.js
 script bug566661.js
 script iterator-in-catch.js
+script strict-function-statements.js
--- a/js/src/tests/ecma_5/extensions/shell.js
+++ b/js/src/tests/ecma_5/extensions/shell.js
@@ -1,37 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/licenses/publicdomain/
- */
-
-/*
- * Return true if both of these return true:
- * - LENIENT_PRED applied to CODE
- * - STRICT_PRED applied to CODE with a use strict directive added to the front
- *
- * Run STRICT_PRED first, for testing code that affects the global environment
- * in loose mode, but fails in strict mode.
- */
-function testLenientAndStrict(code, lenient_pred, strict_pred) {
-  return (strict_pred("'use strict'; " + code) && 
-          lenient_pred(code));
-}
-
-/*
- * raisesException(EXCEPTION)(CODE) returns true if evaluating CODE (as eval
- * code) throws an exception object whose prototype is
- * EXCEPTION.prototype, and returns false if it throws any other error
- * or evaluates successfully. For example: raises(TypeError)("0()") ==
- * true.
- */
-function raisesException(exception) {
-  return function (code) {
-    try {
-      eval(code);
-      return false;
-    } catch (actual) {
-      return exception.prototype.isPrototypeOf(actual);
-    }
-  };
-};
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_5/extensions/strict-function-statements.js
@@ -0,0 +1,100 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+// Ordinary function definitions should be unaffected.
+assertEq(testLenientAndStrict("function f() { }",
+                              parsesSuccessfully,
+                              parsesSuccessfully),
+         true);
+
+// Function statements within blocks are forbidden in strict mode code.
+assertEq(testLenientAndStrict("{ function f() { } }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+
+// Lambdas are always permitted within blocks.
+assertEq(testLenientAndStrict("{ (function f() { }) }",
+                              parsesSuccessfully,
+                              parsesSuccessfully),
+         true);
+
+// Function statements within any sort of statement are forbidden in strict mode code.
+assertEq(testLenientAndStrict("if (true) function f() { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("while (true) function f() { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("do function f() { } while (true);",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("for(;;) function f() { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("for(x in []) function f() { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("with(o) function f() { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("switch(1) { case 1: function f() { } }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("x: function f() { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+assertEq(testLenientAndStrict("try { function f() { } } catch (x) { }",
+                              parsesSuccessfully,
+                              parseRaisesException(SyntaxError)),
+         true);
+
+// Lambdas are always permitted within any sort of statement.
+assertEq(testLenientAndStrict("if (true) (function f() { })",
+                              parsesSuccessfully,
+                              parsesSuccessfully),
+         true);
+
+// Function statements are permitted in blocks within lenient functions.
+assertEq(parsesSuccessfully("function f() { function g() { } }"),
+         true);
+
+// Function statements are permitted in any statement within lenient functions.
+assertEq(parsesSuccessfully("function f() { if (true) function g() { } }"),
+         true);
+
+assertEq(parseRaisesException(SyntaxError)
+         ("function f() { 'use strict'; if (true) function g() { } }"),
+         true);
+
+assertEq(parseRaisesException(SyntaxError)
+         ("function f() { 'use strict'; { function g() { } } }"),
+         true);
+
+assertEq(parsesSuccessfully("function f() { 'use strict'; if (true) (function g() { }) }"),
+         true);
+
+assertEq(parsesSuccessfully("function f() { 'use strict'; { (function g() { }) } }"),
+         true);
+
+// Eval should behave the same way. (The parse-only tests use the Function constructor.)
+assertEq(testLenientAndStrict("function f() { }",
+                              completesNormally,
+                              completesNormally),
+         true);
+assertEq(testLenientAndStrict("{ function f() { } }",
+                              completesNormally,
+                              raisesException(SyntaxError)),
+         true);
+
+reportCompare(true, true);
--- a/js/src/tests/ecma_5/strict/jstests.list
+++ b/js/src/tests/ecma_5/strict/jstests.list
@@ -34,9 +34,9 @@ script B.1.2.js
 script function-name-arity.js
 script primitive-this-no-writeback.js
 script regress-532254.js
 script regress-532041.js
 script unbrand-this.js
 script this-for-function-expression-recursion.js
 script assign-to-callee-name.js
 script directive-prologue-01.js
-# script eval-variable-environment.js
+script eval-variable-environment.js
--- a/js/src/tests/js1_8_1/extensions/regress-452498-196.js
+++ b/js/src/tests/js1_8_1/extensions/regress-452498-196.js
@@ -60,16 +60,16 @@ function test()
   x+=NaN;
 
   reportCompare(expect, actual, summary + ': 1');
 
 // Assertion failure: lexdep->isLet(), at ../jsparse.cpp:1900
 
   (function (){
     var x;
-    eval("const x; (function ()x)");
+    eval("var x; (function ()x)");
   }
     )();
 
   reportCompare(expect, actual, summary + ': 2');
 
   exitFunc ('test');
 }
--- a/js/src/tests/js1_8_1/regress/regress-452498-119.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-119.js
@@ -73,17 +73,17 @@ function test()
 // =====
   uneval(new Function("[(x = x) for (c in []) if ([{} for (x in [])])]"))
 
 // Assertion failure: (uintN)i < ss->top, at ../jsopcode.cpp:2814
 // =====
     function f() {
     var x;
     (function(){})();
-    eval("if(x|=[]) {const x; }");
+    eval("if(x|=[]) {var x; }");
   }
   f();
 
 // Opt crash [@ js_ValueToNumber] at 0xc3510424
 // Dbg crash [@ js_ValueToNumber] at 0xdadadad8
 // =====
 
   reportCompare(expect, actual, summary);
--- a/js/src/tests/js1_8_5/regress/jstests.list
+++ b/js/src/tests/js1_8_5/regress/jstests.list
@@ -10,16 +10,18 @@ script regress-541455.js
 script regress-546615.js
 script regress-551763-0.js
 script regress-551763-1.js
 script regress-551763-2.js
 script regress-552432.js
 script regress-553778.js
 script regress-555246-0.js
 script regress-555246-1.js
+script regress-559402-1.js
+script regress-559402-2.js
 script regress-559438.js
 script regress-560101.js
 script regress-560998-1.js
 script regress-560998-2.js
 script regress-563210.js
 script regress-563221.js
 script regress-566549.js
 script regress-566914.js
@@ -33,16 +35,20 @@ script regress-583429.js
 script regress-584355.js
 script regress-586482-1.js
 script regress-586482-2.js
 script regress-586482-3.js
 script regress-586482-4.js
 script regress-586482-5.js
 script regress-588339.js
 script regress-yarr-regexp.js
+script regress-592202-1.js
+script regress-592202-2.js
+script regress-592202-3.js
+script regress-592202-4.js
 script regress-592217.js
 script regress-592556-c35.js
 script regress-593256.js
 fails-if(!xulRuntime.shell) script regress-595230-1.js
 fails-if(!xulRuntime.shell) script regress-595230-2.js
 script regress-595365-1.js
 fails-if(!xulRuntime.shell) script regress-595365-2.js
 script regress-596103.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-559402-1.js
@@ -0,0 +1,22 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+var expect = "No error";
+var actual = expect;
+
+if (typeof options == "function") {
+    var opts = options();
+    if (!/\bstrict\b/.test(opts))
+        options("strict");
+    if (!/\bwerror\b/.test(opts))
+        options("werror");
+}
+
+try {
+    eval('function foo() { "use strict"; }');
+} catch (e) {
+    actual = '' + e;
+}
+
+reportCompare(expect, actual, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-559402-2.js
@@ -0,0 +1,8 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+var expect = undefined;
+var actual = (function foo() { "bogus"; })();
+
+reportCompare(expect, actual, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-592202-1.js
@@ -0,0 +1,7 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+i = 42
+eval("let(y){(function(){let({}=y){(function(){let({}=y=[])(i)})()}})()}")
+reportCompare(0, 0, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-592202-2.js
@@ -0,0 +1,15 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+eval("\
+  let(b)((\
+    function(){\
+      let(d=b)\
+      ((function(){\
+        b=b\
+      })())\
+    }\
+  )())\
+")
+reportCompare(0, 0, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-592202-3.js
@@ -0,0 +1,28 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+test();
+
+function test()
+{
+  var counter = 0;
+  function f(x,y) {
+      try
+      { 
+        throw 42;
+      }
+      catch(e2)
+      { 
+        foo(function(){ return x; }| "9.2" && 5 || counter && e);
+        ++counter;
+      }
+  }
+
+  f(2, 1);
+}
+
+function foo(bar) { return ""+bar; }
+
+reportCompare(0, 0, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/js1_8_5/regress/regress-592202-4.js
@@ -0,0 +1,31 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+function p() { }
+
+function test()
+{
+  var counter = 0;
+
+  function f(x) {
+      try
+      { 
+        throw 42;
+      }
+      catch(e)
+      { 
+        assertEq(counter, 0);
+        p(function(){x;});
+        counter = 1;
+      }
+  }
+
+  f(2);
+  assertEq(counter, 1);
+}
+
+test();
+
+reportCompare(0, 0, "ok");
--- a/js/src/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/src/xpconnect/wrappers/XrayWrapper.cpp
@@ -139,16 +139,18 @@ GetWrappedNativeObjectFromHolder(JSConte
 }
 
 static JSObject *
 GetExpandoObject(JSContext *cx, JSObject *holder)
 {
     JSObject *expando = holder->getSlot(JSSLOT_EXPANDO).toObjectOrNull();
     if (!expando) {
         expando =  JS_NewObjectWithGivenProto(cx, nsnull, nsnull, holder->getParent());
+        if (!expando)
+            return NULL;
         holder->setSlot(JSSLOT_EXPANDO, ObjectValue(*expando));
     }
     return expando;
 }
 
 // Some DOM objects have shared properties that don't have an explicit
 // getter/setter and rely on the class getter/setter. We install a
 // class getter/setter on the holder object to trigger them.
--- a/js/src/yarr/yarr/RegexParser.h
+++ b/js/src/yarr/yarr/RegexParser.h
@@ -34,38 +34,39 @@ namespace JSC { namespace Yarr {
 
 enum BuiltInCharacterClassID {
     DigitClassID,
     SpaceClassID,
     WordClassID,
     NewlineClassID
 };
 
+enum ErrorCode {
+    NoError,
+    PatternTooLarge,
+    QuantifierOutOfOrder,
+    QuantifierWithoutAtom,
+    MissingParentheses,
+    ParenthesesUnmatched,
+    ParenthesesTypeInvalid,
+    CharacterClassUnmatched,
+    CharacterClassOutOfOrder,
+    CharacterClassRangeSingleChar,
+    EscapeUnterminated,
+    QuantifierTooLarge,
+    NumberOfErrorCodes
+};
+
 // The Parser class should not be used directly - only via the Yarr::parse() method.
 template<class Delegate>
 class Parser {
 private:
     template<class FriendDelegate>
     friend int parse(FriendDelegate& delegate, const UString& pattern, unsigned backReferenceLimit);
 
-    enum ErrorCode {
-        NoError,
-        PatternTooLarge,
-        QuantifierOutOfOrder,
-        QuantifierWithoutAtom,
-        MissingParentheses,
-        ParenthesesUnmatched,
-        ParenthesesTypeInvalid,
-        CharacterClassUnmatched,
-        CharacterClassOutOfOrder,
-        EscapeUnterminated,
-        QuantifierTooLarge,
-        NumberOfErrorCodes
-    };
-
     /*
      * CharacterClassParserDelegate:
      *
      * The class CharacterClassParserDelegate is used in the parsing of character
      * classes.  This class handles detection of character ranges.  This class
      * implements enough of the delegate interface such that it can be passed to
      * parseEscape() as an EscapeDelegate.  This allows parseEscape() to be reused
      * to perform the parsing of escape characters in character sets.
@@ -142,16 +143,25 @@ private:
 
         /*
          * atomBuiltInCharacterClass():
          *
          * Adds a built-in character class, called by parseEscape().
          */
         void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert)
         {
+            if (m_state == cachedCharacterHyphen) {
+                // If the RHS of a range does not contain exacly one character then a SyntaxError
+                // must be thrown. SpiderMonkey only errors out in the [c-\s] case as an extension.
+                // (This assumes none of the built in character classes contain a single
+                // character.)
+                m_err = CharacterClassRangeSingleChar;
+                m_state = empty;
+                return;
+            }
             flush();
             m_delegate.atomCharacterClassBuiltIn(classID, invert);
         }
 
         /*
          * end():
          *
          * Called at end of construction.
@@ -399,17 +409,17 @@ private:
     void parseCharacterClassEscape(CharacterClassParserDelegate& delegate)
     {
         parseEscape<true>(delegate);
     }
 
     /*
      * parseCharacterClass():
      *
-     * Helper for parseTokens(); calls dirctly and indirectly (via parseCharacterClassEscape)
+     * Helper for parseTokens(); calls directly and indirectly (via parseCharacterClassEscape)
      * to an instance of CharacterClassParserDelegate, to describe the character class to the
      * delegate.
      */
     void parseCharacterClass()
     {
         JS_ASSERT(!m_err);
         JS_ASSERT(peek() == '[');
         consume();