Bug 820846 - Modify the ListBase IC to work with [OverrideBuiltins] bindings. r=jandem.
authorPeter Van der Beken <peterv@propagandism.org>
Mon, 29 Apr 2013 15:17:59 +0200
changeset 141813 e604349e5a7975326b0624f63966cc6410161622
parent 141812 e500bc167b4e5a485e02996c65104d957786ce61
child 141814 9fc3d4e62179a7a14f8fd3307a394807b2bf7aa8
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs820846
milestone23.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 820846 - Modify the ListBase IC to work with [OverrideBuiltins] bindings. r=jandem.
dom/bindings/DOMJSProxyHandler.cpp
js/src/ion/BaselineIC.cpp
js/src/ion/BaselineIC.h
js/src/ion/IonCaches.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/methodjit/PolyIC.cpp
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -32,21 +32,45 @@ DefineStaticJSVals(JSContext* cx)
   JSAutoRequest ar(cx);
 
   return InternJSString(cx, s_length_id, "length");
 }
 
 
 int HandlerFamily;
 
+js::ListBaseShadowsResult
+DOMListShadows(JSContext* cx, JSHandleObject proxy, JSHandleId id)
+{
+  JS::Value v = js::GetProxyExtra(proxy, JSPROXYSLOT_EXPANDO);
+  if (v.isObject()) {
+    JSBool hasOwn;
+    if (!JS_AlreadyHasOwnPropertyById(cx, &v.toObject(), id, &hasOwn))
+      return js::ShadowCheckFailed;
+
+    return hasOwn ? js::Shadows : js::DoesntShadow;
+  }
+
+  if (v.isUndefined()) {
+    return js::DoesntShadow;
+  }
+
+  bool hasOwn;
+  if (!GetProxyHandler(proxy)->hasOwn(cx, proxy, id, &hasOwn))
+    return js::ShadowCheckFailed;
+
+  return hasOwn ? js::Shadows : js::DoesntShadowUnique;
+}
+
 // Store the information for the specialized ICs.
 struct SetListBaseInformation
 {
   SetListBaseInformation() {
-    js::SetListBaseInformation((void*) &HandlerFamily, js::JSSLOT_PROXY_EXTRA + JSPROXYSLOT_EXPANDO);
+    js::SetListBaseInformation((void*) &HandlerFamily,
+                               js::JSSLOT_PROXY_EXTRA + JSPROXYSLOT_EXPANDO, DOMListShadows);
   }
 };
 
 SetListBaseInformation gSetListBaseInformation;
 
 // static
 JSObject*
 DOMProxyHandler::GetAndClearExpandoObject(JSObject* obj)
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -296,18 +296,23 @@ ICStub::trace(JSTracer *trc)
       }
       case ICStub::GetProp_NativePrototype: {
         ICGetProp_NativePrototype *propStub = toGetProp_NativePrototype();
         MarkShape(trc, &propStub->shape(), "baseline-getpropnativeproto-stub-shape");
         MarkObject(trc, &propStub->holder(), "baseline-getpropnativeproto-stub-holder");
         MarkShape(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape");
         break;
       }
-      case ICStub::GetProp_CallListBaseNative: {
-        ICGetProp_CallListBaseNative *propStub = toGetProp_CallListBaseNative();
+      case ICStub::GetProp_CallListBaseNative:
+      case ICStub::GetProp_CallListBaseWithGenerationNative: {
+        ICGetPropCallListBaseNativeStub *propStub;
+        if (kind() ==  ICStub::GetProp_CallListBaseNative)
+            propStub = toGetProp_CallListBaseNative();
+        else
+            propStub = toGetProp_CallListBaseWithGenerationNative();
         MarkShape(trc, &propStub->shape(), "baseline-getproplistbasenative-stub-shape");
         if (propStub->expandoShape()) {
             MarkShape(trc, &propStub->expandoShape(),
                       "baseline-getproplistbasenative-stub-expandoshape");
         }
         MarkObject(trc, &propStub->holder(), "baseline-getproplistbasenative-stub-holder");
         MarkShape(trc, &propStub->holderShape(), "baseline-getproplistbasenative-stub-holdershape");
         MarkObject(trc, &propStub->getter(), "baseline-getproplistbasenative-stub-getter");
@@ -602,16 +607,17 @@ ICStubCompiler::leaveStubFrame(MacroAsse
 
 void
 ICStubCompiler::guardProfilingEnabled(MacroAssembler &masm, Register scratch, Label *skip)
 {
     // This should only be called from the following stubs.
     JS_ASSERT(kind == ICStub::Call_Scripted      || kind == ICStub::Call_AnyScripted     ||
               kind == ICStub::Call_Native        || kind == ICStub::GetProp_CallScripted ||
               kind == ICStub::GetProp_CallNative || kind == ICStub::GetProp_CallListBaseNative ||
+              kind == ICStub::GetProp_CallListBaseWithGenerationNative ||
               kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
 
     // Guard on bit in frame that indicates if the SPS frame was pushed in the first
     // place.  This code is expected to be called from within a stub that has already
     // entered a stub frame.
     JS_ASSERT(entersStubFrame_);
     masm.loadPtr(Address(BaselineFrameReg, 0), scratch);
     masm.branchTest32(Assembler::Zero,
@@ -3031,16 +3037,18 @@ GetListBaseProto(JSObject *obj)
     JS_ASSERT(IsCacheableListBase(obj));
     return obj->getTaggedProto().toObjectOrNull();
 }
 
 static void
 GenerateListBaseChecks(JSContext *cx, MacroAssembler &masm, Register object,
                        Address checkProxyHandlerAddr,
                        Address checkExpandoShapeAddr,
+                       Address *expandoAndGenerationAddr,
+                       Address *generationAddr,
                        Register scratch,
                        GeneralRegisterSet &listBaseRegSet,
                        Label *checkFailed)
 {
     // Guard the following:
     //      1. The object is a ListBase.
     //      2. The object does not have expando properties, or has an expando
     //          which is known to not have the desired property.
@@ -3054,17 +3062,32 @@ GenerateListBaseChecks(JSContext *cx, Ma
     // For the remaining code, we need to reserve some registers to load a value.
     // This is ugly, but unvaoidable.
     ValueOperand tempVal = listBaseRegSet.takeAnyValue();
     masm.pushValue(tempVal);
 
     Label failListBaseCheck;
     Label listBaseOk;
 
-    masm.loadValue(expandoAddr, tempVal);
+    if (expandoAndGenerationAddr) {
+        JS_ASSERT(generationAddr);
+
+        masm.loadPtr(*expandoAndGenerationAddr, tempVal.scratchReg());
+        masm.branchPrivatePtr(Assembler::NotEqual, expandoAddr, tempVal.scratchReg(),
+                              &failListBaseCheck);
+
+        masm.load32(*generationAddr, scratch);
+        masm.branch32(Assembler::NotEqual,
+                      Address(tempVal.scratchReg(), offsetof(ExpandoAndGeneration, expando)),
+                      scratch, &failListBaseCheck);
+
+        masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal);
+    } else {
+        masm.loadValue(expandoAddr, tempVal);
+    }
 
     // If the incoming object does not have an expando object then we're sure we're not
     // shadowing.
     masm.branchTestUndefined(Assembler::Equal, tempVal, &listBaseOk);
 
     // The reference object used to generate this check may not have had an
     // expando object at all, in which case the presence of a non-undefined
     // expando value in the incoming object is automatically a failure.
@@ -3088,42 +3111,44 @@ GenerateListBaseChecks(JSContext *cx, Ma
 }
 
 // Look up a property's shape on an object, being careful never to do any effectful
 // operations.  This procedure not yielding a shape should not be taken as a lack of
 // existence of the property on the object.
 static bool
 EffectlesslyLookupProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
                            MutableHandleObject holder, MutableHandleShape shape,
-                           bool *checkListBase=NULL)
+                           bool *checkListBase=NULL, bool *listBaseHasGeneration=NULL)
 {
     shape.set(NULL);
     holder.set(NULL);
 
     bool isListBase = false;
     if (checkListBase)
         *checkListBase = false;
 
     // Check for list base if asked to.
     RootedObject checkObj(cx, obj);
     if (checkListBase && IsCacheableListBase(obj)) {
+        JS_ASSERT(listBaseHasGeneration);
+
         *checkListBase = isListBase = true;
         if (obj->hasUncacheableProto())
             return true;
 
-        // Expando objects just hold any extra properties the object has been given by a script,
-        // and have no prototype or anything else that will complicate property lookups on them.
-        Value expandoVal = obj->getFixedSlot(GetListBaseExpandoSlot());
-
-        JS_ASSERT_IF(expandoVal.isObject(),
-                     expandoVal.toObject().isNative() && !expandoVal.toObject().getProto());
-
-        if (expandoVal.isObject() && expandoVal.toObject().nativeContains(cx, name))
+        RootedId id(cx, NameToId(name));
+        ListBaseShadowsResult shadows =
+            GetListBaseShadowsCheck()(cx, obj, id);
+        if (shadows == ShadowCheckFailed)
+            return false;
+        if (shadows == Shadows)
             return true;
 
+        *listBaseHasGeneration = (shadows == DoesntShadowUnique);
+
         checkObj = GetListBaseProto(obj);
     }
 
     if (!isListBase && !obj->isNative())
         return true;
 
     if (checkObj->hasIdempotentProtoChain()) {
         if (!JSObject::lookupProperty(cx, checkObj, name, holder, shape))
@@ -5082,31 +5107,59 @@ TryAttachLengthStub(JSContext *cx, Handl
         stub->addNewStub(newStub);
         return true;
     }
 
     return true;
 }
 
 static bool
+UpdateExistingGenerationalListBaseStub(ICGetProp_Fallback *stub,
+                                       HandleObject obj)
+{
+    Value expandoSlot = obj->getFixedSlot(GetListBaseExpandoSlot());
+    JS_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
+    ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate();
+    for (ICStubConstIterator iter = stub->beginChainConst(); !iter.atEnd(); iter++) {
+        if (iter->isGetProp_CallListBaseWithGenerationNative()) {
+            ICGetProp_CallListBaseWithGenerationNative* updateStub =
+                iter->toGetProp_CallListBaseWithGenerationNative();
+            if (updateStub->expandoAndGeneration() == expandoAndGeneration) {
+                // Update generation
+                uint32_t generation = expandoAndGeneration->generation;
+                IonSpew(IonSpew_BaselineIC,
+                        "  Updating existing stub with generation, old value: %i, "
+                        "new value: %i", updateStub->generation(),
+                        generation);
+                updateStub->setGeneration(generation);
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+static bool
 TryAttachNativeGetPropStub(JSContext *cx, HandleScript script, jsbytecode *pc,
                            ICGetProp_Fallback *stub, HandlePropertyName name,
                            HandleValue val, HandleValue res, bool *attached)
 {
     JS_ASSERT(!*attached);
 
     if (!val.isObject())
         return true;
 
     RootedObject obj(cx, &val.toObject());
 
     bool isListBase;
+    bool listBaseHasGeneration;
     RootedShape shape(cx);
     RootedObject holder(cx);
-    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isListBase))
+    if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape, &isListBase,
+                                    &listBaseHasGeneration))
         return false;
 
     if (!isListBase && !obj->isNative())
         return true;
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
     if (!isListBase && IsCacheableGetPropReadSlot(obj, holder, shape)) {
         bool isFixedSlot;
@@ -5153,29 +5206,37 @@ TryAttachNativeGetPropStub(JSContext *cx
     }
 
     // Try handling JSNative getters.
     if (cacheableCall && !isScripted) {
         RootedFunction callee(cx, shape->getterObject()->toFunction());
         JS_ASSERT(obj != holder);
         JS_ASSERT(callee->isNative());
 
+        IonSpew(IonSpew_BaselineIC, "  Generating GetProp(%s%s/NativeGetter %p) stub",
+                isListBase ? "ListBaseObj" : "NativeObj",
+                isListBase && listBaseHasGeneration ? "WithGeneration" : "",
+                callee->native());
+
         ICStub *newStub = NULL;
         if (isListBase) {
-            IonSpew(IonSpew_BaselineIC, "  Generating GetProp(ListBaseObj/NativeGetter %p) stub",
-                        callee->native());
-
-            ICGetProp_CallListBaseNative::Compiler compiler(cx, monitorStub, obj, holder, callee,
+            ICStub::Kind kind;
+            if (listBaseHasGeneration) {
+                if (UpdateExistingGenerationalListBaseStub(stub, obj)) {
+                    *attached = true;
+                    return true;
+                }
+                kind = ICStub::GetProp_CallListBaseWithGenerationNative;
+            } else {
+                kind = ICStub::GetProp_CallListBaseNative;
+            }
+            ICGetPropCallListBaseNativeCompiler compiler(cx, kind, monitorStub, obj, holder, callee,
                                                             pc - script->code);
             newStub = compiler.getStub(compiler.getStubSpace(script));
-
         } else {
-            IonSpew(IonSpew_BaselineIC, "  Generating GetProp(NativeObj/NativeGetter %p) stub",
-                        callee->native());
-
             ICGetProp_CallNative::Compiler compiler(cx, monitorStub, obj, holder, callee,
                                                     pc - script->code);
             newStub = compiler.getStub(compiler.getStubSpace(script));
         }
         if (!newStub)
             return false;
         stub->addNewStub(newStub);
         *attached = true;
@@ -5684,17 +5745,19 @@ ICGetProp_CallNative::Compiler::generate
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
-ICGetProp_CallListBaseNative::Compiler::generateStubCode(MacroAssembler &masm)
+ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm,
+                                                      Address* expandoAndGenerationAddr,
+                                                      Address* generationAddr)
 {
     Label failure;
     GeneralRegisterSet regs(availableGeneralRegs(1));
     Register scratch;
     if (regs.has(BaselineTailCallReg)) {
         regs.take(BaselineTailCallReg);
         scratch = regs.takeAny();
         regs.add(BaselineTailCallReg);
@@ -5717,16 +5780,17 @@ ICGetProp_CallListBaseNative::Compiler::
         GeneralRegisterSet listBaseRegSet(GeneralRegisterSet::All());
         listBaseRegSet.take(BaselineStubReg);
         listBaseRegSet.take(objReg);
         listBaseRegSet.take(scratch);
         GenerateListBaseChecks(
                 cx, masm, objReg,
                 Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfProxyHandler()),
                 Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfExpandoShape()),
+                expandoAndGenerationAddr, generationAddr,
                 scratch,
                 listBaseRegSet,
                 &failure);
     }
 
     Register holderReg = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallListBaseNative::offsetOfHolder()),
                  holderReg);
@@ -5776,16 +5840,64 @@ ICGetProp_CallListBaseNative::Compiler::
 
     // Failure case - jump to next stub
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 bool
+ICGetPropCallListBaseNativeCompiler::generateStubCode(MacroAssembler &masm)
+{
+    if (kind == ICStub::GetProp_CallListBaseNative)
+        return generateStubCode(masm, NULL, NULL);
+
+    Address internalStructAddress(BaselineStubReg,
+        ICGetProp_CallListBaseWithGenerationNative::offsetOfInternalStruct());
+    Address generationAddress(BaselineStubReg,
+        ICGetProp_CallListBaseWithGenerationNative::offsetOfGeneration());
+    return generateStubCode(masm, &internalStructAddress, &generationAddress);
+}
+
+ICStub*
+ICGetPropCallListBaseNativeCompiler::getStub(ICStubSpace *space)
+{
+    RootedShape shape(cx, obj_->lastProperty());
+    RootedShape holderShape(cx, holder_->lastProperty());
+
+    Value expandoSlot = obj_->getFixedSlot(GetListBaseExpandoSlot());
+    RootedShape expandoShape(cx, NULL);
+    ExpandoAndGeneration *expandoAndGeneration;
+    int32_t generation;
+    Value expandoVal;
+    if (kind == ICStub::GetProp_CallListBaseNative) {
+        expandoVal = expandoSlot;
+    } else {
+        JS_ASSERT(kind == ICStub::GetProp_CallListBaseWithGenerationNative);
+        JS_ASSERT(!expandoSlot.isObject() && !expandoSlot.isUndefined());
+        expandoAndGeneration = (ExpandoAndGeneration*)expandoSlot.toPrivate();
+        expandoVal = expandoAndGeneration->expando;
+        generation = expandoAndGeneration->generation;
+    }
+
+    if (expandoVal.isObject())
+        expandoShape = expandoVal.toObject().lastProperty();
+
+    if (kind == ICStub::GetProp_CallListBaseNative) {
+        return ICGetProp_CallListBaseNative::New(
+            space, getStubCode(), firstMonitorStub_, shape, GetProxyHandler(obj_),
+            expandoShape, holder_, holderShape, getter_, pcOffset_);
+    }
+
+    return ICGetProp_CallListBaseWithGenerationNative::New(
+        space, getStubCode(), firstMonitorStub_, shape, GetProxyHandler(obj_),
+        expandoAndGeneration, generation, expandoShape, holder_, holderShape, getter_,
+        pcOffset_);
+}
+bool
 ICGetProp_ArgumentsLength::Compiler::generateStubCode(MacroAssembler &masm)
 {
     Label failure;
     if (which_ == ICGetProp_ArgumentsLength::Magic) {
         // Ensure that this is lazy arguments.
         masm.branchTestMagicValue(Assembler::NotEqual, R0, JS_OPTIMIZED_ARGUMENTS, &failure);
 
         // Ensure that frame has not loaded different arguments object since.
@@ -7760,39 +7872,49 @@ ICCall_Scripted::ICCall_Scripted(IonCode
 
 ICCall_Native::ICCall_Native(IonCode *stubCode, ICStub *firstMonitorStub, HandleFunction callee,
                              uint32_t pcOffset)
   : ICMonitoredStub(ICStub::Call_Native, stubCode, firstMonitorStub),
     callee_(callee),
     pcOffset_(pcOffset)
 { }
 
-ICGetProp_CallListBaseNative::ICGetProp_CallListBaseNative(IonCode *stubCode, ICStub *firstMonitorStub,
-                                                           HandleShape shape, BaseProxyHandler *proxyHandler,
-                                                           HandleShape expandoShape, HandleObject holder,
-                                                           HandleShape holderShape, HandleFunction getter,
-                                                           uint32_t pcOffset)
-  : ICMonitoredStub(GetProp_CallListBaseNative, stubCode, firstMonitorStub),
+ICGetPropCallListBaseNativeStub::ICGetPropCallListBaseNativeStub(Kind kind, IonCode *stubCode,
+                                                                 ICStub *firstMonitorStub,
+                                                                 HandleShape shape,
+                                                                 BaseProxyHandler *proxyHandler,
+                                                                 HandleShape expandoShape,
+                                                                 HandleObject holder,
+                                                                 HandleShape holderShape,
+                                                                 HandleFunction getter,
+                                                                 uint32_t pcOffset)
+  : ICMonitoredStub(kind, stubCode, firstMonitorStub),
     shape_(shape),
     proxyHandler_(proxyHandler),
     expandoShape_(expandoShape),
     holder_(holder),
     holderShape_(holderShape),
     getter_(getter),
     pcOffset_(pcOffset)
 { }
 
-ICGetProp_CallListBaseNative::Compiler::Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj,
-                                                 HandleObject holder, HandleFunction getter,
-                                                 uint32_t pcOffset)
-  : ICStubCompiler(cx, ICStub::GetProp_CallListBaseNative),
+ICGetPropCallListBaseNativeCompiler::ICGetPropCallListBaseNativeCompiler(JSContext *cx,
+                                                                         ICStub::Kind kind,
+                                                                         ICStub *firstMonitorStub,
+                                                                         HandleObject obj,
+                                                                         HandleObject holder,
+                                                                         HandleFunction getter,
+                                                                         uint32_t pcOffset)
+  : ICStubCompiler(cx, kind),
     firstMonitorStub_(firstMonitorStub),
     obj_(cx, obj),
     holder_(cx, holder),
     getter_(cx, getter),
     pcOffset_(pcOffset)
 {
+    JS_ASSERT(kind == ICStub::GetProp_CallListBaseNative ||
+              kind == ICStub::GetProp_CallListBaseWithGenerationNative);
     JS_ASSERT(obj_->isProxy());
     JS_ASSERT(GetProxyHandler(obj_)->family() == GetListBaseHandlerFamily());
 }
 
 } // namespace ion
 } // namespace js
--- a/js/src/ion/BaselineIC.h
+++ b/js/src/ion/BaselineIC.h
@@ -360,16 +360,17 @@ class ICEntry
     _(GetProp_TypedArrayLength) \
     _(GetProp_String)           \
     _(GetProp_StringLength)     \
     _(GetProp_Native)           \
     _(GetProp_NativePrototype)  \
     _(GetProp_CallScripted)     \
     _(GetProp_CallNative)       \
     _(GetProp_CallListBaseNative)\
+    _(GetProp_CallListBaseWithGenerationNative)\
     _(GetProp_ArgumentsLength)  \
                                 \
     _(SetProp_Fallback)         \
     _(SetProp_Native)           \
     _(SetProp_NativeAdd)        \
     _(SetProp_CallScripted)     \
     _(SetProp_CallNative)       \
                                 \
@@ -723,16 +724,17 @@ class ICStub
           case Call_Scripted:
           case Call_AnyScripted:
           case Call_Native:
           case Call_Fallback:
           case UseCount_Fallback:
           case GetProp_CallScripted:
           case GetProp_CallNative:
           case GetProp_CallListBaseNative:
+          case GetProp_CallListBaseWithGenerationNative:
           case SetProp_CallScripted:
           case SetProp_CallNative:
             return true;
           default:
             return false;
         }
     }
 
@@ -4158,17 +4160,17 @@ class ICGetProp_CallNative : public ICGe
             RootedShape shape(cx, obj_->lastProperty());
             RootedShape holderShape(cx, holder_->lastProperty());
             return ICGetProp_CallNative::New(space, getStubCode(), firstMonitorStub_, shape,
                                                holder_, holderShape, getter_, pcOffset_);
         }
     };
 };
 
-class ICGetProp_CallListBaseNative : public ICMonitoredStub
+class ICGetPropCallListBaseNativeStub : public ICMonitoredStub
 {
   friend class ICStubSpace;
   protected:
     // Shape of the ListBase proxy
     HeapPtrShape shape_;
 
     // Proxy handler to check against.
     BaseProxyHandler *proxyHandler_;
@@ -4181,36 +4183,23 @@ class ICGetProp_CallListBaseNative : pub
     HeapPtrShape holderShape_;
 
     // Function to call.
     HeapPtrFunction getter_;
 
     // PC offset of call
     uint32_t pcOffset_;
 
-    ICGetProp_CallListBaseNative(IonCode *stubCode, ICStub *firstMonitorStub,
-                                 HandleShape shape, BaseProxyHandler *proxyHandler,
-                                 HandleShape expandoShape, HandleObject holder,
-                                 HandleShape holderShape, HandleFunction getter,
-                                 uint32_t pcOffset);
+    ICGetPropCallListBaseNativeStub(ICStub::Kind kind, IonCode *stubCode,
+                                    ICStub *firstMonitorStub, HandleShape shape,
+                                    BaseProxyHandler *proxyHandler, HandleShape expandoShape,
+                                    HandleObject holder, HandleShape holderShape,
+                                    HandleFunction getter, uint32_t pcOffset);
 
   public:
-    static inline ICGetProp_CallListBaseNative *New(
-            ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub,
-            HandleShape shape, BaseProxyHandler *proxyHandler,
-            HandleShape expandoShape, HandleObject holder, HandleShape holderShape,
-            HandleFunction getter, uint32_t pcOffset)
-    {
-        if (!code)
-            return NULL;
-        return space->allocate<ICGetProp_CallListBaseNative>(code, firstMonitorStub, shape,
-                                                   proxyHandler, expandoShape, holder,
-                                                   holderShape, getter, pcOffset);
-    }
-
     HeapPtrShape &shape() {
         return shape_;
     }
     HeapPtrShape &expandoShape() {
         return expandoShape_;
     }
     HeapPtrObject &holder() {
         return holder_;
@@ -4221,65 +4210,138 @@ class ICGetProp_CallListBaseNative : pub
     HeapPtrFunction &getter() {
         return getter_;
     }
     uint32_t pcOffset() const {
         return pcOffset_;
     }
 
     static size_t offsetOfShape() {
-        return offsetof(ICGetProp_CallListBaseNative, shape_);
+        return offsetof(ICGetPropCallListBaseNativeStub, shape_);
     }
     static size_t offsetOfProxyHandler() {
-        return offsetof(ICGetProp_CallListBaseNative, proxyHandler_);
+        return offsetof(ICGetPropCallListBaseNativeStub, proxyHandler_);
     }
     static size_t offsetOfExpandoShape() {
-        return offsetof(ICGetProp_CallListBaseNative, expandoShape_);
+        return offsetof(ICGetPropCallListBaseNativeStub, expandoShape_);
     }
     static size_t offsetOfHolder() {
-        return offsetof(ICGetProp_CallListBaseNative, holder_);
+        return offsetof(ICGetPropCallListBaseNativeStub, holder_);
     }
     static size_t offsetOfHolderShape() {
-        return offsetof(ICGetProp_CallListBaseNative, holderShape_);
+        return offsetof(ICGetPropCallListBaseNativeStub, holderShape_);
     }
     static size_t offsetOfGetter() {
-        return offsetof(ICGetProp_CallListBaseNative, getter_);
+        return offsetof(ICGetPropCallListBaseNativeStub, getter_);
     }
     static size_t offsetOfPCOffset() {
-        return offsetof(ICGetProp_CallListBaseNative, pcOffset_);
-    }
-
-    class Compiler : public ICStubCompiler {
-      protected:
-        ICStub *firstMonitorStub_;
-        RootedObject obj_;
-        RootedObject holder_;
-        RootedFunction getter_;
-        uint32_t pcOffset_;
-
-        bool generateStubCode(MacroAssembler &masm);
-
-      public:
-        Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj,
-                 HandleObject holder, HandleFunction getter, uint32_t pcOffset);
-
-        ICStub *getStub(ICStubSpace *space) {
-            RootedShape shape(cx, obj_->lastProperty());
-            RootedShape holderShape(cx, holder_->lastProperty());
-
-            Value expandoVal = obj_->getFixedSlot(GetListBaseExpandoSlot());
-            RootedShape expandoShape(cx, NULL);
-            if (expandoVal.isObject())
-                expandoShape = expandoVal.toObject().lastProperty();
-
-            return ICGetProp_CallListBaseNative::New(
-                        space, getStubCode(), firstMonitorStub_, shape, GetProxyHandler(obj_),
-                        expandoShape, holder_, holderShape, getter_, pcOffset_);
-        }
-    };
+        return offsetof(ICGetPropCallListBaseNativeStub, pcOffset_);
+    }
+};
+
+class ICGetProp_CallListBaseNative : public ICGetPropCallListBaseNativeStub
+{
+    friend class ICStubSpace;
+    ICGetProp_CallListBaseNative(IonCode *stubCode, ICStub *firstMonitorStub, HandleShape shape,
+                                 BaseProxyHandler *proxyHandler, HandleShape expandoShape,
+                                 HandleObject holder, HandleShape holderShape,
+                                 HandleFunction getter, uint32_t pcOffset)
+      : ICGetPropCallListBaseNativeStub(ICStub::GetProp_CallListBaseNative, stubCode,
+                                        firstMonitorStub, shape, proxyHandler, expandoShape,
+                                        holder, holderShape, getter, pcOffset)
+    {}
+
+  public:
+    static inline ICGetProp_CallListBaseNative *New(
+            ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub,
+            HandleShape shape, BaseProxyHandler *proxyHandler,
+            HandleShape expandoShape, HandleObject holder, HandleShape holderShape,
+            HandleFunction getter, uint32_t pcOffset)
+    {
+        if (!code)
+            return NULL;
+        return space->allocate<ICGetProp_CallListBaseNative>(code, firstMonitorStub, shape,
+                                                   proxyHandler, expandoShape, holder,
+                                                   holderShape, getter, pcOffset);
+    }
+};
+
+class ICGetProp_CallListBaseWithGenerationNative : public ICGetPropCallListBaseNativeStub
+{
+  protected:
+    ExpandoAndGeneration *expandoAndGeneration_;
+    uint32_t generation_;
+
+  public:
+    ICGetProp_CallListBaseWithGenerationNative(IonCode *stubCode, ICStub *firstMonitorStub,
+                                               HandleShape shape, BaseProxyHandler *proxyHandler,
+                                               ExpandoAndGeneration *expandoAndGeneration,
+                                               uint32_t generation, HandleShape expandoShape,
+                                               HandleObject holder, HandleShape holderShape,
+                                               HandleFunction getter, uint32_t pcOffset)
+      : ICGetPropCallListBaseNativeStub(ICStub::GetProp_CallListBaseWithGenerationNative,
+                                        stubCode, firstMonitorStub, shape, proxyHandler,
+                                        expandoShape, holder, holderShape, getter, pcOffset),
+        expandoAndGeneration_(expandoAndGeneration),
+        generation_(generation)
+    {
+    }
+
+    static inline ICGetProp_CallListBaseWithGenerationNative *New(
+            ICStubSpace *space, IonCode *code, ICStub *firstMonitorStub,
+            HandleShape shape, BaseProxyHandler *proxyHandler,
+            ExpandoAndGeneration *expandoAndGeneration, uint32_t generation,
+            HandleShape expandoShape, HandleObject holder, HandleShape holderShape,
+            HandleFunction getter, uint32_t pcOffset)
+    {
+        if (!code)
+            return NULL;
+        return space->allocate<ICGetProp_CallListBaseWithGenerationNative>(code, firstMonitorStub,
+                                                   shape, proxyHandler, expandoAndGeneration,
+                                                   generation, expandoShape, holder, holderShape,
+                                                   getter, pcOffset);
+    }
+
+    void *expandoAndGeneration() const {
+        return expandoAndGeneration_;
+    }
+    uint32_t generation() const {
+        return generation_;
+    }
+
+    void setGeneration(uint32_t value) {
+        generation_ = value;
+    }
+
+    static size_t offsetOfInternalStruct() {
+        return offsetof(ICGetProp_CallListBaseWithGenerationNative, expandoAndGeneration_);
+    }
+    static size_t offsetOfGeneration() {
+        return offsetof(ICGetProp_CallListBaseWithGenerationNative, generation_);
+    }
+};
+
+class ICGetPropCallListBaseNativeCompiler : public ICStubCompiler {
+    ICStub *firstMonitorStub_;
+    RootedObject obj_;
+    RootedObject holder_;
+    RootedFunction getter_;
+    uint32_t pcOffset_;
+
+    bool generateStubCode(MacroAssembler &masm, Address* internalStructAddr,
+                          Address* generationAddr);
+    bool generateStubCode(MacroAssembler &masm);
+
+  public:
+    ICGetPropCallListBaseNativeCompiler(JSContext *cx, ICStub::Kind kind,
+                                        ICStub *firstMonitorStub, HandleObject obj,
+                                        HandleObject holder, HandleFunction getter,
+                                        uint32_t pcOffset);
+
+    ICStub *getStub(ICStubSpace *space);
 };
 
 class ICGetProp_ArgumentsLength : public ICStub
 {
   friend class ICStubSpace;
   public:
     enum Which { Normal, Strict, Magic };
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -629,38 +629,52 @@ GenerateListBaseChecks(JSContext *cx, Ma
 {
     MOZ_ASSERT(IsCacheableListBase(obj));
 
     // Guard the following:
     //      1. The object is a ListBase.
     //      2. The object does not have expando properties, or has an expando
     //          which is known to not have the desired property.
     Address handlerAddr(object, JSObject::getFixedSlotOffset(JSSLOT_PROXY_HANDLER));
-    Address expandoAddr(object, JSObject::getFixedSlotOffset(GetListBaseExpandoSlot()));
+    Address expandoSlotAddr(object, JSObject::getFixedSlotOffset(GetListBaseExpandoSlot()));
 
     // Check that object is a ListBase.
     masm.branchPrivatePtr(Assembler::NotEqual, handlerAddr, ImmWord(GetProxyHandler(obj)), stubFailure);
 
     // For the remaining code, we need to reserve some registers to load a value.
     // This is ugly, but unvaoidable.
     RegisterSet listBaseRegSet(RegisterSet::All());
     listBaseRegSet.take(AnyRegister(object));
     ValueOperand tempVal = listBaseRegSet.takeValueOperand();
     masm.pushValue(tempVal);
 
     Label failListBaseCheck;
     Label listBaseOk;
 
-    masm.loadValue(expandoAddr, tempVal);
+    Value expandoVal = obj->getFixedSlot(GetListBaseExpandoSlot());
+    masm.loadValue(expandoSlotAddr, tempVal);
+
+    if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
+        masm.branchTestValue(Assembler::NotEqual, tempVal, expandoVal, &failListBaseCheck);
+
+        ExpandoAndGeneration *expandoAndGeneration = (ExpandoAndGeneration*)expandoVal.toPrivate();
+        masm.movePtr(ImmWord(expandoAndGeneration), tempVal.scratchReg());
+
+        masm.branch32(Assembler::NotEqual, Address(tempVal.scratchReg(), sizeof(Value)),
+                                                   Imm32(expandoAndGeneration->generation),
+                                                   &failListBaseCheck);
+
+        expandoVal = expandoAndGeneration->expando;
+        masm.loadValue(Address(tempVal.scratchReg(), 0), tempVal);
+    }
 
     // If the incoming object does not have an expando object then we're sure we're not
     // shadowing.
     masm.branchTestUndefined(Assembler::Equal, tempVal, &listBaseOk);
 
-    Value expandoVal = obj->getFixedSlot(GetListBaseExpandoSlot());
     if (expandoVal.isObject()) {
         JS_ASSERT(!expandoVal.toObject().nativeContains(cx, name));
 
         // Reference object has an expando object that doesn't define the name. Check that
         // the incoming object has an expando object with the same shape.
         masm.branchTestObject(Assembler::NotEqual, tempVal, &failListBaseCheck);
         masm.extractObject(tempVal, tempVal.scratchReg());
         masm.branchPtr(Assembler::Equal,
@@ -1239,26 +1253,30 @@ TryAttachNativeGetPropStub(JSContext *cx
                            const SafepointIndex *safepointIndex,
                            void *returnAddr, bool *isCacheable)
 {
     JS_ASSERT(!*isCacheable);
     JS_ASSERT(cache.canAttachStub());
 
     RootedObject checkObj(cx, obj);
     if (IsCacheableListBase(obj)) {
-        Value expandoVal = obj->getFixedSlot(GetListBaseExpandoSlot());
-
-        // Expando objects just hold any extra properties the object has been given by a script,
-        // and have no prototype or anything else that will complicate property lookups on them.
-        JS_ASSERT_IF(expandoVal.isObject(),
-                     expandoVal.toObject().isNative() && !expandoVal.toObject().getProto());
-
-        if (expandoVal.isObject() && expandoVal.toObject().nativeContains(cx, name))
+        RootedId id(cx, NameToId(name));
+        ListBaseShadowsResult shadows =
+            GetListBaseShadowsCheck()(cx, obj, id);
+        if (shadows == ShadowCheckFailed)
+            return false;
+        if (shadows == Shadows)
             return true;
-
+        if (shadows == DoesntShadowUnique)
+            // We reset the cache to clear out an existing IC for this object
+            // (if there is one). The generation is a constant in the generated
+            // code and we will not have the same generation again for this
+            // object, so the generation check in the existing IC would always
+            // fail anyway.
+            cache.reset();
         checkObj = obj->getTaggedProto().toObjectOrNull();
     }
 
     if (!checkObj || !checkObj->isNative())
         return true;
 
     if (IsIdempotentAndMaybeHasHooks(cache, checkObj))
         return true;
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -999,36 +999,45 @@ js::SetDOMCallbacks(JSRuntime *rt, const
 JS_FRIEND_API(const DOMCallbacks *)
 js::GetDOMCallbacks(JSRuntime *rt)
 {
     return rt->DOMcallbacks;
 }
 
 static void *gListBaseHandlerFamily = NULL;
 static uint32_t gListBaseExpandoSlot = 0;
+static ListBaseShadowsCheck gListBaseShadowsCheck;
 
 JS_FRIEND_API(void)
-js::SetListBaseInformation(void *listBaseHandlerFamily, uint32_t listBaseExpandoSlot)
+js::SetListBaseInformation(void *listBaseHandlerFamily, uint32_t listBaseExpandoSlot,
+                           ListBaseShadowsCheck listBaseShadowsCheck)
 {
     gListBaseHandlerFamily = listBaseHandlerFamily;
     gListBaseExpandoSlot = listBaseExpandoSlot;
+    gListBaseShadowsCheck = listBaseShadowsCheck;
 }
 
 void *
 js::GetListBaseHandlerFamily()
 {
     return gListBaseHandlerFamily;
 }
 
 uint32_t
 js::GetListBaseExpandoSlot()
 {
     return gListBaseExpandoSlot;
 }
 
+ListBaseShadowsCheck
+js::GetListBaseShadowsCheck()
+{
+    return gListBaseShadowsCheck;
+}
+
 JS_FRIEND_API(void)
 js::SetCTypesActivityCallback(JSRuntime *rt, CTypesActivityCallback cb)
 {
     rt->ctypesActivityCallback = cb;
 }
 
 js::AutoCTypesActivityCallback::AutoCTypesActivityCallback(JSContext *cx,
                                                            js::CTypesActivityType beginType,
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -850,21 +850,56 @@ struct CompartmentsWithPrincipals : publ
 
 extern JS_FRIEND_API(JSBool)
 NukeCrossCompartmentWrappers(JSContext* cx,
                              const CompartmentFilter& sourceFilter,
                              const CompartmentFilter& targetFilter,
                              NukeReferencesToWindow nukeReferencesToWindow);
 
 /* Specify information about ListBase proxies in the DOM, for use by ICs. */
+
+/*
+ * The ListBaseShadowsCheck function will be called to check if the property for
+ * id should be gotten from the prototype, or if there is an own property that
+ * shadows it.
+ * If DoesntShadow is returned then the slot at listBaseExpandoSlot should
+ * either be undefined or point to an expando object that would contain the own
+ * property.
+ * If DoesntShadowUnique is returned then the slot at listBaseExpandoSlot should
+ * contain a private pointer to a ExpandoAndGeneration, which contains a
+ * JS::Value that should either be undefined or point to an expando object, and
+ * a uint32 value. If that value changes then the IC for getting a property will
+ * be invalidated.
+ */
+
+struct ExpandoAndGeneration {
+  ExpandoAndGeneration()
+    : expando(UndefinedValue()),
+      generation(0)
+  {}
+
+  Value expando;
+  uint32_t generation;
+};
+
+typedef enum ListBaseShadowsResult {
+  ShadowCheckFailed,
+  Shadows,
+  DoesntShadow,
+  DoesntShadowUnique
+} ListBaseShadowsResult;
+typedef ListBaseShadowsResult
+(* ListBaseShadowsCheck)(JSContext* cx, JSHandleObject object, JSHandleId id);
 JS_FRIEND_API(void)
-SetListBaseInformation(void *listBaseHandlerFamily, uint32_t listBaseExpandoSlot);
+SetListBaseInformation(void *listBaseHandlerFamily, uint32_t listBaseExpandoSlot,
+                       ListBaseShadowsCheck listBaseShadowsCheck);
 
 void *GetListBaseHandlerFamily();
 uint32_t GetListBaseExpandoSlot();
+ListBaseShadowsCheck GetListBaseShadowsCheck();
 
 } /* namespace js */
 
 /* Implemented in jsdate.cpp. */
 
 /*
  * Detect whether the internal date value is NaN.  (Because failure is
  * out-of-band for js_DateGet*)
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -672,27 +672,25 @@ struct GetPropHelper {
             return ic.disable(cx, "non-native holder");
         shape = prop;
         return Lookup_Cacheable;
     }
 
     LookupStatus lookup() {
         RootedObject aobj(cx, obj);
         if (IsCacheableListBase(obj)) {
-            Value expandoValue = obj->getFixedSlot(GetListBaseExpandoSlot());
-
-            // Expando objects just hold any extra properties the object has been given by a
-            // script, and have no prototype or anything else that will complicate property
-            // lookups on them.
-            JS_ASSERT_IF(expandoValue.isObject(),
-                         expandoValue.toObject().isNative() && !expandoValue.toObject().getProto());
-
-            if (expandoValue.isObject() && expandoValue.toObject().nativeContains(cx, name))
+            RootedId aid(cx, NameToId(name));
+            ListBaseShadowsResult shadows =
+                GetListBaseShadowsCheck()(cx, obj, aid);
+            if (shadows == ShadowCheckFailed)
+                return ic.error(cx);
+            // Either the property is shadowed or we need an additional check in
+            // the stub, which we haven't implemented.
+            if (shadows == Shadows || shadows == DoesntShadowUnique)
                 return Lookup_Uncacheable;
-
             aobj = obj->getTaggedProto().toObjectOrNull();
         }
 
         if (!aobj->isNative())
             return ic.disable(f, "non-native");
 
         RecompilationMonitor monitor(cx);
         if (!JSObject::lookupProperty(cx, aobj, name, &holder, &prop))