Bug 807443 - IonMonkey, Compile named lambdas. r=dvander
authorNicolas B. Pierron <nicolas.b.pierron@mozilla.com>
Sat, 08 Dec 2012 18:28:37 -0800
changeset 115400 93cac86bdd9547c3a165b82af1cc1a5dcd90a228
parent 115399 ffcf9d8125335c59c4c4ccb209602d6777471ba8
child 115401 2bd521c3118f4337500f879c6a67dd64d2edc4f7
push id19336
push usernpierron@mozilla.com
push dateSun, 09 Dec 2012 02:32:12 +0000
treeherdermozilla-inbound@93cac86bdd95 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdvander
bugs807443
milestone20.0a1
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
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/jit-test/tests/jaeger/bug819035.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
@@ -1723,16 +1723,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(callee, 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,50 @@ IonBuilder::inlineScriptedCall(AutoObjec
     //  -2 for callee/this
     //  +1 for retval
     JS_ASSERT(current->stackDepth() == origStackDepth - argc - 1);
 
     return true;
 }
 
 MInstruction *
+IonBuilder::createDeclEnvObject(MDefinition *callee, 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;
+
+    // Add dummy values on the slot of the template object such as we do not try
+    // mark uninitialized values.
+    templateObj->setFixedSlot(DeclEnvObject::enclosingScopeSlot(), MagicValue(JS_GENERIC_MAGIC));
+    templateObj->setFixedSlot(DeclEnvObject::lambdaSlot(), MagicValue(JS_GENERIC_MAGIC));
+
+    // 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));
+    current->add(MStoreFixedSlot::New(declEnvObj, DeclEnvObject::lambdaSlot(), callee));
+
+    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 +3806,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 *callee, 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
@@ -5523,16 +5523,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);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/jaeger/bug819035.js
@@ -0,0 +1,38 @@
+// This test case check the difference between fp->callee() and fp->fun() on
+// lambdas.
+(function (a, u) {
+  var sum = function (array, callback) {
+    for (var i = 0; i < array.length; i++)
+      callback(array[i]);
+  };
+  (function () {
+    (function r(t) {
+       t !== u,
+         sum(t, function (v)  r(v) );
+    })(arguments);
+  })(a);
+}) (
+    [
+        [
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1]
+        ], [
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1],
+            [1], [1], [1], [1], [1], [1], [1], [1], [1], [1]
+        ]
+    ]
+);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -249,57 +249,76 @@ 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)) {
+    // Assign a fixed slot to a property with the same name as the lambda.
+    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;
     }
 
+    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());
+    obj->setFixedSlot(lambdaSlot(), ObjectValue(fp->callee()));
     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