Bug 807443 - IonMonkey, Compile named lambdas. r=dvander
☠☠ backed out by 6d32ee966b68 ☠ ☠
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Wed, 05 Dec 2012 22:50:20 -0800
changeset 120758 747b04228fa634553305663035edc0641b9c533d
parent 120757 2971c45877a749edd9e43f645e7259800d6f97f5
child 120759 3e3b0aa054745aaa9569c0c3e198724bc1da47fd
push idunknown
push userunknown
push dateunknown
reviewersdvander
bugs807443
milestone20.0a1
Bug 807443 - IonMonkey, Compile named lambdas. r=dvander
js/src/ion/Bailouts.cpp
js/src/ion/Bailouts.h
js/src/ion/CodeGenerator.cpp
js/src/ion/CodeGenerator.h
js/src/ion/Ion.cpp
js/src/ion/IonBuilder.cpp
js/src/ion/IonBuilder.h
js/src/ion/IonFrames.h
js/src/ion/LIR-Common.h
js/src/ion/LIR.h
js/src/ion/LOpcodes.h
js/src/ion/Lowering.cpp
js/src/ion/Lowering.h
js/src/ion/MIR.h
js/src/ion/MOpcodes.h
js/src/ion/VMFunctions.h
js/src/ion/shared/CodeGenerator-shared.cpp
js/src/jit-test/tests/basic/bug522136.js
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack.cpp
js/src/vm/Stack.h
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -514,24 +514,25 @@ ion::RecompileForInlining()
         return BAILOUT_RETURN_FATAL_ERROR;
 
     // Invalidation should not reset the use count.
     JS_ASSERT(script->getUseCount() >= js_IonOptions.usesBeforeInlining);
 
     return true;
 }
 
+// Initialize the decl env Object and the call object of the current frame.
 bool
-ion::EnsureHasCallObject(JSContext *cx, StackFrame *fp)
+ion::EnsureHasScopeObjects(JSContext *cx, StackFrame *fp)
 {
     if (fp->isFunctionFrame() &&
         fp->fun()->isHeavyweight() &&
         !fp->hasCallObj())
     {
-        return fp->initCallObject(cx);
+        return fp->initFunctionScopeObjects(cx);
     }
     return true;
 }
 
 uint32_t
 ion::BoundsCheckFailure()
 {
     JSContext *cx = GetIonContext()->cx;
@@ -591,17 +592,17 @@ ion::CachedShapeGuardFailure()
 
 uint32_t
 ion::ThunkToInterpreter(Value *vp)
 {
     JSContext *cx = GetIonContext()->cx;
     IonActivation *activation = cx->runtime->ionActivation;
     BailoutClosure *br = activation->takeBailout();
 
-    if (!EnsureHasCallObject(cx, cx->fp()))
+    if (!EnsureHasScopeObjects(cx, cx->fp()))
         return Interpret_Error;
 
     // By default we set the forbidOsr flag on the ion script, but if a GC
     // happens just after we re-enter the interpreter, the ion script get
     // invalidated and we do not see the forbidOsr flag.  This may cause a loop
     // which apear with eager compilation and gc zeal enabled.  This code is a
     // workaround to avoid recompiling with OSR just after a bailout followed by
     // a GC. (see Bug 746691 & Bug 751383)
--- a/js/src/ion/Bailouts.h
+++ b/js/src/ion/Bailouts.h
@@ -199,17 +199,17 @@ class IonBailoutIterator : public IonFra
         if (topIonScript_)
             return topIonScript_;
         return IonFrameIterator::ionScript();
     }
 
     void dump() const;
 };
 
-bool EnsureHasCallObject(JSContext *cx, StackFrame *fp);
+bool EnsureHasScopeObjects(JSContext *cx, StackFrame *fp);
 
 // Called from a bailout thunk. Returns a BAILOUT_* error code.
 uint32_t Bailout(BailoutStack *sp);
 
 // Called from the invalidation thunk. Returns a BAILOUT_* error code.
 uint32_t InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut);
 
 // Called from a bailout thunk. Interprets the frame(s) that have been bailed
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1721,16 +1721,40 @@ bool
 CodeGenerator::visitOutOfLineNewObject(OutOfLineNewObject *ool)
 {
     if (!visitNewObjectVMCall(ool->lir()))
         return false;
     masm.jump(ool->rejoin());
     return true;
 }
 
+typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction);
+static const VMFunction NewDeclEnvObjectInfo =
+    FunctionInfo<NewDeclEnvObjectFn>(DeclEnvObject::createTemplateObject);
+
+bool
+CodeGenerator::visitNewDeclEnvObject(LNewDeclEnvObject *lir)
+{
+    Register obj = ToRegister(lir->output());
+    JSObject *templateObj = lir->mir()->templateObj();
+    CompileInfo &info = lir->mir()->block()->info();
+
+    // If we have a template object, we can inline call object creation.
+    OutOfLineCode *ool = oolCallVM(NewDeclEnvObjectInfo, lir,
+                                   (ArgList(), ImmGCPtr(info.fun())),
+                                   StoreRegisterTo(obj));
+    if (!ool)
+        return false;
+
+    masm.newGCThing(obj, templateObj, ool->entry());
+    masm.initGCThing(obj, templateObj);
+    masm.bind(ool->rejoin());
+    return true;
+}
+
 typedef JSObject *(*NewCallObjectFn)(JSContext *, HandleShape,
                                      HandleTypeObject, HeapSlot *);
 static const VMFunction NewCallObjectInfo =
     FunctionInfo<NewCallObjectFn>(NewCallObject);
 
 bool
 CodeGenerator::visitNewCallObject(LNewCallObject *lir)
 {
--- a/js/src/ion/CodeGenerator.h
+++ b/js/src/ion/CodeGenerator.h
@@ -87,16 +87,17 @@ class CodeGenerator : public CodeGenerat
     bool visitDoubleToInt32(LDoubleToInt32 *lir);
     bool visitNewSlots(LNewSlots *lir);
     bool visitNewArrayCallVM(LNewArray *lir);
     bool visitNewArray(LNewArray *lir);
     bool visitOutOfLineNewArray(OutOfLineNewArray *ool);
     bool visitNewObjectVMCall(LNewObject *lir);
     bool visitNewObject(LNewObject *lir);
     bool visitOutOfLineNewObject(OutOfLineNewObject *ool);
+    bool visitNewDeclEnvObject(LNewDeclEnvObject *lir);
     bool visitNewCallObject(LNewCallObject *lir);
     bool visitNewStringObject(LNewStringObject *lir);
     bool visitInitProp(LInitProp *lir);
     bool visitCreateThis(LCreateThis *lir);
     bool visitCreateThisVM(LCreateThisVM *lir);
     bool visitReturnFromCtor(LReturnFromCtor *lir);
     bool visitArrayLength(LArrayLength *lir);
     bool visitTypedArrayLength(LTypedArrayLength *lir);
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -1522,17 +1522,17 @@ EnterIon(JSContext *cx, StackFrame *fp, 
         IonActivation activation(cx, fp);
         JSAutoResolveFlags rf(cx, RESOLVE_INFER);
         AutoFlushInhibitor afi(cx->compartment->ionCompartment());
         // Single transition point from Interpreter to Ion.
         enter(jitcode, maxArgc, maxArgv, fp, calleeToken, &result);
     }
 
     if (result.isMagic() && result.whyMagic() == JS_ION_BAILOUT) {
-        if (!EnsureHasCallObject(cx, cx->fp()))
+        if (!EnsureHasScopeObjects(cx, cx->fp()))
             return IonExec_Error;
         return IonExec_Bailout;
     }
 
     JS_ASSERT(fp == cx->fp());
     JS_ASSERT(!cx->runtime->hasIonReturnOverride());
 
     // The trampoline wrote the return value but did not set the HAS_RVAL flag.
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -583,20 +583,23 @@ IonBuilder::initScopeChain()
 
     if (JSFunction *fun = info().fun()) {
         MCallee *callee = MCallee::New();
         current->add(callee);
 
         scope = MFunctionEnvironment::New(callee);
         current->add(scope);
 
+        // This reproduce what is done in CallObject::createForFunction
         if (fun->isHeavyweight()) {
-            // We don't yet support inlining of DeclEnv objects.
-            if (fun->isNamedLambda())
-                return abort("DeclEnv scope objects are not yet supported");
+            if (fun->isNamedLambda()) {
+                scope = createDeclEnvObject(scope);
+                if (!scope)
+                    return false;
+            }
 
             scope = createCallObject(callee, scope);
             if (!scope)
                 return false;
         }
     } else {
         scope = MConstant::New(ObjectValue(script_->global()));
         current->add(scope);
@@ -1034,16 +1037,24 @@ IonBuilder::inspectOpcode(JSOp op)
         return jsop_length();
 
       case JSOP_NOT:
         return jsop_not();
 
       case JSOP_THIS:
         return jsop_this();
 
+      case JSOP_CALLEE:
+      {
+        MCallee *callee = MCallee::New();
+        current->add(callee);
+        current->push(callee);
+        return callee;
+      }
+
       case JSOP_GETPROP:
       case JSOP_CALLPROP:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
         return jsop_getprop(name);
       }
 
       case JSOP_SETPROP:
@@ -3734,16 +3745,44 @@ IonBuilder::inlineScriptedCall(AutoObjec
     //  -2 for callee/this
     //  +1 for retval
     JS_ASSERT(current->stackDepth() == origStackDepth - argc - 1);
 
     return true;
 }
 
 MInstruction *
+IonBuilder::createDeclEnvObject(MDefinition *scope)
+{
+    // Create a template CallObject that we'll use to generate inline object
+    // creation.
+
+    RootedScript script(cx, script_);
+    RootedFunction fun(cx, info().fun());
+    RootedObject templateObj(cx, DeclEnvObject::createTemplateObject(cx, fun));
+    if (!templateObj)
+        return NULL;
+
+    // One field is added to the function to handle its name.  This cannot be a
+    // dynamic slot because there is still plenty of room on the DeclEnv object.
+    JS_ASSERT(!templateObj->hasDynamicSlots());
+
+    // Allocate the actual object. It is important that no intervening
+    // instructions could potentially bailout, thus leaking the dynamic slots
+    // pointer.
+    MInstruction *declEnvObj = MNewDeclEnvObject::New(templateObj);
+    current->add(declEnvObj);
+
+    // Initialize the object's reserved slots.
+    current->add(MStoreFixedSlot::New(declEnvObj, DeclEnvObject::enclosingScopeSlot(), scope));
+
+    return declEnvObj;
+}
+
+MInstruction *
 IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
 {
     // Create a template CallObject that we'll use to generate inline object
     // creation.
 
     RootedObject templateObj(cx, CallObject::createTemplateObject(cx, script_));
     if (!templateObj)
         return NULL;
@@ -3761,18 +3800,18 @@ IonBuilder::createCallObject(MDefinition
 
     // Allocate the actual object. It is important that no intervening
     // instructions could potentially bailout, thus leaking the dynamic slots
     // pointer.
     MInstruction *callObj = MNewCallObject::New(templateObj, slots);
     current->add(callObj);
 
     // Initialize the object's reserved slots.
+    current->add(MStoreFixedSlot::New(callObj, CallObject::enclosingScopeSlot(), scope));
     current->add(MStoreFixedSlot::New(callObj, CallObject::calleeSlot(), callee));
-    current->add(MStoreFixedSlot::New(callObj, CallObject::enclosingScopeSlot(), scope));
 
     // Initialize argument slots.
     for (AliasedFormalIter i(script_); i; i++) {
         unsigned slot = i.scopeSlot();
         unsigned formal = i.frameIndex();
         MDefinition *param = current->getSlot(info().argSlot(formal));
         if (slot >= templateObj->numFixedSlots())
             current->add(MStoreSlot::New(slots, slot - templateObj->numFixedSlots(), param));
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -292,16 +292,17 @@ class IonBuilder : public MIRGenerator
     void monitorResult(MInstruction *ins, types::TypeSet *barrier, types::TypeSet *types);
 
     JSObject *getSingletonPrototype(JSFunction *target);
 
     MDefinition *createThisNative();
     MDefinition *createThisScripted(MDefinition *callee);
     MDefinition *createThisScriptedSingleton(HandleFunction target, HandleObject proto, MDefinition *callee);
     MDefinition *createThis(HandleFunction target, MDefinition *callee);
+    MInstruction *createDeclEnvObject(MDefinition *scopeObj);
     MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj);
 
     bool makeCall(HandleFunction target, uint32_t argc, bool constructing);
 
     MDefinition *walkScopeChain(unsigned hops);
 
     MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length);
     MInstruction *addShapeGuard(MDefinition *obj, const Shape *shape, BailoutKind bailoutKind);
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -279,17 +279,17 @@ GetPcScript(JSContext *cx, MutableHandle
 
 // Given a slot index, returns the offset, in bytes, of that slot from an
 // IonJSFrameLayout. Slot distances are uniform across architectures, however,
 // the distance does depend on the size of the frame header.
 static inline int32_t
 OffsetOfFrameSlot(int32_t slot)
 {
     if (slot <= 0)
-        return sizeof(IonJSFrameLayout) + -slot;
+        return -slot;
     return -(slot * STACK_SLOT_SIZE);
 }
 
 static inline uintptr_t
 ReadFrameSlot(IonJSFrameLayout *fp, int32_t slot)
 {
     return *(uintptr_t *)((char *)fp + OffsetOfFrameSlot(slot));
 }
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -264,16 +264,32 @@ class LNewObject : public LInstructionHe
   public:
     LIR_HEADER(NewObject);
 
     MNewObject *mir() const {
         return mir_->toNewObject();
     }
 };
 
+// Allocates a new DeclEnvObject.
+//
+// This instruction generates two possible instruction sets:
+//   (1) An inline allocation of the call object is attempted.
+//   (2) Otherwise, a callVM create a new object.
+//
+class LNewDeclEnvObject : public LInstructionHelper<1, 0, 0>
+{
+  public:
+    LIR_HEADER(NewDeclEnvObject);
+
+    MNewDeclEnvObject *mir() const {
+        return mir_->toNewDeclEnvObject();
+    }
+};
+
 // Allocates a new CallObject. The inputs are:
 //      slots: either a reg representing a HeapSlot *, or a placeholder
 //             meaning that no slots pointer is needed.
 //
 // This instruction generates two possible instruction sets:
 //   (1) If the call object is extensible, this is a callVM to create the
 //       call object.
 //   (2) Otherwise, an inline allocation of the call object is attempted.
--- a/js/src/ion/LIR.h
+++ b/js/src/ion/LIR.h
@@ -360,18 +360,19 @@ class LStackSlot : public LAllocation
         return kind() == DOUBLE_SLOT;
     }
 
     uint32_t slot() const {
         return data();
     }
 };
 
-// Arguments are reverse indexes into the stack, and like LStackSlot, each
-// index is measured in increments of STACK_SLOT_SIZE.
+// Arguments are reverse indexes into the stack, and as opposed to LStackSlot,
+// each index is measured in bytes because we have to index the middle of a
+// Value on 32 bits architectures.
 class LArgument : public LAllocation
 {
   public:
     explicit LArgument(int32_t index)
       : LAllocation(ARGUMENT, index)
     { }
 
     int32_t index() const {
--- a/js/src/ion/LOpcodes.h
+++ b/js/src/ion/LOpcodes.h
@@ -20,16 +20,17 @@
     _(Parameter)                    \
     _(Callee)                       \
     _(TableSwitch)                  \
     _(TableSwitchV)                 \
     _(Goto)                         \
     _(NewArray)                     \
     _(NewObject)                    \
     _(NewSlots)                     \
+    _(NewDeclEnvObject)             \
     _(NewCallObject)                \
     _(NewStringObject)              \
     _(InitProp)                     \
     _(CheckOverRecursed)            \
     _(RecompileCheck)               \
     _(DefVar)                       \
     _(CallKnown)                    \
     _(CallGeneric)                  \
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -154,16 +154,23 @@ LIRGenerator::visitNewArray(MNewArray *i
 bool
 LIRGenerator::visitNewObject(MNewObject *ins)
 {
     LNewObject *lir = new LNewObject();
     return define(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
+LIRGenerator::visitNewDeclEnvObject(MNewDeclEnvObject *ins)
+{
+    LNewDeclEnvObject *lir = new LNewDeclEnvObject();
+    return define(lir, ins) && assignSafepoint(lir, ins);
+}
+
+bool
 LIRGenerator::visitNewCallObject(MNewCallObject *ins)
 {
     LAllocation slots;
     if (ins->slots()->type() == MIRType_Slots)
         slots = useRegister(ins->slots());
     else
         slots = LConstantIndex::Bogus();
 
--- a/js/src/ion/Lowering.h
+++ b/js/src/ion/Lowering.h
@@ -76,16 +76,17 @@ class LIRGenerator : public LIRGenerator
     // intercept without a bunch of explicit gunk in the .cpp.
     bool visitParameter(MParameter *param);
     bool visitCallee(MCallee *callee);
     bool visitGoto(MGoto *ins);
     bool visitTableSwitch(MTableSwitch *tableswitch);
     bool visitNewSlots(MNewSlots *ins);
     bool visitNewArray(MNewArray *ins);
     bool visitNewObject(MNewObject *ins);
+    bool visitNewDeclEnvObject(MNewDeclEnvObject *ins);
     bool visitNewCallObject(MNewCallObject *ins);
     bool visitNewStringObject(MNewStringObject *ins);
     bool visitInitProp(MInitProp *ins);
     bool visitCheckOverRecursed(MCheckOverRecursed *ins);
     bool visitDefVar(MDefVar *ins);
     bool visitPrepareCall(MPrepareCall *ins);
     bool visitPassArg(MPassArg *arg);
     bool visitCreateThis(MCreateThis *ins);
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -5519,16 +5519,42 @@ class MNewSlots : public MNullaryInstruc
     unsigned nslots() const {
         return nslots_;
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
 };
 
+class MNewDeclEnvObject : public MNullaryInstruction
+{
+    CompilerRootObject templateObj_;
+
+    MNewDeclEnvObject(HandleObject templateObj)
+      : MNullaryInstruction(),
+        templateObj_(templateObj)
+    {
+        setResultType(MIRType_Object);
+    }
+
+  public:
+    INSTRUCTION_HEADER(NewDeclEnvObject);
+
+    static MNewDeclEnvObject *New(HandleObject templateObj) {
+        return new MNewDeclEnvObject(templateObj);
+    }
+
+    JSObject *templateObj() {
+        return templateObj_;
+    }
+    AliasSet getAliasSet() const {
+        return AliasSet::None();
+    }
+};
+
 class MNewCallObject : public MUnaryInstruction
 {
     CompilerRootObject templateObj_;
 
     MNewCallObject(HandleObject templateObj, MDefinition *slots)
       : MUnaryInstruction(slots),
         templateObj_(templateObj)
     {
--- a/js/src/ion/MOpcodes.h
+++ b/js/src/ion/MOpcodes.h
@@ -66,16 +66,17 @@ namespace ion {
     _(GuardString)                                                          \
     _(ToDouble)                                                             \
     _(ToInt32)                                                              \
     _(TruncateToInt32)                                                      \
     _(ToString)                                                             \
     _(NewSlots)                                                             \
     _(NewArray)                                                             \
     _(NewObject)                                                            \
+    _(NewDeclEnvObject)                                                     \
     _(NewCallObject)                                                        \
     _(NewStringObject)                                                      \
     _(InitProp)                                                             \
     _(Start)                                                                \
     _(OsrEntry)                                                             \
     _(Nop)                                                                  \
     _(RegExp)                                                               \
     _(RegExpTest)                                                           \
--- a/js/src/ion/VMFunctions.h
+++ b/js/src/ion/VMFunctions.h
@@ -182,16 +182,17 @@ struct VMFunction
   private:
     // Add this to the global list of VMFunctions.
     void addToFunctions();
 };
 
 template <class> struct TypeToDataType { /* Unexpected return type for a VMFunction. */ };
 template <> struct TypeToDataType<bool> { static const DataType result = Type_Bool; };
 template <> struct TypeToDataType<JSObject *> { static const DataType result = Type_Object; };
+template <> struct TypeToDataType<DeclEnvObject *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<JSString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<JSFlatString *> { static const DataType result = Type_Object; };
 template <> struct TypeToDataType<HandleObject> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleString> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandlePropertyName> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleFunction> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; };
 template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; };
--- a/js/src/ion/shared/CodeGenerator-shared.cpp
+++ b/js/src/ion/shared/CodeGenerator-shared.cpp
@@ -69,24 +69,26 @@ CodeGeneratorShared::addOutOfLineCode(Ou
     // because they're probably not relevant any more.
     if (oolIns)
         code->setSource(oolIns->script(), oolIns->pc());
     else
         code->setSource(current ? current->mir()->info().script() : NULL, lastPC_);
     return outOfLineCode_.append(code);
 }
 
+// see OffsetOfFrameSlot
 static inline int32_t
 ToStackIndex(LAllocation *a)
 {
     if (a->isStackSlot()) {
         JS_ASSERT(a->toStackSlot()->slot() >= 1);
         return a->toStackSlot()->slot();
     }
-    return -a->toArgument()->index();
+    JS_ASSERT(-int32_t(sizeof(IonJSFrameLayout)) <= a->toArgument()->index());
+    return -(sizeof(IonJSFrameLayout) + a->toArgument()->index());
 }
 
 bool
 CodeGeneratorShared::encodeSlots(LSnapshot *snapshot, MResumePoint *resumePoint,
                                  uint32_t *startIndex)
 {
     IonSpew(IonSpew_Codegen, "Encoding %u of resume point %p's operands starting from %u",
             resumePoint->numOperands(), (void *) resumePoint, *startIndex);
--- a/js/src/jit-test/tests/basic/bug522136.js
+++ b/js/src/jit-test/tests/basic/bug522136.js
@@ -2,10 +2,9 @@ var Q = 0;
 var thrown = false;
 try {
    (function f(i) { Q = i; if (i == 200000) return; f(i+1); })(1)
 } catch (e) {
     thrown = true;
 }
 
 // Exact behavior of recursion check depends on which JIT we use.
-assertEq(thrown && Q > 10000, true);
-
+assertEq(thrown && Q > 8000, true);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -249,57 +249,75 @@ JS_PUBLIC_DATA(Class) js::CallClass = {
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     NULL                     /* convert: Leave it NULL so we notice if calls ever escape */
 };
 
 Class js::DeclEnvClass = {
     js_Object_str,
-    JSCLASS_HAS_PRIVATE |
     JSCLASS_HAS_RESERVED_SLOTS(DeclEnvObject::RESERVED_SLOTS) |
     JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
     JS_PropertyStub,         /* addProperty */
     JS_PropertyStub,         /* delProperty */
     JS_PropertyStub,         /* getProperty */
     JS_StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     JS_ResolveStub,
     JS_ConvertStub
 };
 
+/*
+ * Create a DeclEnvObject for a JSScript that is not initialized to any
+ * particular callsite. This object can either be initialized (with an enclosing
+ * scope and callee) or used as a template for jit compilation.
+ */
 DeclEnvObject *
-DeclEnvObject::create(JSContext *cx, StackFrame *fp)
+DeclEnvObject::createTemplateObject(JSContext *cx, HandleFunction fun)
 {
-    assertSameCompartment(cx, fp);
-
     RootedTypeObject type(cx, cx->compartment->getNewType(cx, NULL));
     if (!type)
         return NULL;
 
     RootedShape emptyDeclEnvShape(cx);
     emptyDeclEnvShape = EmptyShape::getInitialShape(cx, &DeclEnvClass, NULL,
-                                                    &fp->global(), FINALIZE_KIND,
+                                                    cx->global(), FINALIZE_KIND,
                                                     BaseShape::DELEGATE);
     if (!emptyDeclEnvShape)
         return NULL;
 
     RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, emptyDeclEnvShape, type, NULL));
     if (!obj)
         return NULL;
 
-    obj->asScope().setEnclosingScope(fp->scopeChain());
-    Rooted<jsid> id(cx, AtomToId(fp->fun()->atom()));
-    RootedValue value(cx, ObjectValue(fp->callee()));
-    if (!DefineNativeProperty(cx, obj, id, value, NULL, NULL,
-                              JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
-                              0, 0)) {
+    Rooted<jsid> id(cx, AtomToId(fun->atom()));
+    Class *clasp = obj->getClass();
+    unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
+    if (!obj->putProperty(cx, id, clasp->getProperty, clasp->setProperty,
+                          lambdaSlot(), attrs, 0, 0))
+    {
         return NULL;
     }
 
+    obj->setFixedSlot(lambdaSlot(), ObjectValue(*fun.get()));
+    JS_ASSERT(!obj->hasDynamicSlots());
+    return &obj->asDeclEnv();
+}
+
+DeclEnvObject *
+DeclEnvObject::create(JSContext *cx, StackFrame *fp)
+{
+    assertSameCompartment(cx, fp);
+
+    RootedFunction fun(cx, fp->fun());
+    RootedObject obj(cx, createTemplateObject(cx, fun));
+    if (!obj)
+        return NULL;
+
+    obj->asScope().setEnclosingScope(fp->scopeChain());
     return &obj->asDeclEnv();
 }
 
 WithObject *
 WithObject::create(JSContext *cx, HandleObject proto, HandleObject enclosing, uint32_t depth)
 {
     RootedTypeObject type(cx, proto->getNewType(cx));
     if (!type)
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -213,21 +213,31 @@ class CallObject : public ScopeObject
     static inline size_t offsetOfCallee();
     static inline size_t calleeSlot() {
         return CALLEE_SLOT;
     }
 };
 
 class DeclEnvObject : public ScopeObject
 {
+    // Pre-allocated slot for the named lambda.
+    static const uint32_t LAMBDA_SLOT = 1;
+
   public:
-    static const uint32_t RESERVED_SLOTS = 1;
+    static const uint32_t RESERVED_SLOTS = 2;
     static const gc::AllocKind FINALIZE_KIND = gc::FINALIZE_OBJECT2;
 
+    static DeclEnvObject *
+    createTemplateObject(JSContext *cx, HandleFunction fun);
+
     static DeclEnvObject *create(JSContext *cx, StackFrame *fp);
+
+    static inline size_t lambdaSlot() {
+        return LAMBDA_SLOT;
+    }
 };
 
 class NestedScopeObject : public ScopeObject
 {
   protected:
     static const unsigned DEPTH_SLOT = 1;
 
   public:
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -262,17 +262,17 @@ AssertDynamicScopeMatchesStaticScope(JSS
     /*
      * Ideally, we'd JS_ASSERT(!scope->isScope()) but the enclosing lexical
      * scope chain stops at eval() boundaries. See StaticScopeIter comment.
      */
 #endif
 }
 
 bool
-StackFrame::initCallObject(JSContext *cx)
+StackFrame::initFunctionScopeObjects(JSContext *cx)
 {
     CallObject *callobj = CallObject::createForFunction(cx, this);
     if (!callobj)
         return false;
     pushOnScopeChain(*callobj);
     flags_ |= HAS_CALL_OBJ;
     return true;
 }
@@ -300,17 +300,17 @@ StackFrame::prologue(JSContext *cx, bool
     if (isGlobalFrame()) {
         Probes::enterScript(cx, script, NULL, this);
         return true;
     }
 
     JS_ASSERT(isNonEvalFunctionFrame());
     AssertDynamicScopeMatchesStaticScope(script, scopeChain());
 
-    if (fun()->isHeavyweight() && !initCallObject(cx))
+    if (fun()->isHeavyweight() && !initFunctionScopeObjects(cx))
         return false;
 
     if (isConstructing()) {
         RootedObject callee(cx, &this->callee());
         JSObject *obj = js_CreateThisForFunction(cx, callee, newType);
         if (!obj)
             return false;
         functionThis() = ObjectValue(*obj);
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -381,17 +381,17 @@ class StackFrame
     void epilogue(JSContext *cx);
 
     /* Subsets of 'prologue' called from jit code. */
     inline bool jitHeavyweightFunctionPrologue(JSContext *cx);
     bool jitStrictEvalPrologue(JSContext *cx);
 
     /* Called from IonMonkey to transition from bailouts. */
     void initFromBailout(JSContext *cx, ion::SnapshotIterator &iter);
-    bool initCallObject(JSContext *cx);
+    bool initFunctionScopeObjects(JSContext *cx);
 
     /* Initialize local variables of newly-pushed frame. */
     void initVarsToUndefined();
 
     /*
      * Stack frame type
      *
      * A stack frame may have one of three types, which determines which