Bug 1135816 - Handle unboxed object receivers when compiling getter/setter calls in baseline/Ion, r=jandem.
authorBrian Hackett <bhackett1024@gmail.com>
Tue, 03 Mar 2015 06:32:27 -0600
changeset 247026 9e86bfdf6bd8fb92e92ed4a5f5efea77d79e5d40
parent 247025 c75a393d7bfb81aede37b182e30c7758765268ba
child 247050 e142f57da9acbdf55cfccdbb57dbdf9871e270f8
push id884
push userdburns@mozilla.com
push dateTue, 03 Mar 2015 15:29:12 +0000
reviewersjandem
bugs1135816
milestone39.0a1
Bug 1135816 - Handle unboxed object receivers when compiling getter/setter calls in baseline/Ion, r=jandem.
js/src/jit/BaselineIC.cpp
js/src/jit/BaselineIC.h
js/src/jit/BaselineInspector.cpp
js/src/jit/BaselineInspector.h
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonBuilder.cpp
js/src/jit/IonBuilder.h
js/src/jit/IonCaches.cpp
js/src/jit/LIR-Common.h
js/src/jit/LOpcodes.h
js/src/jit/Lowering.cpp
js/src/jit/Lowering.h
js/src/jit/MIR.cpp
js/src/jit/MIR.h
js/src/jit/MOpcodes.h
js/src/jit/MacroAssembler.h
js/src/jsobj.cpp
js/src/jsobj.h
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -160,16 +160,26 @@ ICStub::markCode(JSTracer *trc, const ch
 void
 ICStub::updateCode(JitCode *code)
 {
     // Write barrier on the old code.
     JitCode::writeBarrierPre(jitCode());
     stubCode_ = code->raw();
 }
 
+void
+ReceiverGuard::trace(JSTracer *trc)
+{
+    MOZ_ASSERT(!!shape_ != !!group_);
+    if (shape_)
+        MarkShape(trc, &shape_, "receiver_guard_shape");
+    else
+        MarkObjectGroup(trc, &group_, "receiver_guard_group");
+}
+
 /* static */ void
 ICStub::trace(JSTracer *trc)
 {
     markCode(trc, "baseline-stub-jitcode");
 
     // If the stub is a monitored fallback stub, then mark the monitor ICs hanging
     // off of that stub.  We don't need to worry about the regular monitored stubs,
     // because the regular monitored stubs will always have a monitored fallback stub
@@ -337,30 +347,24 @@ ICStub::trace(JSTracer *trc)
       }
       case ICStub::GetProp_Native: {
         ICGetProp_Native *propStub = toGetProp_Native();
         MarkShape(trc, &propStub->shape(), "baseline-getpropnative-stub-shape");
         break;
       }
       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_UnboxedPrototype: {
-        ICGetProp_UnboxedPrototype *propStub = toGetProp_UnboxedPrototype();
-        MarkObjectGroup(trc, &propStub->group(), "baseline-getpropnativeproto-stub-group");
+        propStub->guard().trace(trc);
         MarkObject(trc, &propStub->holder(), "baseline-getpropnativeproto-stub-holder");
         MarkShape(trc, &propStub->holderShape(), "baseline-getpropnativeproto-stub-holdershape");
         break;
       }
       case ICStub::GetProp_NativeDoesNotExist: {
         ICGetProp_NativeDoesNotExist *propStub = toGetProp_NativeDoesNotExist();
+        propStub->guard().trace(trc);
         JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
         switch (propStub->protoChainDepth()) {
           case 0: propStub->toImpl<0>()->traceShapes(trc); break;
           case 1: propStub->toImpl<1>()->traceShapes(trc); break;
           case 2: propStub->toImpl<2>()->traceShapes(trc); break;
           case 3: propStub->toImpl<3>()->traceShapes(trc); break;
           case 4: propStub->toImpl<4>()->traceShapes(trc); break;
           case 5: propStub->toImpl<5>()->traceShapes(trc); break;
@@ -401,32 +405,32 @@ ICStub::trace(JSTracer *trc)
       case ICStub::GetProp_DOMProxyShadowed: {
         ICGetProp_DOMProxyShadowed *propStub = toGetProp_DOMProxyShadowed();
         MarkShape(trc, &propStub->shape(), "baseline-getproplistbaseshadowed-stub-shape");
         MarkString(trc, &propStub->name(), "baseline-getproplistbaseshadowed-stub-name");
         break;
       }
       case ICStub::GetProp_CallScripted: {
         ICGetProp_CallScripted *callStub = toGetProp_CallScripted();
-        MarkShape(trc, &callStub->receiverShape(), "baseline-getpropcallscripted-stub-receivershape");
+        callStub->receiverGuard().trace(trc);
         MarkObject(trc, &callStub->holder(), "baseline-getpropcallscripted-stub-holder");
         MarkShape(trc, &callStub->holderShape(), "baseline-getpropcallscripted-stub-holdershape");
         MarkObject(trc, &callStub->getter(), "baseline-getpropcallscripted-stub-getter");
         break;
       }
       case ICStub::GetProp_CallNative: {
         ICGetProp_CallNative *callStub = toGetProp_CallNative();
         MarkObject(trc, &callStub->holder(), "baseline-getpropcallnative-stub-holder");
         MarkShape(trc, &callStub->holderShape(), "baseline-getpropcallnative-stub-holdershape");
         MarkObject(trc, &callStub->getter(), "baseline-getpropcallnative-stub-getter");
         break;
       }
       case ICStub::GetProp_CallNativePrototype: {
         ICGetProp_CallNativePrototype *callStub = toGetProp_CallNativePrototype();
-        MarkShape(trc, &callStub->receiverShape(), "baseline-getpropcallnativeproto-stub-receivershape");
+        callStub->receiverGuard().trace(trc);
         MarkObject(trc, &callStub->holder(), "baseline-getpropcallnativeproto-stub-holder");
         MarkShape(trc, &callStub->holderShape(), "baseline-getpropcallnativeproto-stub-holdershape");
         MarkObject(trc, &callStub->getter(), "baseline-getpropcallnativeproto-stub-getter");
         break;
       }
       case ICStub::SetProp_Native: {
         ICSetProp_Native *propStub = toSetProp_Native();
         MarkShape(trc, &propStub->shape(), "baseline-setpropnative-stub-shape");
@@ -458,25 +462,25 @@ ICStub::trace(JSTracer *trc)
       case ICStub::SetProp_TypedObject: {
         ICSetProp_TypedObject *propStub = toSetProp_TypedObject();
         MarkShape(trc, &propStub->shape(), "baseline-setprop-typedobject-stub-shape");
         MarkObjectGroup(trc, &propStub->group(), "baseline-setprop-typedobject-stub-group");
         break;
       }
       case ICStub::SetProp_CallScripted: {
         ICSetProp_CallScripted *callStub = toSetProp_CallScripted();
-        MarkShape(trc, &callStub->shape(), "baseline-setpropcallscripted-stub-shape");
+        callStub->guard().trace(trc);
         MarkObject(trc, &callStub->holder(), "baseline-setpropcallscripted-stub-holder");
         MarkShape(trc, &callStub->holderShape(), "baseline-setpropcallscripted-stub-holdershape");
         MarkObject(trc, &callStub->setter(), "baseline-setpropcallscripted-stub-setter");
         break;
       }
       case ICStub::SetProp_CallNative: {
         ICSetProp_CallNative *callStub = toSetProp_CallNative();
-        MarkShape(trc, &callStub->shape(), "baseline-setpropcallnative-stub-shape");
+        callStub->guard().trace(trc);
         MarkObject(trc, &callStub->holder(), "baseline-setpropcallnative-stub-holder");
         MarkShape(trc, &callStub->holderShape(), "baseline-setpropcallnative-stub-holdershape");
         MarkObject(trc, &callStub->setter(), "baseline-setpropcallnative-stub-setter");
         break;
       }
       case ICStub::InstanceOf_Function: {
         ICInstanceOf_Function *instanceofStub = toInstanceOf_Function();
         MarkShape(trc, &instanceofStub->shape(), "baseline-instanceof-fun-shape");
@@ -3293,50 +3297,52 @@ EffectlesslyLookupProperty(JSContext *cx
             return true;
         }
 
         *domProxyHasGeneration = (*shadowsResult == DoesntShadowUnique);
 
         checkObj = GetDOMProxyProto(obj);
         if (!checkObj)
             return true;
-    } else if (!obj->isNative()) {
-        return true;
-    }
-
-    if (checkObj->hasIdempotentProtoChain()) {
-        if (!LookupProperty(cx, checkObj, name, holder, shape))
-            return false;
-    } else if (checkObj->isNative()) {
-        shape.set(checkObj->as<NativeObject>().lookup(cx, NameToId(name)));
-        if (shape)
-            holder.set(checkObj);
-    }
+    } else if (!obj->isNative() && !obj->is<UnboxedPlainObject>()) {
+        return true;
+    }
+
+    if (LookupPropertyPure(cx, checkObj, NameToId(name), holder.address(), shape.address()))
+        return true;
+
+    holder.set(nullptr);
+    shape.set(nullptr);
     return true;
 }
 
 static bool
 CheckHasNoSuchProperty(JSContext *cx, HandleObject obj, HandlePropertyName name,
                        MutableHandleObject lastProto, size_t *protoChainDepthOut)
 {
     MOZ_ASSERT(protoChainDepthOut != nullptr);
 
     size_t depth = 0;
     RootedObject curObj(cx, obj);
     while (curObj) {
-        if (!curObj->isNative())
-            return false;
-
-        // Don't handle proto chains with resolve hooks.
-        if (curObj->getClass()->resolve)
-            return false;
-
-        Shape *shape = curObj->as<NativeObject>().lookup(cx, NameToId(name));
-        if (shape)
-            return false;
+        if (curObj->isNative()) {
+            // Don't handle proto chains with resolve hooks.
+            if (curObj->getClass()->resolve)
+                return false;
+
+            if (curObj->as<NativeObject>().contains(cx, NameToId(name)))
+                return false;
+        } else {
+            // Handle unboxed plain objects as the original receiver.
+            if (!curObj->is<UnboxedPlainObject>() || curObj != obj)
+                return false;
+
+            if (curObj->as<UnboxedPlainObject>().layout().lookup(name))
+                return false;
+        }
 
         JSObject *proto = curObj->getTaggedProto().toObjectOrNull();
         if (!proto)
             break;
 
         curObj = proto;
         depth++;
     }
@@ -5360,17 +5366,16 @@ ICSetElem_Dense::Compiler::generateStubC
     masm.bind(&failure);
     EmitStubGuardFailure(masm);
     return true;
 }
 
 static bool
 GetProtoShapes(JSObject *obj, size_t protoChainDepth, AutoShapeVector *shapes)
 {
-    MOZ_ASSERT(shapes->length() == 1);
     JSObject *curProto = obj->getProto();
     for (size_t i = 0; i < protoChainDepth; i++) {
         if (!shapes->append(curProto->lastProperty()))
             return false;
         curProto = curProto->getProto();
     }
     MOZ_ASSERT(!curProto);
     return true;
@@ -5762,80 +5767,80 @@ UpdateExistingGetPropCallStubs(ICFallbac
                                HandleFunction getter)
 {
     MOZ_ASSERT(kind == ICStub::GetProp_CallScripted ||
                kind == ICStub::GetProp_CallNative ||
                kind == ICStub::GetProp_CallNativePrototype);
     MOZ_ASSERT(fallbackStub->isGetName_Fallback() ||
                fallbackStub->isGetProp_Fallback());
     bool foundMatchingStub = false;
-    Shape *receiverShape = receiver ? receiver->lastProperty() : nullptr;
+    ReceiverGuard::Token receiverGuard = receiver ? ReceiverGuard::objectToken(receiver) : 0;
     for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->kind() == kind) {
             ICGetPropCallGetter *getPropStub = static_cast<ICGetPropCallGetter *>(*iter);
             if (getPropStub->holder() == holder) {
                 // We want to update the holder shape to match the new one no
                 // matter what, even if the receiver shape is different.
-                Shape *cachedReceiverShape;
+                ReceiverGuard::Token cachedReceiverGuard;
                 if (kind == ICStub::GetProp_CallNative) {
-                    cachedReceiverShape = nullptr;
+                    cachedReceiverGuard = 0;
                 } else {
                     ICGetPropCallPrototypeGetter *stubWithReceiver =
                         static_cast<ICGetPropCallPrototypeGetter*>(getPropStub);
-                    cachedReceiverShape = stubWithReceiver->receiverShape();
+                    cachedReceiverGuard = stubWithReceiver->receiverGuard().token();
                 }
                 // We would like to assert that either
                 // getPropStub->holderShape() != holder->lastProperty() or
                 // receiverShape != cachedReceiverShape, but that assertion can
                 // fail if there is something in a getter that changes something
                 // that we guard on in our stub but don't check for before/after
                 // differences across the set during stub generation.  For
                 // example, a getter mutating the shape of the proto the getter
                 // lives on would cause us to create an IC stub that never
                 // matches as protos with the old shape flow into it, but always
                 // matches post-get, which is where we are now.
                 getPropStub->holderShape() = holder->lastProperty();
                 // Make sure to update the getter, since a shape change might
                 // have changed which getter we want to use.
                 getPropStub->getter() = getter;
-                if (receiverShape == cachedReceiverShape)
+                if (receiverGuard == cachedReceiverGuard)
                     foundMatchingStub = true;
             }
         }
     }
 
     return foundMatchingStub;
 }
 
 // Try to update existing SetProp setter call stubs for the given holder in
 // place with a new shape and setter.
 static bool
 UpdateExistingSetPropCallStubs(ICSetProp_Fallback* fallbackStub,
-                              ICStub::Kind kind,
-                              HandleObject holder,
-                              HandleShape receiverShape,
-                              HandleFunction setter)
+                               ICStub::Kind kind,
+                               JSObject *holder,
+                               ReceiverGuard::Token receiverGuard,
+                               JSFunction *setter)
 {
     MOZ_ASSERT(kind == ICStub::SetProp_CallScripted ||
                kind == ICStub::SetProp_CallNative);
     bool foundMatchingStub = false;
     for (ICStubConstIterator iter = fallbackStub->beginChainConst(); !iter.atEnd(); iter++) {
         if (iter->kind() == kind) {
             ICSetPropCallSetter *setPropStub = static_cast<ICSetPropCallSetter *>(*iter);
             if (setPropStub->holder() == holder) {
                 // We want to update the holder shape to match the new one no
                 // matter what, even if the receiver shape is different.
                 MOZ_ASSERT(setPropStub->holderShape() != holder->lastProperty() ||
-                           setPropStub->shape() != receiverShape,
+                           setPropStub->guard().token() != receiverGuard,
                            "Why didn't we end up using this stub?");
                 setPropStub->holderShape() = holder->lastProperty();
                 // Make sure to update the setter, since a shape change might
                 // have changed which setter we want to use.
                 setPropStub->setter() = setter;
-                if (receiverShape == setPropStub->shape())
+                if (receiverGuard == setPropStub->guard().token())
                     foundMatchingStub = true;
             }
         }
     }
 
     return foundMatchingStub;
 }
 
@@ -6510,25 +6515,18 @@ TryAttachNativeGetPropStub(JSContext *cx
         bool isFixedSlot;
         uint32_t offset;
         GetFixedOrDynamicSlotOffset(&holder->as<NativeObject>(), shape->slot(), &isFixedSlot, &offset);
 
         // Instantiate this property for singleton holders, for use during Ion compilation.
         if (IsIonEnabled(cx))
             EnsureTrackPropertyTypes(cx, holder, NameToId(name));
 
-        ICStub::Kind kind;
-        if (obj == holder)
-            kind = ICStub::GetProp_Native;
-        else if (obj->isNative())
-            kind = ICStub::GetProp_NativePrototype;
-        else if (obj->is<UnboxedPlainObject>())
-            kind = ICStub::GetProp_UnboxedPrototype;
-        else
-            MOZ_CRASH("Bad kind");
+        ICStub::Kind kind =
+            (obj == holder) ? ICStub::GetProp_Native : ICStub::GetProp_NativePrototype;
 
         JitSpew(JitSpew_BaselineIC, "  Generating GetProp(%s %s) stub",
                     isDOMProxy ? "DOMProxy" : "Native",
                     (obj == holder) ? "direct" : "prototype");
         ICGetPropNativeCompiler compiler(cx, kind, isCallProp, monitorStub, obj, holder,
                                          name, isFixedSlot, offset);
         ICGetPropNativeStub *newStub = compiler.getStub(compiler.getStubSpace(script));
         if (!newStub)
@@ -6540,18 +6538,18 @@ TryAttachNativeGetPropStub(JSContext *cx
             StripPreliminaryObjectStubs(cx, stub);
 
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     bool isScripted = false;
-    bool cacheableCall = obj->isNative() && IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted,
-                                                                   isTemporarilyUnoptimizable);
+    bool cacheableCall = IsCacheableGetPropCall(cx, obj, holder, shape, &isScripted,
+                                                isTemporarilyUnoptimizable);
 
     // Try handling scripted getters.
     if (cacheableCall && isScripted && !isDOMProxy) {
 #if JS_HAS_NO_SUCH_METHOD
         // It's hard to keep the original object alive through a call, and it's unlikely
         // that a getter will be used to generate functions for calling in CALLPROP locations.
         // Just don't attach stubs in that case.
         if (isCallProp)
@@ -6602,17 +6600,17 @@ TryAttachNativeGetPropStub(JSContext *cx
         if (!newStub)
             return false;
         stub->addNewStub(newStub);
         *attached = true;
         return true;
     }
 
     const Class *outerClass = nullptr;
-    if (!isDOMProxy && !obj->isNative()) {
+    if (!isDOMProxy && !obj->isNative() && !obj->is<UnboxedPlainObject>()) {
         outerClass = obj->getClass();
         DebugOnly<JSObject *> outer = obj.get();
         obj = GetInnerObject(obj);
         MOZ_ASSERT(script->global().isNative());
         if (obj != &script->global())
             return true;
         // ICGetProp_CallNative*::Compiler::generateStubCode depends on this.
         MOZ_ASSERT(&((GetProxyDataLayout(outer)->values->privateSlot).toObject()) == obj);
@@ -6850,18 +6848,16 @@ TryAttachNativeDoesNotExistStub(JSContex
     if (!CheckHasNoSuchProperty(cx, obj, name, &lastProto, &protoChainDepth))
         return true;
     MOZ_ASSERT(protoChainDepth < SIZE_MAX);
 
     if (protoChainDepth > ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH)
         return true;
 
     ICStub *monitorStub = stub->fallbackMonitorStub()->firstMonitorStub();
-    RootedShape objShape(cx, obj->lastProperty());
-    RootedShape lastShape(cx, lastProto->lastProperty());
 
     // Confirmed no-such-property.  Add stub.
     JitSpew(JitSpew_BaselineIC, "  Generating GetProp_NativeDoesNotExist stub");
     ICGetPropNativeDoesNotExistCompiler compiler(cx, monitorStub, obj, protoChainDepth);
     ICStub *newStub = compiler.getStub(compiler.getStubSpace(script));
     if (!newStub)
         return false;
 
@@ -7172,35 +7168,46 @@ ICGetPropNativeCompiler::getStub(ICStubS
       case ICStub::GetProp_Native: {
         MOZ_ASSERT(obj_ == holder_);
         RootedShape shape(cx, obj_->lastProperty());
         return ICStub::New<ICGetProp_Native>(space, getStubCode(), firstMonitorStub_, shape, offset_);
       }
 
       case ICStub::GetProp_NativePrototype: {
         MOZ_ASSERT(obj_ != holder_);
-        RootedShape shape(cx, obj_->lastProperty());
-        RootedShape holderShape(cx, holder_->lastProperty());
-        return ICStub::New<ICGetProp_NativePrototype>(space, getStubCode(), firstMonitorStub_, shape,
+        ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
+        Shape *holderShape = holder_->lastProperty();
+        return ICStub::New<ICGetProp_NativePrototype>(space, getStubCode(), firstMonitorStub_, guard,
                                                       offset_, holder_, holderShape);
       }
 
-      case ICStub::GetProp_UnboxedPrototype: {
-        MOZ_ASSERT(obj_ != holder_);
-        RootedObjectGroup group(cx, obj_->group());
-        RootedShape holderShape(cx, holder_->lastProperty());
-        return ICStub::New<ICGetProp_UnboxedPrototype>(space, getStubCode(), firstMonitorStub_, group,
-                                                       offset_, holder_, holderShape);
-      }
-
       default:
         MOZ_CRASH("Bad stub kind");
     }
 }
 
+static void
+GuardNativeOrUnboxedReceiver(MacroAssembler &masm, JSObject *obj,
+                             Register object, Register scratch,
+                             size_t receiverGuardOffset, Label *failure)
+{
+    if (obj->isNative()) {
+        masm.loadPtr(Address(BaselineStubReg,
+                             receiverGuardOffset + ReceiverGuard::offsetOfShape()),
+                     scratch);
+        masm.branchTestObjShape(Assembler::NotEqual, object, scratch, failure);
+    } else {
+        MOZ_ASSERT(obj->is<UnboxedPlainObject>());
+        masm.loadPtr(Address(BaselineStubReg,
+                             receiverGuardOffset + ReceiverGuard::offsetOfGroup()),
+                     scratch);
+        masm.branchTestObjGroup(Assembler::NotEqual, object, scratch, failure);
+    }
+}
+
 bool
 ICGetPropNativeCompiler::generateStubCode(MacroAssembler &masm)
 {
     Label failure;
     GeneralRegisterSet regs(availableGeneralRegs(0));
     Register objReg = InvalidReg;
 
     if (inputDefinitelyObject_) {
@@ -7211,36 +7218,26 @@ ICGetPropNativeCompiler::generateStubCod
         masm.branchTestObject(Assembler::NotEqual, R0, &failure);
         objReg = masm.extractObject(R0, ExtractTemp0);
     }
     regs.takeUnchecked(objReg);
 
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Shape/group guard.
-    if (kind == ICStub::GetProp_UnboxedPrototype) {
-        masm.loadPtr(Address(BaselineStubReg, ICGetProp_UnboxedPrototype::offsetOfGroup()), scratch);
-        masm.branchPtr(Assembler::NotEqual, Address(objReg, JSObject::offsetOfGroup()), scratch,
-                       &failure);
-    } else {
-        MOZ_ASSERT(ICGetProp_Native::offsetOfShape() ==
-                   ICGetProp_NativePrototype::offsetOfShape());
-        masm.loadPtr(Address(BaselineStubReg, ICGetProp_Native::offsetOfShape()), scratch);
-        masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
-    }
+    MOZ_ASSERT(ICGetProp_Native::offsetOfShape() ==
+               ICGetProp_NativePrototype::offsetOfGuard() + ReceiverGuard::offsetOfShape());
+    GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
+                                 ICGetProp_NativePrototype::offsetOfGuard(), &failure);
 
     Register holderReg;
     if (obj_ == holder_) {
         holderReg = objReg;
     } else {
         // Shape guard holder.
-        MOZ_ASSERT(ICGetProp_NativePrototype::offsetOfHolder() ==
-                   ICGetProp_UnboxedPrototype::offsetOfHolder());
-        MOZ_ASSERT(ICGetProp_NativePrototype::offsetOfHolderShape() ==
-                   ICGetProp_UnboxedPrototype::offsetOfHolderShape());
         holderReg = regs.takeAny();
         masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativePrototype::offsetOfHolder()),
                      holderReg);
         masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativePrototype::offsetOfHolderShape()),
                      scratch);
         masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
     }
 
@@ -7317,18 +7314,16 @@ ICGetPropNativeCompiler::generateStubCod
     EmitStubGuardFailure(masm);
     return true;
 }
 
 ICStub *
 ICGetPropNativeDoesNotExistCompiler::getStub(ICStubSpace *space)
 {
     AutoShapeVector shapes(cx);
-    if (!shapes.append(obj_->lastProperty()))
-        return nullptr;
 
     if (!GetProtoShapes(obj_, protoChainDepth_, &shapes))
         return nullptr;
 
     JS_STATIC_ASSERT(ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH == 8);
 
     ICStub *stub = nullptr;
     switch(protoChainDepth_) {
@@ -7365,28 +7360,27 @@ ICGetPropNativeDoesNotExistCompiler::gen
         masm.assumeUnreachable("Non-matching proto chain depth on stub.");
         masm.bind(&ok);
     }
 #endif // DEBUG
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
 
-    // Unbox and guard against old shape.
+    // Unbox and guard against old shape/group.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICGetProp_NativeDoesNotExist::offsetOfShape(0)),
-                 scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
+    GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
+                                 ICGetProp_NativeDoesNotExist::offsetOfGuard(), &failure);
 
     Register protoReg = regs.takeAny();
     // Check the proto chain.
     for (size_t i = 0; i < protoChainDepth_; i++) {
         masm.loadObjProto(i == 0 ? objReg : protoReg, protoReg);
         masm.branchTestPtr(Assembler::Zero, protoReg, protoReg, &failure);
-        size_t shapeOffset = ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(i + 1);
+        size_t shapeOffset = ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(i);
         masm.loadPtr(Address(BaselineStubReg, shapeOffset), scratch);
         masm.branchTestObjShape(Assembler::NotEqual, protoReg, scratch, &failure);
     }
 
     // Shape and type checks succeeded, ok to proceed.
     masm.moveValue(UndefinedValue(), R0);
 
     // Normally for this op, the result would have to be monitored by TI.
@@ -7407,18 +7401,18 @@ ICGetProp_CallScripted::Compiler::genera
     GeneralRegisterSet regs(availableGeneralRegs(1));
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Guard input is an object.
     masm.branchTestObject(Assembler::NotEqual, R0, &failure);
 
     // Unbox and shape guard.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallScripted::offsetOfReceiverShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
+    GuardNativeOrUnboxedReceiver(masm, receiver_, objReg, scratch,
+                                 ICGetProp_CallScripted::offsetOfReceiverGuard(), &failure);
 
     Register holderReg = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallScripted::offsetOfHolder()), holderReg);
     masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallScripted::offsetOfHolderShape()), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
     regs.add(holderReg);
 
     // Push a stub frame so that we can perform a non-tail call.
@@ -7569,18 +7563,18 @@ ICGetProp_CallNativePrototype::Compiler:
             regs.add(tmp);
         }
     }
     regs.takeUnchecked(objReg);
 
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Shape guard.
-    masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallNativePrototype::offsetOfReceiverShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failure);
+    GuardNativeOrUnboxedReceiver(masm, receiver_, objReg, scratch,
+                                 ICGetProp_CallNativePrototype::offsetOfReceiverGuard(), &failure);
 
     Register holderReg = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallNativePrototype::offsetOfHolder()), holderReg);
     masm.loadPtr(Address(BaselineStubReg, ICGetProp_CallNativePrototype::offsetOfHolderShape()), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failure);
     regs.add(holderReg);
 
     // Push a stub frame so that we can perform a non-tail call.
@@ -8142,24 +8136,24 @@ TryAttachSetValuePropStub(JSContext *cx,
 
     return true;
 }
 
 // Attach an optimized property set stub for a SETPROP/SETGNAME/SETNAME op on
 // an accessor property.
 static bool
 TryAttachSetAccessorPropStub(JSContext *cx, HandleScript script, jsbytecode *pc, ICSetProp_Fallback *stub,
-                             HandleObject obj, HandleShape receiverShape, HandlePropertyName name,
+                             HandleObject obj, ReceiverGuard::Token receiverGuard, HandlePropertyName name,
                              HandleId id, HandleValue rhs, bool *attached,
                              bool *isTemporarilyUnoptimizable)
 {
     MOZ_ASSERT(!*attached);
     MOZ_ASSERT(!*isTemporarilyUnoptimizable);
 
-    if (!obj->isNative() || obj->watched())
+    if (obj->watched())
         return true;
 
     RootedShape shape(cx);
     RootedObject holder(cx);
     if (!EffectlesslyLookupProperty(cx, obj, name, &holder, &shape))
         return false;
 
     bool isScripted = false;
@@ -8168,17 +8162,17 @@ TryAttachSetAccessorPropStub(JSContext *
 
     // Try handling scripted setters.
     if (cacheableCall && isScripted) {
         RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
         MOZ_ASSERT(obj != holder);
         MOZ_ASSERT(callee->hasScript());
 
         if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallScripted,
-                                           holder, receiverShape, callee)) {
+                                           holder, receiverGuard, callee)) {
             *attached = true;
             return true;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/ScriptedSetter %s:%" PRIuSIZE ") stub",
                     callee->nonLazyScript()->filename(), callee->nonLazyScript()->lineno());
 
         ICSetProp_CallScripted::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8193,17 +8187,17 @@ TryAttachSetAccessorPropStub(JSContext *
 
     // Try handling JSNative setters.
     if (cacheableCall && !isScripted) {
         RootedFunction callee(cx, &shape->setterObject()->as<JSFunction>());
         MOZ_ASSERT(obj != holder);
         MOZ_ASSERT(callee->isNative());
 
         if (UpdateExistingSetPropCallStubs(stub, ICStub::SetProp_CallNative,
-                                           holder, receiverShape, callee)) {
+                                           holder, receiverGuard, callee)) {
             *attached = true;
             return true;
         }
 
         JitSpew(JitSpew_BaselineIC, "  Generating SetProp(NativeObj/NativeSetter %p) stub",
                     callee->native());
 
         ICSetProp_CallNative::Compiler compiler(cx, obj, holder, callee, script->pcToOffset(pc));
@@ -8324,31 +8318,32 @@ DoSetPropFallback(JSContext *cx, Baselin
         name = ScopeCoordinateName(cx->runtime()->scopeCoordinateNameCache, script, pc);
     else
         name = script->getName(pc);
     RootedId id(cx, NameToId(name));
 
     RootedObject obj(cx, ToObjectFromStack(cx, lhs));
     if (!obj)
         return false;
+    ReceiverGuard::Token oldGuard = ReceiverGuard::objectToken(obj);
     RootedShape oldShape(cx, obj->lastProperty());
     RootedObjectGroup oldGroup(cx, obj->getGroup(cx));
     if (!oldGroup)
         return false;
     uint32_t oldSlots = obj->isNative() ? obj->as<NativeObject>().numDynamicSlots() : 0;
 
     bool attached = false;
     // There are some reasons we can fail to attach a stub that are temporary.
     // We want to avoid calling noteUnoptimizableAccess() if the reason we
     // failed to attach a stub is one of those temporary reasons, since we might
     // end up attaching a stub for the exact same access later.
     bool isTemporarilyUnoptimizable = false;
     if (stub->numOptimizedStubs() < ICSetProp_Fallback::MAX_OPTIMIZED_STUBS &&
         lhs.isObject() &&
-        !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldShape, name, id,
+        !TryAttachSetAccessorPropStub(cx, script, pc, stub, obj, oldGuard, name, id,
                                       rhs, &attached, &isTemporarilyUnoptimizable))
     {
         return false;
     }
 
     if (op == JSOP_INITPROP ||
         op == JSOP_INITLOCKEDPROP ||
         op == JSOP_INITHIDDENPROP)
@@ -8891,18 +8886,18 @@ ICSetProp_CallScripted::Compiler::genera
     // Stow R0 and R1 to free up registers.
     EmitStowICValues(masm, 2);
 
     GeneralRegisterSet regs(availableGeneralRegs(1));
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Unbox and shape guard.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failureUnstow);
+    GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
+                                 ICSetProp_CallScripted::offsetOfGuard(), &failureUnstow);
 
     Register holderReg = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfHolder()), holderReg);
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallScripted::offsetOfHolderShape()), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
     regs.add(holderReg);
 
     // Push a stub frame so that we can perform a non-tail call.
@@ -9010,18 +9005,18 @@ ICSetProp_CallNative::Compiler::generate
     // Stow R0 and R1 to free up registers.
     EmitStowICValues(masm, 2);
 
     GeneralRegisterSet regs(availableGeneralRegs(1));
     Register scratch = regs.takeAnyExcluding(BaselineTailCallReg);
 
     // Unbox and shape guard.
     Register objReg = masm.extractObject(R0, ExtractTemp0);
-    masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfShape()), scratch);
-    masm.branchTestObjShape(Assembler::NotEqual, objReg, scratch, &failureUnstow);
+    GuardNativeOrUnboxedReceiver(masm, obj_, objReg, scratch,
+                                 ICSetProp_CallNative::offsetOfGuard(), &failureUnstow);
 
     Register holderReg = regs.takeAny();
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfHolder()), holderReg);
     masm.loadPtr(Address(BaselineStubReg, ICSetProp_CallNative::offsetOfHolderShape()), scratch);
     masm.branchTestObjShape(Assembler::NotEqual, holderReg, scratch, &failureUnstow);
     regs.add(holderReg);
 
     // Push a stub frame so that we can perform a non-tail call.
@@ -11847,73 +11842,61 @@ ICGetPropNativeStub::ICGetPropNativeStub
 ICGetProp_Native::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
                         ICGetProp_Native &other)
 {
     return New<ICGetProp_Native>(space, other.jitCode(), firstMonitorStub, other.shape(),
                                  other.offset());
 }
 
 ICGetProp_NativePrototype::ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
-                                                     Shape *shape, uint32_t offset,
+                                                     ReceiverGuard::Token guard, uint32_t offset,
                                                      JSObject *holder, Shape *holderShape)
   : ICGetPropNativeStub(GetProp_NativePrototype, stubCode, firstMonitorStub, offset),
-    shape_(shape),
+    guard_(guard),
     holder_(holder),
     holderShape_(holderShape)
 { }
 
 /* static */ ICGetProp_NativePrototype *
 ICGetProp_NativePrototype::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
                                  ICGetProp_NativePrototype &other)
 {
-    return New<ICGetProp_NativePrototype>(space, other.jitCode(), firstMonitorStub, other.shape(),
+    return New<ICGetProp_NativePrototype>(space, other.jitCode(), firstMonitorStub,
+                                          other.guard().token(),
                                           other.offset(), other.holder_, other.holderShape_);
 }
 
-ICGetProp_UnboxedPrototype::ICGetProp_UnboxedPrototype(JitCode *stubCode, ICStub *firstMonitorStub,
-                                                       ObjectGroup *group, uint32_t offset,
-                                                       JSObject *holder, Shape *holderShape)
-  : ICGetPropNativeStub(GetProp_UnboxedPrototype, stubCode, firstMonitorStub, offset),
-    group_(group),
-    holder_(holder),
-    holderShape_(holderShape)
-{ }
-
-/* static */ ICGetProp_UnboxedPrototype *
-ICGetProp_UnboxedPrototype::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
-                                  ICGetProp_UnboxedPrototype &other)
-{
-    return New<ICGetProp_UnboxedPrototype>(space, other.jitCode(), firstMonitorStub, other.group(),
-                                           other.offset(), other.holder_, other.holderShape_);
-}
-
 ICGetProp_NativeDoesNotExist::ICGetProp_NativeDoesNotExist(
-        JitCode *stubCode, ICStub *firstMonitorStub, size_t protoChainDepth)
-  : ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub)
+    JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::Token guard, size_t protoChainDepth)
+  : ICMonitoredStub(GetProp_NativeDoesNotExist, stubCode, firstMonitorStub),
+    guard_(guard)
 {
     MOZ_ASSERT(protoChainDepth <= MAX_PROTO_CHAIN_DEPTH);
     extra_ = protoChainDepth;
 }
 
 /* static */ size_t
 ICGetProp_NativeDoesNotExist::offsetOfShape(size_t idx)
 {
     MOZ_ASSERT(ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx) ==
                ICGetProp_NativeDoesNotExistImpl<
                     ICGetProp_NativeDoesNotExist::MAX_PROTO_CHAIN_DEPTH>::offsetOfShape(idx));
     return ICGetProp_NativeDoesNotExistImpl<0>::offsetOfShape(idx);
 }
 
 template <size_t ProtoChainDepth>
 ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>::ICGetProp_NativeDoesNotExistImpl(
-        JitCode *stubCode, ICStub *firstMonitorStub, const AutoShapeVector *shapes)
-  : ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, ProtoChainDepth)
+        JitCode *stubCode, ICStub *firstMonitorStub, ReceiverGuard::Token guard,
+        const AutoShapeVector *shapes)
+  : ICGetProp_NativeDoesNotExist(stubCode, firstMonitorStub, guard, ProtoChainDepth)
 {
     MOZ_ASSERT(shapes->length() == NumShapes);
-    for (size_t i = 0; i < NumShapes; i++)
+
+    // Note: using int32_t here to avoid gcc warning.
+    for (int32_t i = 0; i < int32_t(NumShapes); i++)
         shapes_[i].init((*shapes)[i]);
 }
 
 ICGetPropNativeDoesNotExistCompiler::ICGetPropNativeDoesNotExistCompiler(
         JSContext *cx, ICStub *firstMonitorStub, HandleObject obj, size_t protoChainDepth)
   : ICStubCompiler(cx, ICStub::GetProp_NativeDoesNotExist),
     firstMonitorStub_(firstMonitorStub),
     obj_(cx, obj),
@@ -11935,21 +11918,22 @@ ICGetPropCallGetter::ICGetPropCallGetter
                kind == ICStub::GetProp_CallNative    ||
                kind == ICStub::GetProp_CallNativePrototype ||
                kind == ICStub::GetProp_CallDOMProxyNative ||
                kind == ICStub::GetProp_CallDOMProxyWithGenerationNative);
 }
 
 ICGetPropCallPrototypeGetter::ICGetPropCallPrototypeGetter(Kind kind, JitCode *stubCode,
                                                            ICStub *firstMonitorStub,
-                                                           Shape *receiverShape, JSObject *holder,
+                                                           ReceiverGuard::Token receiverGuard,
+                                                           JSObject *holder,
                                                            Shape *holderShape,
                                                            JSFunction *getter, uint32_t pcOffset)
   : ICGetPropCallGetter(kind, stubCode, firstMonitorStub, holder, holderShape, getter, pcOffset),
-    receiverShape_(receiverShape)
+    receiverGuard_(receiverGuard)
 {
     MOZ_ASSERT(kind == ICStub::GetProp_CallScripted || kind == ICStub::GetProp_CallNativePrototype);
 }
 
 ICInstanceOf_Function::ICInstanceOf_Function(JitCode *stubCode, Shape *shape,
                                              JSObject *prototypeObj, uint32_t slot)
   : ICStub(InstanceOf_Function, stubCode),
     shape_(shape),
@@ -11957,34 +11941,35 @@ ICInstanceOf_Function::ICInstanceOf_Func
     slot_(slot)
 { }
 
 /* static */ ICGetProp_CallScripted *
 ICGetProp_CallScripted::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
                               ICGetProp_CallScripted &other)
 {
     return New<ICGetProp_CallScripted>(space, other.jitCode(), firstMonitorStub,
-                                       other.receiverShape_, other.holder_, other.holderShape_,
+                                       other.receiverGuard().token(),
+                                       other.holder_, other.holderShape_,
                                        other.getter_, other.pcOffset_);
 }
 
 /* static */ ICGetProp_CallNative *
 ICGetProp_CallNative::Clone( ICStubSpace *space, ICStub *firstMonitorStub,
                             ICGetProp_CallNative &other)
 {
     return New<ICGetProp_CallNative>(space, other.jitCode(), firstMonitorStub, other.holder_,
                                      other.holderShape_, other.getter_, other.pcOffset_);
 }
 
 /* static */ ICGetProp_CallNativePrototype *
 ICGetProp_CallNativePrototype::Clone(ICStubSpace *space, ICStub *firstMonitorStub,
                                      ICGetProp_CallNativePrototype &other)
 {
     return New<ICGetProp_CallNativePrototype>(space, other.jitCode(), firstMonitorStub,
-                                              other.receiverShape_, other.holder_,
+                                              other.receiverGuard().token(), other.holder_,
                                               other.holderShape_, other.getter_, other.pcOffset_);
 }
 
 ICSetProp_Native::ICSetProp_Native(JitCode *stubCode, ObjectGroup *group, Shape *shape,
                                    uint32_t offset)
   : ICUpdatedStub(SetProp_Native, stubCode),
     group_(group),
     shape_(shape),
@@ -12046,40 +12031,40 @@ ICSetPropNativeAddCompiler::ICSetPropNat
     oldGroup_(cx, oldGroup),
     protoChainDepth_(protoChainDepth),
     isFixedSlot_(isFixedSlot),
     offset_(offset)
 {
     MOZ_ASSERT(protoChainDepth_ <= ICSetProp_NativeAdd::MAX_PROTO_CHAIN_DEPTH);
 }
 
-ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, Shape *shape,
+ICSetPropCallSetter::ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::Token guard,
                                          JSObject *holder, Shape *holderShape,
                                          JSFunction *setter, uint32_t pcOffset)
   : ICStub(kind, stubCode),
-    shape_(shape),
+    guard_(guard),
     holder_(holder),
     holderShape_(holderShape),
     setter_(setter),
     pcOffset_(pcOffset)
 {
     MOZ_ASSERT(kind == ICStub::SetProp_CallScripted || kind == ICStub::SetProp_CallNative);
 }
 
 /* static */ ICSetProp_CallScripted *
 ICSetProp_CallScripted::Clone(ICStubSpace *space, ICStub *, ICSetProp_CallScripted &other)
 {
-    return New<ICSetProp_CallScripted>(space, other.jitCode(), other.shape_, other.holder_,
+    return New<ICSetProp_CallScripted>(space, other.jitCode(), other.guard().token(), other.holder_,
                                        other.holderShape_, other.setter_, other.pcOffset_);
 }
 
 /* static */ ICSetProp_CallNative *
 ICSetProp_CallNative::Clone(ICStubSpace *space, ICStub *, ICSetProp_CallNative &other)
 {
-    return New<ICSetProp_CallNative>(space, other.jitCode(), other.shape_, other.holder_,
+    return New<ICSetProp_CallNative>(space, other.jitCode(), other.guard().token(), other.holder_,
                                      other.holderShape_, other.setter_, other.pcOffset_);
 }
 
 ICCall_Scripted::ICCall_Scripted(JitCode *stubCode, ICStub *firstMonitorStub,
                                  JSFunction *callee, JSObject *templateObject,
                                  uint32_t pcOffset)
   : ICMonitoredStub(ICStub::Call_Scripted, stubCode, firstMonitorStub),
     callee_(callee),
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -14,16 +14,17 @@
 #include "jsgc.h"
 #include "jsopcode.h"
 
 #include "builtin/TypedObject.h"
 #include "jit/BaselineJIT.h"
 #include "jit/BaselineRegisters.h"
 #include "vm/ArrayObject.h"
 #include "vm/TypedArrayCommon.h"
+#include "vm/UnboxedObject.h"
 
 namespace js {
 
 class TypedArrayLayout;
 
 namespace jit {
 
 //
@@ -426,17 +427,16 @@ class ICEntry
                                 \
     _(GetProp_Fallback)         \
     _(GetProp_ArrayLength)      \
     _(GetProp_Primitive)        \
     _(GetProp_StringLength)     \
     _(GetProp_Native)           \
     _(GetProp_NativeDoesNotExist) \
     _(GetProp_NativePrototype)  \
-    _(GetProp_UnboxedPrototype) \
     _(GetProp_Unboxed)          \
     _(GetProp_TypedObject)      \
     _(GetProp_CallScripted)     \
     _(GetProp_CallNative)       \
     _(GetProp_CallNativePrototype)\
     _(GetProp_CallDOMProxyNative)\
     _(GetProp_CallDOMProxyWithGenerationNative)\
     _(GetProp_DOMProxyShadowed) \
@@ -839,17 +839,16 @@ class ICStub
           // but the fallback code for the bailout path needs to pop the stub frame
           // pushed during the bailout.
           case GetProp_Fallback:
           case SetProp_Fallback:
 #if JS_HAS_NO_SUCH_METHOD
           case GetElem_Dense:
           case GetElem_Arguments:
           case GetProp_NativePrototype:
-          case GetProp_UnboxedPrototype:
           case GetProp_Native:
 #endif
             return true;
           default:
             return false;
         }
     }
 
@@ -3890,131 +3889,150 @@ class ICGetProp_Native : public ICGetPro
     static size_t offsetOfShape() {
         return offsetof(ICGetProp_Native, shape_);
     }
 
     static ICGetProp_Native *Clone(ICStubSpace *space, ICStub *firstMonitorStub,
                                    ICGetProp_Native &other);
 };
 
-// Stub for accessing a property on a native object's prototype. Note that due to
-// the shape teleporting optimization, we only have to guard on the object's shape
-// and the holder's shape.
+// Structure encapsulating the guarding that needs to be done on an object
+// which might be either native or unboxed. In the former case, only the
+// object's shape needs to be guarded. In the latter case, only the object's
+// group needs to be guarded.
+class ReceiverGuard
+{
+    HeapPtrShape shape_;
+    HeapPtrObjectGroup group_;
+
+  public:
+    typedef uintptr_t Token;
+
+    static Token shapeToken(Shape *shape) {
+        return reinterpret_cast<Token>(shape) | 1;
+    }
+
+    static Token groupToken(ObjectGroup *group) {
+        return reinterpret_cast<Token>(group);
+    }
+
+    static Shape *tokenShape(Token token) {
+        if (token & 1)
+            return reinterpret_cast<Shape *>(token & ~1);
+        return nullptr;
+    }
+
+    static ObjectGroup *tokenGroup(Token token) {
+        if (!(token & 1))
+            return reinterpret_cast<ObjectGroup *>(token);
+        return nullptr;
+    }
+
+    static Token objectToken(JSObject *obj) {
+        if (obj->is<UnboxedPlainObject>())
+            return groupToken(obj->group());
+        return shapeToken(obj->lastProperty());
+    }
+
+    explicit ReceiverGuard(Token token)
+      : shape_(tokenShape(token)), group_(tokenGroup(token))
+    {
+        MOZ_ASSERT(shape_ || group_);
+    }
+
+    void trace(JSTracer *trc);
+
+    Token token() {
+        MOZ_ASSERT(!!shape_ != !!group_);
+        if (shape_)
+            return shapeToken(shape_);
+        return groupToken(group_);
+    }
+
+    Shape *shape() const {
+        return shape_;
+    }
+    ObjectGroup *group() const {
+        return group_;
+    }
+
+    static size_t offsetOfShape() {
+        return offsetof(ReceiverGuard, shape_);
+    }
+    static size_t offsetOfGroup() {
+        return offsetof(ReceiverGuard, group_);
+    }
+};
+
+// Stub for accessing a property on the native prototype of a native or unboxed
+// object. Note that due to the shape teleporting optimization, we only have to
+// guard on the object's shape/group and the holder's shape.
 class ICGetProp_NativePrototype : public ICGetPropNativeStub
 {
     friend class ICStubSpace;
 
   protected:
-    // Object shape (lastProperty).
-    HeapPtrShape shape_;
+    // Object shape/group.
+    ReceiverGuard guard_;
 
     // Holder and its shape.
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
-    ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub, Shape *shape,
+    ICGetProp_NativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
+                              ReceiverGuard::Token guard,
                               uint32_t offset, JSObject *holder, Shape *holderShape);
 
   public:
     static ICGetProp_NativePrototype *Clone(ICStubSpace *space,
                                             ICStub *firstMonitorStub,
                                             ICGetProp_NativePrototype &other);
 
   public:
-    HeapPtrShape &shape() {
-        return shape_;
+    ReceiverGuard &guard() {
+        return guard_;
     }
     HeapPtrObject &holder() {
         return holder_;
     }
     HeapPtrShape &holderShape() {
         return holderShape_;
     }
-    static size_t offsetOfShape() {
-        return offsetof(ICGetProp_NativePrototype, shape_);
+    static size_t offsetOfGuard() {
+        return offsetof(ICGetProp_NativePrototype, guard_);
     }
     static size_t offsetOfHolder() {
         return offsetof(ICGetProp_NativePrototype, holder_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICGetProp_NativePrototype, holderShape_);
     }
 };
 
-// Stub for accessing a property on an unboxed object's native prototype. Note
-// that due to the shape teleporting optimization, we only have to guard on the
-// object's type and the holder's shape.
-class ICGetProp_UnboxedPrototype : public ICGetPropNativeStub
-{
-    friend class ICStubSpace;
-
-  protected:
-    // Object group.
-    HeapPtrObjectGroup group_;
-
-    // Holder and its shape.
-    HeapPtrObject holder_;
-    HeapPtrShape holderShape_;
-
-    ICGetProp_UnboxedPrototype(JitCode *stubCode, ICStub *firstMonitorStub,
-                               ObjectGroup *group,
-                               uint32_t offset, JSObject *holder, Shape *holderShape);
-
-  public:
-    static ICGetProp_UnboxedPrototype *Clone(ICStubSpace *space,
-                                             ICStub *firstMonitorStub,
-                                             ICGetProp_UnboxedPrototype &other);
-
-  public:
-    HeapPtrObjectGroup &group() {
-        return group_;
-    }
-    HeapPtrObject &holder() {
-        return holder_;
-    }
-    HeapPtrShape &holderShape() {
-        return holderShape_;
-    }
-    static size_t offsetOfGroup() {
-        return offsetof(ICGetProp_UnboxedPrototype, group_);
-    }
-    static size_t offsetOfHolder() {
-        return offsetof(ICGetProp_UnboxedPrototype, holder_);
-    }
-    static size_t offsetOfHolderShape() {
-        return offsetof(ICGetProp_UnboxedPrototype, holderShape_);
-    }
-};
-
 // Compiler for native GetProp stubs.
 class ICGetPropNativeCompiler : public ICStubCompiler
 {
     bool isCallProp_;
     ICStub *firstMonitorStub_;
     HandleObject obj_;
     HandleObject holder_;
     HandlePropertyName propName_;
     bool isFixedSlot_;
     uint32_t offset_;
     bool inputDefinitelyObject_;
 
     bool generateStubCode(MacroAssembler &masm);
 
   protected:
     virtual int32_t getKey() const {
-#if JS_HAS_NO_SUCH_METHOD
         return static_cast<int32_t>(kind) |
                (static_cast<int32_t>(isCallProp_) << 16) |
                (static_cast<int32_t>(isFixedSlot_) << 17) |
-               (static_cast<int32_t>(inputDefinitelyObject_) << 18);
-#else
-        return static_cast<int32_t>(kind) |
-               (static_cast<int32_t>(isFixedSlot_) << 16) |
-               (static_cast<int32_t>(inputDefinitelyObject_) << 17);
-#endif
+               (static_cast<int32_t>(inputDefinitelyObject_) << 18) |
+               (static_cast<int32_t>(obj_->isNative()) << 19);
     }
 
   public:
     ICGetPropNativeCompiler(JSContext *cx, ICStub::Kind kind, bool isCallProp,
                             ICStub *firstMonitorStub, HandleObject obj, HandleObject holder,
                             HandlePropertyName propName, bool isFixedSlot, uint32_t offset,
                             bool inputDefinitelyObject = false)
       : ICStubCompiler(cx, kind),
@@ -4032,83 +4050,98 @@ class ICGetPropNativeCompiler : public I
 };
 
 template <size_t ProtoChainDepth> class ICGetProp_NativeDoesNotExistImpl;
 
 class ICGetProp_NativeDoesNotExist : public ICMonitoredStub
 {
     friend class ICStubSpace;
   public:
+    ReceiverGuard guard_;
+
     static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
 
   protected:
     ICGetProp_NativeDoesNotExist(JitCode *stubCode, ICStub *firstMonitorStub,
+                                 ReceiverGuard::Token guard,
                                  size_t protoChainDepth);
 
   public:
     size_t protoChainDepth() const {
         MOZ_ASSERT(extra_ <= MAX_PROTO_CHAIN_DEPTH);
         return extra_;
     }
 
     template <size_t ProtoChainDepth>
     ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *toImpl() {
         MOZ_ASSERT(ProtoChainDepth == protoChainDepth());
         return static_cast<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> *>(this);
     }
 
+    ReceiverGuard &guard() {
+        return guard_;
+    }
+
+    static size_t offsetOfGuard() {
+        return offsetof(ICGetProp_NativeDoesNotExist, guard_);
+    }
+
     static size_t offsetOfShape(size_t idx);
 };
 
 template <size_t ProtoChainDepth>
 class ICGetProp_NativeDoesNotExistImpl : public ICGetProp_NativeDoesNotExist
 {
     friend class ICStubSpace;
   public:
     static const size_t MAX_PROTO_CHAIN_DEPTH = 8;
-    static const size_t NumShapes = ProtoChainDepth + 1;
+    static const size_t NumShapes = ProtoChainDepth;
 
   private:
     mozilla::Array<HeapPtrShape, NumShapes> shapes_;
 
     ICGetProp_NativeDoesNotExistImpl(JitCode *stubCode, ICStub *firstMonitorStub,
+                                     ReceiverGuard::Token guard,
                                      const AutoShapeVector *shapes);
 
   public:
     void traceShapes(JSTracer *trc) {
-        for (size_t i = 0; i < NumShapes; i++)
+        // Note: using int32_t here to avoid gcc warning.
+        for (int32_t i = 0; i < int32_t(NumShapes); i++)
             MarkShape(trc, &shapes_[i], "baseline-getpropnativedoesnotexist-stub-shape");
     }
 
     static size_t offsetOfShape(size_t idx) {
         return offsetof(ICGetProp_NativeDoesNotExistImpl, shapes_) + (idx * sizeof(HeapPtrShape));
     }
 };
 
 class ICGetPropNativeDoesNotExistCompiler : public ICStubCompiler
 {
     ICStub *firstMonitorStub_;
     RootedObject obj_;
     size_t protoChainDepth_;
 
   protected:
     virtual int32_t getKey() const {
-        return static_cast<int32_t>(kind) | (static_cast<int32_t>(protoChainDepth_) << 16);
+        return static_cast<int32_t>(kind) |
+               (static_cast<int32_t>(obj_->isNative()) << 16) |
+               (static_cast<int32_t>(protoChainDepth_) << 17);
     }
 
     bool generateStubCode(MacroAssembler &masm);
 
   public:
     ICGetPropNativeDoesNotExistCompiler(JSContext *cx, ICStub *firstMonitorStub,
                                         HandleObject obj, size_t protoChainDepth);
 
     template <size_t ProtoChainDepth>
     ICStub *getStubSpecific(ICStubSpace *space, const AutoShapeVector *shapes) {
-        return ICStub::New<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth>>(space, getStubCode(),
-                                                                              firstMonitorStub_, shapes);
+        return ICStub::New<ICGetProp_NativeDoesNotExistImpl<ProtoChainDepth> >
+            (space, getStubCode(), firstMonitorStub_, ReceiverGuard::objectToken(obj_), shapes);
     }
 
     ICStub *getStub(ICStubSpace *space);
 };
 
 class ICGetProp_Unboxed : public ICMonitoredStub
 {
     friend class ICStubSpace;
@@ -4281,94 +4314,98 @@ class ICGetPropCallGetter : public ICMon
 
     class Compiler : public ICStubCompiler {
       protected:
         ICStub *firstMonitorStub_;
         RootedObject holder_;
         RootedFunction getter_;
         uint32_t pcOffset_;
         const Class *outerClass_;
+        bool receiverNative_;
 
         virtual int32_t getKey() const {
             return static_cast<int32_t>(kind) |
-                   (static_cast<int32_t>(!!outerClass_) << 16);
+                   (static_cast<int32_t>(receiverNative_) << 16) |
+                   (static_cast<int32_t>(!!outerClass_) << 17);
         }
 
       public:
         Compiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub,
                  HandleObject holder, HandleFunction getter, uint32_t pcOffset,
-                 const Class *outerClass)
+                 const Class *outerClass, bool receiverNative)
           : ICStubCompiler(cx, kind),
             firstMonitorStub_(firstMonitorStub),
             holder_(cx, holder),
             getter_(cx, getter),
             pcOffset_(pcOffset),
-            outerClass_(outerClass)
+            outerClass_(outerClass),
+            receiverNative_(receiverNative)
         {
             MOZ_ASSERT(kind == ICStub::GetProp_CallScripted ||
                        kind == ICStub::GetProp_CallNative ||
                        kind == ICStub::GetProp_CallNativePrototype);
         }
     };
 };
 
 // Stub for calling a getter (native or scripted) on a native object when the getter is kept on
 // the proto-chain.
 class ICGetPropCallPrototypeGetter : public ICGetPropCallGetter
 {
     friend class ICStubSpace;
 
   protected:
-    // shape of receiver object.
-    HeapPtrShape receiverShape_;
+    // Shape/group of receiver object.
+    ReceiverGuard receiverGuard_;
 
     ICGetPropCallPrototypeGetter(Kind kind, JitCode *stubCode, ICStub *firstMonitorStub,
-                                 Shape *receiverShape,
+                                 ReceiverGuard::Token receiverGuard,
                                  JSObject *holder, Shape *holderShape,
                                  JSFunction *getter, uint32_t pcOffset);
 
   public:
-    HeapPtrShape &receiverShape() {
-        return receiverShape_;
-    }
-
-    static size_t offsetOfReceiverShape() {
-        return offsetof(ICGetPropCallPrototypeGetter, receiverShape_);
+    ReceiverGuard &receiverGuard() {
+        return receiverGuard_;
+    }
+
+    static size_t offsetOfReceiverGuard() {
+        return offsetof(ICGetPropCallPrototypeGetter, receiverGuard_);
     }
 
     class Compiler : public ICGetPropCallGetter::Compiler {
       protected:
         RootedObject receiver_;
 
       public:
         Compiler(JSContext *cx, ICStub::Kind kind, ICStub *firstMonitorStub,
                  HandleObject obj, HandleObject holder, HandleFunction getter, uint32_t pcOffset,
                  const Class *outerClass)
           : ICGetPropCallGetter::Compiler(cx, kind, firstMonitorStub, holder, getter, pcOffset,
-                                          outerClass),
+                                          outerClass, obj->isNative()),
             receiver_(cx, obj)
         {
             MOZ_ASSERT(kind == ICStub::GetProp_CallScripted ||
                        kind == ICStub::GetProp_CallNativePrototype);
         }
     };
 };
 
 // Stub for calling a scripted getter on a native object when the getter is kept on the
 // proto-chain.
 class ICGetProp_CallScripted : public ICGetPropCallPrototypeGetter
 {
     friend class ICStubSpace;
 
   protected:
     ICGetProp_CallScripted(JitCode *stubCode, ICStub *firstMonitorStub,
-                           Shape *receiverShape, JSObject *holder, Shape *holderShape,
+                           ReceiverGuard::Token receiverGuard,
+                           JSObject *holder, Shape *holderShape,
                            JSFunction *getter, uint32_t pcOffset)
       : ICGetPropCallPrototypeGetter(GetProp_CallScripted, stubCode, firstMonitorStub,
-                                     receiverShape, holder, holderShape, getter, pcOffset)
+                                     receiverGuard, holder, holderShape, getter, pcOffset)
     {}
 
   public:
     static ICGetProp_CallScripted *Clone(ICStubSpace *space,
                                          ICStub *firstMonitorStub, ICGetProp_CallScripted &other);
 
     class Compiler : public ICGetPropCallPrototypeGetter::Compiler {
       protected:
@@ -4378,20 +4415,20 @@ class ICGetProp_CallScripted : public IC
         Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj,
                  HandleObject holder, HandleFunction getter, uint32_t pcOffset)
           : ICGetPropCallPrototypeGetter::Compiler(cx, ICStub::GetProp_CallScripted,
                                                    firstMonitorStub, obj, holder,
                                                    getter, pcOffset, /* outerClass = */ nullptr)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
-            RootedShape receiverShape(cx, receiver_->lastProperty());
-            RootedShape holderShape(cx, holder_->lastProperty());
+            ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
+            Shape *holderShape = holder_->lastProperty();
             return ICStub::New<ICGetProp_CallScripted>(space, getStubCode(), firstMonitorStub_,
-                                                       receiverShape, holder_, holderShape, getter_,
+                                                       guard, holder_, holderShape, getter_,
                                                        pcOffset_);
         }
     };
 };
 
 // Stub for calling an own native getter on a native object.
 class ICGetProp_CallNative : public ICGetPropCallGetter
 {
@@ -4420,17 +4457,17 @@ class ICGetProp_CallNative : public ICGe
                    (static_cast<int32_t>(inputDefinitelyObject_) << 16);
         }
 
       public:
         Compiler(JSContext *cx, ICStub *firstMonitorStub, HandleObject obj,
                  HandleFunction getter, uint32_t pcOffset, const Class *outerClass,
                  bool inputDefinitelyObject = false)
           : ICGetPropCallGetter::Compiler(cx, ICStub::GetProp_CallNative, firstMonitorStub,
-                                          obj, getter, pcOffset, outerClass),
+                                          obj, getter, pcOffset, outerClass, true),
             inputDefinitelyObject_(inputDefinitelyObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
             RootedShape shape(cx, holder_->lastProperty());
             return ICStub::New<ICGetProp_CallNative>(space, getStubCode(), firstMonitorStub_,
                                                      holder_, shape, getter_, pcOffset_);
         }
@@ -4439,20 +4476,20 @@ class ICGetProp_CallNative : public ICGe
 
 // Stub for calling an native getter on a native object when the getter is kept on the proto-chain.
 class ICGetProp_CallNativePrototype : public ICGetPropCallPrototypeGetter
 {
     friend class ICStubSpace;
 
   protected:
     ICGetProp_CallNativePrototype(JitCode *stubCode, ICStub *firstMonitorStub,
-                         Shape *receiverShape, JSObject *holder, Shape *holderShape,
+                         ReceiverGuard::Token receiverGuard, JSObject *holder, Shape *holderShape,
                          JSFunction *getter, uint32_t pcOffset)
       : ICGetPropCallPrototypeGetter(GetProp_CallNativePrototype, stubCode, firstMonitorStub,
-                                     receiverShape, holder, holderShape, getter, pcOffset)
+                                     receiverGuard, holder, holderShape, getter, pcOffset)
     {}
 
   public:
     static ICGetProp_CallNativePrototype *Clone(ICStubSpace *space,
                                                 ICStub *firstMonitorStub,
                                                 ICGetProp_CallNativePrototype &other);
 
     class Compiler : public ICGetPropCallPrototypeGetter::Compiler {
@@ -4471,20 +4508,20 @@ class ICGetProp_CallNativePrototype : pu
                  const Class *outerClass, bool inputDefinitelyObject = false)
           : ICGetPropCallPrototypeGetter::Compiler(cx, ICStub::GetProp_CallNativePrototype,
                                                    firstMonitorStub, obj, holder,
                                                    getter, pcOffset, outerClass),
             inputDefinitelyObject_(inputDefinitelyObject)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
-            RootedShape receiverShape(cx, receiver_->lastProperty());
-            RootedShape holderShape(cx, holder_->lastProperty());
+            ReceiverGuard::Token guard = ReceiverGuard::objectToken(receiver_);
+            Shape *holderShape = holder_->lastProperty();
             return ICStub::New<ICGetProp_CallNativePrototype>(space, getStubCode(), firstMonitorStub_,
-                                                              receiverShape, holder_, holderShape,
+                                                              guard, holder_, holderShape,
                                                               getter_, pcOffset_);
         }
     };
 };
 
 class ICGetPropCallDOMProxyNativeStub : public ICGetPropCallGetter
 {
   friend class ICStubSpace;
@@ -5081,54 +5118,54 @@ class ICSetProp_TypedObject : public ICU
 
         bool needsUpdateStubs() {
             return fieldDescr_->is<ReferenceTypeDescr>() &&
                    fieldDescr_->as<ReferenceTypeDescr>().type() != ReferenceTypeDescr::TYPE_STRING;
         }
     };
 };
 
-// Base stub for calling a setters on a native object.
+// Base stub for calling a setters on a native or unboxed object.
 class ICSetPropCallSetter : public ICStub
 {
     friend class ICStubSpace;
 
   protected:
-    // Object shape (lastProperty).
-    HeapPtrShape shape_;
+    // Object shape/group.
+    ReceiverGuard guard_;
 
     // Holder and shape.
     HeapPtrObject holder_;
     HeapPtrShape holderShape_;
 
     // Function to call.
     HeapPtrFunction setter_;
 
     // PC of call, for profiler
     uint32_t pcOffset_;
 
-    ICSetPropCallSetter(Kind kind, JitCode *stubCode, Shape *shape, JSObject *holder,
+    ICSetPropCallSetter(Kind kind, JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
                         Shape *holderShape, JSFunction *setter, uint32_t pcOffset);
 
   public:
-    HeapPtrShape &shape() {
-        return shape_;
+    ReceiverGuard &guard() {
+        return guard_;
     }
     HeapPtrObject &holder() {
         return holder_;
     }
     HeapPtrShape &holderShape() {
         return holderShape_;
     }
     HeapPtrFunction &setter() {
         return setter_;
     }
 
-    static size_t offsetOfShape() {
-        return offsetof(ICSetPropCallSetter, shape_);
+    static size_t offsetOfGuard() {
+        return offsetof(ICSetPropCallSetter, guard_);
     }
     static size_t offsetOfHolder() {
         return offsetof(ICSetPropCallSetter, holder_);
     }
     static size_t offsetOfHolderShape() {
         return offsetof(ICSetPropCallSetter, holderShape_);
     }
     static size_t offsetOfSetter() {
@@ -5140,16 +5177,21 @@ class ICSetPropCallSetter : public ICStu
 
     class Compiler : public ICStubCompiler {
       protected:
         RootedObject obj_;
         RootedObject holder_;
         RootedFunction setter_;
         uint32_t pcOffset_;
 
+        virtual int32_t getKey() const {
+            return static_cast<int32_t>(kind) |
+                   (static_cast<int32_t>(obj_->isNative()) << 16);
+        }
+
       public:
         Compiler(JSContext *cx, ICStub::Kind kind, HandleObject obj, HandleObject holder,
                  HandleFunction setter, uint32_t pcOffset)
           : ICStubCompiler(cx, kind),
             obj_(cx, obj),
             holder_(cx, holder),
             setter_(cx, setter),
             pcOffset_(pcOffset)
@@ -5160,19 +5202,19 @@ class ICSetPropCallSetter : public ICStu
 };
 
 // Stub for calling a scripted setter on a native object.
 class ICSetProp_CallScripted : public ICSetPropCallSetter
 {
     friend class ICStubSpace;
 
   protected:
-    ICSetProp_CallScripted(JitCode *stubCode, Shape *shape, JSObject *holder,
+    ICSetProp_CallScripted(JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
                            Shape *holderShape, JSFunction *setter, uint32_t pcOffset)
-      : ICSetPropCallSetter(SetProp_CallScripted, stubCode, shape, holder, holderShape,
+      : ICSetPropCallSetter(SetProp_CallScripted, stubCode, guard, holder, holderShape,
                             setter, pcOffset)
     {}
 
   public:
     static ICSetProp_CallScripted *Clone(ICStubSpace *space, ICStub *,
                                          ICSetProp_CallScripted &other);
 
     class Compiler : public ICSetPropCallSetter::Compiler {
@@ -5182,33 +5224,33 @@ class ICSetProp_CallScripted : public IC
       public:
         Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
                  uint32_t pcOffset)
           : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallScripted,
                                           obj, holder, setter, pcOffset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
-            RootedShape shape(cx, obj_->lastProperty());
-            RootedShape holderShape(cx, holder_->lastProperty());
-            return ICStub::New<ICSetProp_CallScripted>(space, getStubCode(), shape, holder_,
+            ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
+            Shape *holderShape = holder_->lastProperty();
+            return ICStub::New<ICSetProp_CallScripted>(space, getStubCode(), guard, holder_,
                                                        holderShape, setter_, pcOffset_);
         }
     };
 };
 
 // Stub for calling a native setter on a native object.
 class ICSetProp_CallNative : public ICSetPropCallSetter
 {
     friend class ICStubSpace;
 
   protected:
-    ICSetProp_CallNative(JitCode *stubCode, Shape *shape, JSObject *holder,
+    ICSetProp_CallNative(JitCode *stubCode, ReceiverGuard::Token guard, JSObject *holder,
                          Shape *holderShape, JSFunction *setter, uint32_t pcOffset)
-      : ICSetPropCallSetter(SetProp_CallNative, stubCode, shape, holder, holderShape,
+      : ICSetPropCallSetter(SetProp_CallNative, stubCode, guard, holder, holderShape,
                             setter, pcOffset)
     {}
 
   public:
     static ICSetProp_CallNative *Clone(ICStubSpace *space, ICStub *,
                                        ICSetProp_CallNative &other);
 
     class Compiler : public ICSetPropCallSetter::Compiler {
@@ -5218,19 +5260,19 @@ class ICSetProp_CallNative : public ICSe
       public:
         Compiler(JSContext *cx, HandleObject obj, HandleObject holder, HandleFunction setter,
                  uint32_t pcOffset)
           : ICSetPropCallSetter::Compiler(cx, ICStub::SetProp_CallNative,
                                           obj, holder, setter, pcOffset)
         {}
 
         ICStub *getStub(ICStubSpace *space) {
-            RootedShape shape(cx, obj_->lastProperty());
-            RootedShape holderShape(cx, holder_->lastProperty());
-            return ICStub::New<ICSetProp_CallNative>(space, getStubCode(), shape, holder_,
+            ReceiverGuard::Token guard = ReceiverGuard::objectToken(obj_);
+            Shape *holderShape = holder_->lastProperty();
+            return ICStub::New<ICSetProp_CallNative>(space, getStubCode(), guard, holder_,
                                                      holderShape, setter_, pcOffset_);
         }
     };
 };
 
 // Call
 //      JSOP_CALL
 //      JSOP_FUNAPPLY
--- a/js/src/jit/BaselineInspector.cpp
+++ b/js/src/jit/BaselineInspector.cpp
@@ -569,71 +569,77 @@ BaselineInspector::templateCallObject()
     return &res->as<CallObject>();
 }
 
 static Shape *GlobalShapeForGetPropFunction(ICStub *stub)
 {
     if (stub->isGetProp_CallNativePrototype()) {
         ICGetProp_CallNativePrototype *nstub =
             stub->toGetProp_CallNativePrototype();
-        if (nstub->receiverShape()->getObjectClass()->flags & JSCLASS_IS_GLOBAL)
-            return nstub->receiverShape();
+        const ReceiverGuard &guard = nstub->receiverGuard();
+        if (Shape *shape = guard.shape()) {
+            if (shape->getObjectClass()->flags & JSCLASS_IS_GLOBAL)
+                return shape;
+        }
     }
     return nullptr;
 }
 
 static bool
-AddReceiverShape(BaselineInspector::ShapeVector &shapes, Shape *shape)
+AddReceiver(BaselineInspector::ShapeVector &nativeShapes,
+            BaselineInspector::ObjectGroupVector &unboxedGroups,
+            ReceiverGuard::Token receiver)
 {
-    MOZ_ASSERT(shape);
-
-    for (size_t i = 0; i < shapes.length(); i++) {
-        if (shapes[i] == shape)
-            return true;
-    }
-
-    return shapes.append(shape);
+    if (Shape *shape = ReceiverGuard::tokenShape(receiver))
+        return VectorAppendNoDuplicate(nativeShapes, shape);
+    ObjectGroup *group = ReceiverGuard::tokenGroup(receiver);
+    return VectorAppendNoDuplicate(unboxedGroups, group);
 }
 
 static bool
-AddReceiverShapeForGetPropFunction(BaselineInspector::ShapeVector &shapes, ICStub *stub)
+AddReceiverForGetPropFunction(BaselineInspector::ShapeVector &nativeShapes,
+                              BaselineInspector::ObjectGroupVector &unboxedGroups,
+                              ICStub *stub)
 {
     if (stub->isGetProp_CallNative())
         return true;
 
-    Shape *shape = nullptr;
+    ReceiverGuard::Token token;
     if (stub->isGetProp_CallScripted())
-        shape = stub->toGetProp_CallScripted()->receiverShape();
+        token = stub->toGetProp_CallScripted()->receiverGuard().token();
     else
-        shape = stub->toGetProp_CallNativePrototype()->receiverShape();
+        token = stub->toGetProp_CallNativePrototype()->receiverGuard().token();
 
-    return AddReceiverShape(shapes, shape);
+    return AddReceiver(nativeShapes, unboxedGroups, token);
 }
 
 bool
 BaselineInspector::commonGetPropFunction(jsbytecode *pc, JSObject **holder, Shape **holderShape,
                                          JSFunction **commonGetter, Shape **globalShape,
-                                         bool *isOwnProperty, ShapeVector &receiverShapes)
+                                         bool *isOwnProperty,
+                                         ShapeVector &nativeShapes,
+                                         ObjectGroupVector &unboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
-    MOZ_ASSERT(receiverShapes.empty());
+    MOZ_ASSERT(nativeShapes.empty());
+    MOZ_ASSERT(unboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry &entry = icEntryFromPC(pc);
 
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isGetProp_CallScripted()  ||
             stub->isGetProp_CallNative()    ||
             stub->isGetProp_CallNativePrototype())
         {
             ICGetPropCallGetter *nstub = static_cast<ICGetPropCallGetter *>(stub);
             bool isOwn = stub->isGetProp_CallNative();
-            if (!AddReceiverShapeForGetPropFunction(receiverShapes, nstub))
+            if (!AddReceiverForGetPropFunction(nativeShapes, unboxedGroups, nstub))
                 return false;
 
             if (!*holder) {
                 *holder = nstub->holder();
                 *holderShape = nstub->holderShape();
                 *commonGetter = nstub->getter();
                 *globalShape = GlobalShapeForGetPropFunction(nstub);
                 *isOwnProperty = isOwn;
@@ -655,37 +661,39 @@ BaselineInspector::commonGetPropFunction
         } else {
             return false;
         }
     }
 
     if (!*holder)
         return false;
 
-    MOZ_ASSERT(*isOwnProperty == receiverShapes.empty());
+    MOZ_ASSERT(*isOwnProperty == (nativeShapes.empty() && unboxedGroups.empty()));
     return true;
 }
 
 bool
 BaselineInspector::commonSetPropFunction(jsbytecode *pc, JSObject **holder, Shape **holderShape,
                                          JSFunction **commonSetter, bool *isOwnProperty,
-                                         ShapeVector &receiverShapes)
+                                         ShapeVector &nativeShapes,
+                                         ObjectGroupVector &unboxedGroups)
 {
     if (!hasBaselineScript())
         return false;
 
-    MOZ_ASSERT(receiverShapes.empty());
+    MOZ_ASSERT(nativeShapes.empty());
+    MOZ_ASSERT(unboxedGroups.empty());
 
     *holder = nullptr;
     const ICEntry &entry = icEntryFromPC(pc);
 
     for (ICStub *stub = entry.firstStub(); stub; stub = stub->next()) {
         if (stub->isSetProp_CallScripted() || stub->isSetProp_CallNative()) {
             ICSetPropCallSetter *nstub = static_cast<ICSetPropCallSetter *>(stub);
-            if (!AddReceiverShape(receiverShapes, nstub->shape()))
+            if (!AddReceiver(nativeShapes, unboxedGroups, nstub->guard().token()))
                 return false;
 
             if (!*holder) {
                 *holder = nstub->holder();
                 *holderShape = nstub->holderShape();
                 *commonSetter = nstub->setter();
                 *isOwnProperty = false;
             } else if (nstub->holderShape() != *holderShape) {
--- a/js/src/jit/BaselineInspector.h
+++ b/js/src/jit/BaselineInspector.h
@@ -121,20 +121,20 @@ class BaselineInspector
 
     JSFunction *getSingleCallee(jsbytecode *pc);
 
     DeclEnvObject *templateDeclEnvObject();
     CallObject *templateCallObject();
 
     bool commonGetPropFunction(jsbytecode *pc, JSObject **holder, Shape **holderShape,
                                JSFunction **commonGetter, Shape **globalShape, bool *isOwnProperty,
-                               ShapeVector &receiverShapes);
+                               ShapeVector &nativeShapes, ObjectGroupVector &unboxedGroups);
     bool commonSetPropFunction(jsbytecode *pc, JSObject **holder, Shape **holderShape,
                                JSFunction **commonSetter, bool *isOwnProperty,
-                               ShapeVector &receiverShapes);
+                               ShapeVector &nativeShapes, ObjectGroupVector &unboxedGroups);
 
     bool instanceOfData(jsbytecode *pc, Shape **shape, uint32_t *slot, JSObject **prototypeObject);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BaselineInspector_h */
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2522,33 +2522,48 @@ CodeGenerator::visitGuardObjectIdentity(
     Register expected = ToRegister(guard->expected());
 
     Assembler::Condition cond =
         guard->mir()->bailOnEquality() ? Assembler::Equal : Assembler::NotEqual;
     bailoutCmpPtr(cond, input, expected, guard->snapshot());
 }
 
 void
-CodeGenerator::visitGuardShapePolymorphic(LGuardShapePolymorphic *lir)
-{
-    const MGuardShapePolymorphic *mir = lir->mir();
+CodeGenerator::visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic *lir)
+{
+    const MGuardReceiverPolymorphic *mir = lir->mir();
     Register obj = ToRegister(lir->object());
     Register temp = ToRegister(lir->temp());
 
-    MOZ_ASSERT(mir->numShapes() > 1);
+    MOZ_ASSERT(mir->numShapes() + mir->numUnboxedGroups() > 1);
 
     Label done;
-    masm.loadObjShape(obj, temp);
-
-    for (size_t i = 0; i < mir->numShapes(); i++) {
-        Shape *shape = mir->getShape(i);
-        if (i == mir->numShapes() - 1)
-            bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), lir->snapshot());
-        else
-            masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done);
+
+    if (mir->numShapes()) {
+        masm.loadObjShape(obj, temp);
+
+        for (size_t i = 0; i < mir->numShapes(); i++) {
+            Shape *shape = mir->getShape(i);
+            if (i == mir->numShapes() - 1 && !mir->numUnboxedGroups())
+                bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(shape), lir->snapshot());
+            else
+                masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(shape), &done);
+        }
+    }
+
+    if (mir->numUnboxedGroups()) {
+        masm.loadObjGroup(obj, temp);
+
+        for (size_t i = 0; i < mir->numUnboxedGroups(); i++) {
+            ObjectGroup *group = mir->getUnboxedGroup(i);
+            if (i == mir->numUnboxedGroups() - 1)
+                bailoutCmpPtr(Assembler::NotEqual, temp, ImmGCPtr(group), lir->snapshot());
+            else
+                masm.branchPtr(Assembler::Equal, temp, ImmGCPtr(group), &done);
+        }
     }
 
     masm.bind(&done);
 }
 
 void
 CodeGenerator::visitTypeBarrierV(LTypeBarrierV *lir)
 {
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -117,17 +117,17 @@ class CodeGenerator : public CodeGenerat
     void visitLoadSlotV(LLoadSlotV *lir);
     void visitStoreSlotT(LStoreSlotT *lir);
     void visitStoreSlotV(LStoreSlotV *lir);
     void visitElements(LElements *lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles *lir);
     void visitMaybeToDoubleElement(LMaybeToDoubleElement *lir);
     void visitMaybeCopyElementsForWrite(LMaybeCopyElementsForWrite *lir);
     void visitGuardObjectIdentity(LGuardObjectIdentity *guard);
-    void visitGuardShapePolymorphic(LGuardShapePolymorphic *lir);
+    void visitGuardReceiverPolymorphic(LGuardReceiverPolymorphic *lir);
     void visitTypeBarrierV(LTypeBarrierV *lir);
     void visitTypeBarrierO(LTypeBarrierO *lir);
     void visitMonitorTypes(LMonitorTypes *lir);
     void visitPostWriteBarrierO(LPostWriteBarrierO *lir);
     void visitPostWriteBarrierV(LPostWriteBarrierV *lir);
     void visitOutOfLineCallPostWriteBarrier(OutOfLineCallPostWriteBarrier *ool);
     void visitCallNative(LCallNative *call);
     void emitCallInvokeFunction(LInstruction *call, Register callereg,
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -10449,62 +10449,66 @@ IonBuilder::getPropTryUnboxed(bool *emit
 
     trackOptimizationSuccess();
     *emitted = true;
     return true;
 }
 
 MDefinition *
 IonBuilder::addShapeGuardsForGetterSetter(MDefinition *obj, JSObject *holder, Shape *holderShape,
-                                          const BaselineInspector::ShapeVector &receiverShapes,
-                                          bool isOwnProperty)
+                const BaselineInspector::ShapeVector &receiverShapes,
+                const BaselineInspector::ObjectGroupVector &receiverUnboxedGroups,
+                bool isOwnProperty)
 {
     MOZ_ASSERT(holder);
     MOZ_ASSERT(holderShape);
 
     if (isOwnProperty) {
         MOZ_ASSERT(receiverShapes.empty());
         return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
     }
 
     MDefinition *holderDef = constantMaybeNursery(holder);
     addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
 
-    return addShapeGuardPolymorphic(obj, receiverShapes);
+    return addShapeGuardPolymorphic(obj, receiverShapes, receiverUnboxedGroups);
 }
 
 bool
 IonBuilder::getPropTryCommonGetter(bool *emitted, MDefinition *obj, PropertyName *name,
                                    TemporaryTypeSet *types)
 {
     MOZ_ASSERT(*emitted == false);
 
     Shape *lastProperty = nullptr;
     JSFunction *commonGetter = nullptr;
     Shape *globalShape = nullptr;
     JSObject *foundProto = nullptr;
     bool isOwnProperty = false;
     BaselineInspector::ShapeVector receiverShapes(alloc());
+    BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc());
     if (!inspector->commonGetPropFunction(pc, &foundProto, &lastProperty, &commonGetter,
-                                          &globalShape, &isOwnProperty, receiverShapes))
+                                          &globalShape, &isOwnProperty,
+                                          receiverShapes, receiverUnboxedGroups))
     {
         return true;
     }
 
     TemporaryTypeSet *objTypes = obj->resultTypeSet();
     MDefinition *guard = nullptr;
     MDefinition *globalGuard = nullptr;
     bool canUseTIForGetter =
         testCommonGetterSetter(objTypes, name, /* isGetter = */ true,
                                foundProto, lastProperty, &guard, globalShape,
                                &globalGuard);
     if (!canUseTIForGetter) {
         // If type information is bad, we can still optimize the getter if we
         // shape guard.
-        obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, receiverShapes,
+        obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
+                                            receiverShapes, receiverUnboxedGroups,
                                             isOwnProperty);
         if (!obj)
             return false;
     }
 
     bool isDOM = objTypes && objTypes->isDOMClass(constraints());
 
     if (isDOM && testShouldDOMCall(objTypes, commonGetter, JSJitInfo::Getter)) {
@@ -10704,24 +10708,17 @@ IonBuilder::getPropTryInlineAccess(bool 
         *emitted = true;
         return true;
     }
 
     if (nativeShapes.empty() && unboxedGroups.length() == 1) {
         spew("Inlining monomorphic unboxed GETPROP");
 
         ObjectGroup *group = unboxedGroups[0];
-
-        // Failures in this group guard should be treated the same as a shape guard failure.
-        obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false,
-                                     Bailout_ShapeGuard);
-        current->add(obj->toInstruction());
-
-        if (failedShapeGuard_)
-            obj->toGuardObjectGroup()->setNotMovable();
+        obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
         const UnboxedLayout::Property *property = group->unboxedLayout().lookup(name);
         MInstruction *load = loadUnboxedProperty(obj, property->offset, property->type, barrier, types);
         current->push(load);
 
         if (!pushTypeBarrier(load, types, barrier))
             return false;
 
@@ -10733,17 +10730,17 @@ IonBuilder::getPropTryInlineAccess(bool 
     spew("Inlining polymorphic GETPROP");
 
     BaselineInspector::ShapeVector propShapes(alloc());
     bool sameSlot;
     if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot))
         return false;
 
     if (sameSlot && unboxedGroups.empty()) {
-        obj = addShapeGuardPolymorphic(obj, nativeShapes);
+        obj = addShapeGuardPolymorphic(obj, nativeShapes, unboxedGroups);
         if (!obj)
             return false;
 
         if (!loadSlot(obj, propShapes[0], rvalType, barrier, types))
             return false;
 
         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
         *emitted = true;
@@ -10990,32 +10987,35 @@ IonBuilder::setPropTryCommonSetter(bool 
 {
     MOZ_ASSERT(*emitted == false);
 
     Shape *lastProperty = nullptr;
     JSFunction *commonSetter = nullptr;
     JSObject *foundProto = nullptr;
     bool isOwnProperty;
     BaselineInspector::ShapeVector receiverShapes(alloc());
-    if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter, &isOwnProperty,
-                                          receiverShapes))
+    BaselineInspector::ObjectGroupVector receiverUnboxedGroups(alloc());
+    if (!inspector->commonSetPropFunction(pc, &foundProto, &lastProperty, &commonSetter,
+                                          &isOwnProperty,
+                                          receiverShapes, receiverUnboxedGroups))
     {
         trackOptimizationOutcome(TrackedOutcome::NoProtoFound);
         return true;
     }
 
     TemporaryTypeSet *objTypes = obj->resultTypeSet();
     MDefinition *guard = nullptr;
     bool canUseTIForSetter =
         testCommonGetterSetter(objTypes, name, /* isGetter = */ false,
                                foundProto, lastProperty, &guard);
     if (!canUseTIForSetter) {
         // If type information is bad, we can still optimize the setter if we
         // shape guard.
-        obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty, receiverShapes,
+        obj = addShapeGuardsForGetterSetter(obj, foundProto, lastProperty,
+                                            receiverShapes, receiverUnboxedGroups,
                                             isOwnProperty);
         if (!obj)
             return false;
     }
 
     // Emit common setter.
 
     // Setters can be called even if the property write needs a type
@@ -11390,24 +11390,17 @@ IonBuilder::setPropTryInlineAccess(bool 
         *emitted = true;
         return true;
     }
 
     if (nativeShapes.empty() && unboxedGroups.length() == 1) {
         spew("Inlining monomorphic unboxed SETPROP");
 
         ObjectGroup *group = unboxedGroups[0];
-
-        // Failures in this group guard should be treated the same as a shape guard failure.
-        obj = MGuardObjectGroup::New(alloc(), obj, group, /* bailOnEquality = */ false,
-                                    Bailout_ShapeGuard);
-        current->add(obj->toInstruction());
-
-        if (failedShapeGuard_)
-            obj->toGuardObjectGroup()->setNotMovable();
+        obj = addGroupGuard(obj, group, Bailout_ShapeGuard);
 
         const UnboxedLayout::Property *property = group->unboxedLayout().lookup(name);
         storeUnboxedProperty(obj, property->offset, property->type, value);
 
         current->push(value);
 
         *emitted = true;
         return true;
@@ -11417,17 +11410,17 @@ IonBuilder::setPropTryInlineAccess(bool 
     spew("Inlining polymorphic SETPROP");
 
     BaselineInspector::ShapeVector propShapes(alloc());
     bool sameSlot;
     if (!GetPropertyShapes(NameToId(name), nativeShapes, propShapes, &sameSlot))
         return false;
 
     if (sameSlot && unboxedGroups.empty()) {
-        obj = addShapeGuardPolymorphic(obj, nativeShapes);
+        obj = addShapeGuardPolymorphic(obj, nativeShapes, unboxedGroups);
         if (!obj)
             return false;
 
         bool needsBarrier = objTypes->propertyNeedsBarrier(constraints(), NameToId(name));
         if (!storeSlot(obj, propShapes[0], value, needsBarrier))
             return false;
 
         trackOptimizationOutcome(TrackedOutcome::Polymorphic);
@@ -12329,34 +12322,63 @@ IonBuilder::addShapeGuard(MDefinition *o
     // If a shape guard failed in the past, don't optimize shape guard.
     if (failedShapeGuard_)
         guard->setNotMovable();
 
     return guard;
 }
 
 MInstruction *
-IonBuilder::addShapeGuardPolymorphic(MDefinition *obj, const BaselineInspector::ShapeVector &shapes)
-{
-    if (shapes.length() == 1)
+IonBuilder::addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind)
+{
+    MGuardObjectGroup *guard = MGuardObjectGroup::New(alloc(), obj, group,
+                                                      /* bailOnEquality = */ false,
+                                                      bailoutKind);
+    current->add(guard);
+
+    // If a shape guard failed in the past, don't optimize group guards.
+    if (failedShapeGuard_)
+        guard->setNotMovable();
+
+    LifoAlloc *lifoAlloc = alloc().lifoAlloc();
+    guard->setResultTypeSet(lifoAlloc->new_<TemporaryTypeSet>(lifoAlloc,
+                                                            TypeSet::ObjectType(group)));
+
+    return guard;
+}
+
+MInstruction *
+IonBuilder::addShapeGuardPolymorphic(MDefinition *obj,
+                                     const BaselineInspector::ShapeVector &shapes,
+                                     const BaselineInspector::ObjectGroupVector &unboxedGroups)
+{
+    if (shapes.length() == 1 && unboxedGroups.empty())
         return addShapeGuard(obj, shapes[0], Bailout_ShapeGuard);
 
-    MOZ_ASSERT(shapes.length() > 1);
-
-    MGuardShapePolymorphic *guard = MGuardShapePolymorphic::New(alloc(), obj);
+    if (shapes.empty() && unboxedGroups.length() == 1)
+        return addGroupGuard(obj, unboxedGroups[0], Bailout_ShapeGuard);
+
+    MOZ_ASSERT(shapes.length() + unboxedGroups.length() > 1);
+
+    MGuardReceiverPolymorphic *guard = MGuardReceiverPolymorphic::New(alloc(), obj);
     current->add(guard);
 
     if (failedShapeGuard_)
         guard->setNotMovable();
 
-    for (size_t i = 0, len = shapes.length(); i < len; i++) {
+    for (size_t i = 0; i < shapes.length(); i++) {
         if (!guard->addShape(shapes[i]))
             return nullptr;
     }
 
+    for (size_t i = 0; i < unboxedGroups.length(); i++) {
+        if (!guard->addUnboxedGroup(unboxedGroups[i]))
+            return nullptr;
+    }
+
     return guard;
 }
 
 TemporaryTypeSet *
 IonBuilder::bytecodeTypes(jsbytecode *pc)
 {
     return TypeScript::BytecodeTypes(script(), pc, bytecodeTypeMap, &typeArrayHint, typeArray);
 }
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -393,18 +393,22 @@ class IonBuilder
     MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj);
 
     MDefinition *walkScopeChain(unsigned hops);
 
     MInstruction *addConvertElementsToDoubles(MDefinition *elements);
     MDefinition *addMaybeCopyElementsForWrite(MDefinition *object);
     MInstruction *addBoundsCheck(MDefinition *index, MDefinition *length);
     MInstruction *addShapeGuard(MDefinition *obj, Shape *const shape, BailoutKind bailoutKind);
-    MInstruction *addShapeGuardPolymorphic(MDefinition *obj,
-                                           const BaselineInspector::ShapeVector &shapes);
+    MInstruction *addGroupGuard(MDefinition *obj, ObjectGroup *group, BailoutKind bailoutKind);
+
+    MInstruction *
+    addShapeGuardPolymorphic(MDefinition *obj,
+                             const BaselineInspector::ShapeVector &shapes,
+                             const BaselineInspector::ObjectGroupVector &unboxedGroups);
 
     MDefinition *convertShiftToMaskForStaticTypedArray(MDefinition *id,
                                                        Scalar::Type viewType);
 
     bool invalidatedIdempotentCache();
 
     bool hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall);
     bool loadSlot(MDefinition *obj, size_t slot, size_t nfixed, MIRType rvalType,
@@ -896,19 +900,21 @@ class IonBuilder
      */
     bool testCommonGetterSetter(TemporaryTypeSet *types, PropertyName *name,
                                 bool isGetter, JSObject *foundProto, Shape *lastProperty,
                                 MDefinition **guard, Shape *globalShape = nullptr,
                                 MDefinition **globalGuard = nullptr);
     bool testShouldDOMCall(TypeSet *inTypes,
                            JSFunction *func, JSJitInfo::OpType opType);
 
-    MDefinition *addShapeGuardsForGetterSetter(MDefinition *obj, JSObject *holder, Shape *holderShape,
-                                               const BaselineInspector::ShapeVector &receiverShapes,
-                                               bool isOwnProperty);
+    MDefinition *
+    addShapeGuardsForGetterSetter(MDefinition *obj, JSObject *holder, Shape *holderShape,
+                                  const BaselineInspector::ShapeVector &receiverShapes,
+                                  const BaselineInspector::ObjectGroupVector &receiverGroups,
+                                  bool isOwnProperty);
 
     bool annotateGetPropertyCache(MDefinition *obj, MGetPropertyCache *getPropCache,
                                   TemporaryTypeSet *objTypes,
                                   TemporaryTypeSet *pushedTypes);
 
     MGetPropertyCache *getInlineableGetPropertyCache(CallInfo &callInfo);
 
     JSObject *testSingletonProperty(JSObject *obj, PropertyName *name);
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1099,26 +1099,29 @@ EmitGetterCall(JSContext *cx, MacroAssem
 }
 
 static bool
 GenerateCallGetter(JSContext *cx, IonScript *ion, MacroAssembler &masm,
                    IonCache::StubAttacher &attacher, JSObject *obj, PropertyName *name,
                    JSObject *holder, HandleShape shape, RegisterSet &liveRegs, Register object,
                    TypedOrValueRegister output, void *returnAddr, Label *failures = nullptr)
 {
-    MOZ_ASSERT(obj->isNative());
     MOZ_ASSERT(output.hasValue());
 
     // Use the passed in label if there was one. Otherwise, we'll have to make our own.
     Label stubFailure;
     failures = failures ? failures : &stubFailure;
 
-    // Initial shape check.
-    masm.branchPtr(Assembler::NotEqual, Address(object, JSObject::offsetOfShape()),
-                   ImmGCPtr(obj->lastProperty()), failures);
+    // Initial shape/group check.
+    if (obj->isNative())
+        masm.branchTestObjShape(Assembler::NotEqual, object, obj->lastProperty(), failures);
+    else if (obj->is<UnboxedPlainObject>())
+        masm.branchTestObjGroup(Assembler::NotEqual, object, obj->group(), failures);
+    else
+        MOZ_CRASH("Unexpected object");
 
     Register scratchReg = output.valueReg().scratchReg();
     bool spillObjReg = scratchReg == object;
     Label pop1AndFail;
     Label *maybePopAndFail = failures;
 
     // Save off the object register if it aliases the scratchReg
     if (spillObjReg) {
@@ -2050,44 +2053,38 @@ SetPropertyIC::attachSetSlot(JSContext *
     GenerateSetSlot(cx, masm, attacher, obj, shape, object(), value(), needsTypeBarrier(),
                     checkTypeset);
     return linkAndAttachStub(cx, masm, attacher, ion, "setting");
 }
 
 static bool
 IsCacheableSetPropCallNative(HandleObject obj, HandleObject holder, HandleShape shape)
 {
-    MOZ_ASSERT(obj->isNative());
-
     if (!shape || !IsCacheableProtoChainForIon(obj, holder))
         return false;
 
     return shape->hasSetterValue() && shape->setterObject() &&
            shape->setterObject()->is<JSFunction>() &&
            shape->setterObject()->as<JSFunction>().isNative();
 }
 
 static bool
 IsCacheableSetPropCallScripted(HandleObject obj, HandleObject holder, HandleShape shape)
 {
-    MOZ_ASSERT(obj->isNative());
-
     if (!shape || !IsCacheableProtoChainForIon(obj, holder))
         return false;
 
     return shape->hasSetterValue() && shape->setterObject() &&
            shape->setterObject()->is<JSFunction>() &&
            shape->setterObject()->as<JSFunction>().hasJITCode();
 }
 
 static bool
 IsCacheableSetPropCallPropertyOp(HandleObject obj, HandleObject holder, HandleShape shape)
 {
-    MOZ_ASSERT(obj->isNative());
-
     if (!shape)
         return false;
 
     if (!IsCacheableProtoChainForIon(obj, holder))
         return false;
 
     if (shape->hasSlot())
         return false;
@@ -2558,26 +2555,26 @@ SetPropertyIC::attachDOMProxyUnshadowed(
     return linkAndAttachStub(cx, masm, attacher, ion, "DOM proxy unshadowed set");
 }
 
 bool
 SetPropertyIC::attachCallSetter(JSContext *cx, HandleScript outerScript, IonScript *ion,
                                 HandleObject obj, HandleObject holder, HandleShape shape,
                                 void *returnAddr)
 {
-    MOZ_ASSERT(obj->isNative());
-
     MacroAssembler masm(cx, ion, outerScript, profilerLeavePc_);
     RepatchStubAppender attacher(*this);
 
     Label failure;
-    masm.branchPtr(Assembler::NotEqual,
-                   Address(object(), JSObject::offsetOfShape()),
-                   ImmGCPtr(obj->lastProperty()),
-                   &failure);
+    if (obj->isNative())
+        masm.branchTestObjShape(Assembler::NotEqual, object(), obj->lastProperty(), &failure);
+    else if (obj->is<UnboxedPlainObject>())
+        masm.branchTestObjGroup(Assembler::NotEqual, object(), obj->group(), &failure);
+    else
+        MOZ_CRASH("Unexpected object");
 
     if (!GenerateCallSetter(cx, ion, masm, attacher, obj, holder, shape, strict(),
                             object(), value(), &failure, liveRegs_, returnAddr))
     {
         return false;
     }
 
     // Rejoin jump.
@@ -2834,22 +2831,22 @@ IsPropertyAddInlineable(NativeObject *ob
     return true;
 }
 
 static SetPropertyIC::NativeSetPropCacheability
 CanAttachNativeSetProp(JSContext *cx, HandleObject obj, HandleId id, ConstantOrRegister val,
                        bool needsTypeBarrier, MutableHandleObject holder,
                        MutableHandleShape shape, bool *checkTypeset)
 {
-    if (!obj->isNative())
-        return SetPropertyIC::CanAttachNone;
-
     // See if the property exists on the object.
-    if (IsPropertySetInlineable(&obj->as<NativeObject>(), id, shape, val, needsTypeBarrier, checkTypeset))
+    if (obj->isNative() && IsPropertySetInlineable(&obj->as<NativeObject>(), id, shape, val,
+                                                   needsTypeBarrier, checkTypeset))
+    {
         return SetPropertyIC::CanAttachSetSlot;
+    }
 
     // If we couldn't find the property on the object itself, do a full, but
     // still pure lookup for setters.
     if (!LookupPropertyPure(cx, obj, id, holder.address(), shape.address()))
         return SetPropertyIC::CanAttachNone;
 
     // If the object doesn't have the property, we don't know if we can attach
     // a stub to add the property until we do the VM call to add. If the
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -6074,33 +6074,33 @@ class LRest : public LCallInstructionHel
     const LAllocation *numActuals() {
         return getOperand(0);
     }
     MRest *mir() const {
         return mir_->toRest();
     }
 };
 
-class LGuardShapePolymorphic : public LInstructionHelper<0, 1, 1>
-{
-  public:
-    LIR_HEADER(GuardShapePolymorphic)
-
-    LGuardShapePolymorphic(const LAllocation &in, const LDefinition &temp) {
+class LGuardReceiverPolymorphic : public LInstructionHelper<0, 1, 1>
+{
+  public:
+    LIR_HEADER(GuardReceiverPolymorphic)
+
+    LGuardReceiverPolymorphic(const LAllocation &in, const LDefinition &temp) {
         setOperand(0, in);
         setTemp(0, temp);
     }
     const LAllocation *object() {
         return getOperand(0);
     }
     const LDefinition *temp() {
         return getTemp(0);
     }
-    const MGuardShapePolymorphic *mir() const {
-        return mir_->toGuardShapePolymorphic();
+    const MGuardReceiverPolymorphic *mir() const {
+        return mir_->toGuardReceiverPolymorphic();
     }
 };
 
 // Guard that a value is in a TypeSet.
 class LTypeBarrierV : public LInstructionHelper<0, BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(TypeBarrierV)
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -198,17 +198,17 @@
     _(ConvertElementsToDoubles)     \
     _(MaybeToDoubleElement)         \
     _(MaybeCopyElementsForWrite)    \
     _(LoadSlotV)                    \
     _(LoadSlotT)                    \
     _(StoreSlotV)                   \
     _(StoreSlotT)                   \
     _(GuardShape)                   \
-    _(GuardShapePolymorphic)        \
+    _(GuardReceiverPolymorphic)     \
     _(GuardObjectGroup)             \
     _(GuardObjectIdentity)          \
     _(GuardClass)                   \
     _(TypeBarrierV)                 \
     _(TypeBarrierO)                 \
     _(MonitorTypes)                 \
     _(PostWriteBarrierO)            \
     _(PostWriteBarrierV)            \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3229,23 +3229,23 @@ LIRGenerator::visitGuardString(MGuardStr
 void
 LIRGenerator::visitPolyInlineGuard(MPolyInlineGuard *ins)
 {
     MOZ_ASSERT(ins->input()->type() == MIRType_Object);
     redefine(ins, ins->input());
 }
 
 void
-LIRGenerator::visitGuardShapePolymorphic(MGuardShapePolymorphic *ins)
+LIRGenerator::visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic *ins)
 {
     MOZ_ASSERT(ins->obj()->type() == MIRType_Object);
     MOZ_ASSERT(ins->type() == MIRType_Object);
 
-    LGuardShapePolymorphic *guard =
-        new(alloc()) LGuardShapePolymorphic(useRegister(ins->obj()), temp());
+    LGuardReceiverPolymorphic *guard =
+        new(alloc()) LGuardReceiverPolymorphic(useRegister(ins->obj()), temp());
     assignSnapshot(guard, Bailout_ShapeGuard);
     add(guard, ins);
     redefine(ins, ins->obj());
 }
 
 void
 LIRGenerator::visitAssertRange(MAssertRange *ins)
 {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -215,17 +215,17 @@ class LIRGenerator : public LIRGenerator
     void visitGetPropertyPolymorphic(MGetPropertyPolymorphic *ins);
     void visitSetPropertyPolymorphic(MSetPropertyPolymorphic *ins);
     void visitGetElementCache(MGetElementCache *ins);
     void visitBindNameCache(MBindNameCache *ins);
     void visitGuardObjectIdentity(MGuardObjectIdentity *ins);
     void visitGuardClass(MGuardClass *ins);
     void visitGuardObject(MGuardObject *ins);
     void visitGuardString(MGuardString *ins);
-    void visitGuardShapePolymorphic(MGuardShapePolymorphic *ins);
+    void visitGuardReceiverPolymorphic(MGuardReceiverPolymorphic *ins);
     void visitPolyInlineGuard(MPolyInlineGuard *ins);
     void visitAssertRange(MAssertRange *ins);
     void visitCallGetProperty(MCallGetProperty *ins);
     void visitDeleteProperty(MDeleteProperty *ins);
     void visitDeleteElement(MDeleteElement *ins);
     void visitGetNameCache(MGetNameCache *ins);
     void visitCallGetIntrinsicValue(MCallGetIntrinsicValue *ins);
     void visitCallGetElement(MCallGetElement *ins);
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -4104,30 +4104,37 @@ MLoadElement::foldsTo(TempAllocator &all
 
     if (store->index() != index())
         return this;
 
     return foldsToStoredValue(alloc, store->value());
 }
 
 bool
-MGuardShapePolymorphic::congruentTo(const MDefinition *ins) const
-{
-    if (!ins->isGuardShapePolymorphic())
+MGuardReceiverPolymorphic::congruentTo(const MDefinition *ins) const
+{
+    if (!ins->isGuardReceiverPolymorphic())
         return false;
 
-    const MGuardShapePolymorphic *other = ins->toGuardShapePolymorphic();
+    const MGuardReceiverPolymorphic *other = ins->toGuardReceiverPolymorphic();
+
     if (numShapes() != other->numShapes())
         return false;
-
     for (size_t i = 0; i < numShapes(); i++) {
         if (getShape(i) != other->getShape(i))
             return false;
     }
 
+    if (numUnboxedGroups() != other->numUnboxedGroups())
+        return false;
+    for (size_t i = 0; i < numUnboxedGroups(); i++) {
+        if (getUnboxedGroup(i) != other->getUnboxedGroup(i))
+            return false;
+    }
+
     return congruentIfOperandsEqual(ins);
 }
 
 void
 InlinePropertyTable::trimTo(const ObjectVector &targets, const BoolVector &choiceSet)
 {
     for (size_t i = 0; i < targets.length(); i++) {
         // If the target was inlined, don't erase the entry.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -10047,52 +10047,65 @@ class MGuardShape
             return false;
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
-// Bail if the object's shape is not one of the shapes in shapes_.
-class MGuardShapePolymorphic
+// Bail if the object's shape or unboxed group is not in the input list.
+class MGuardReceiverPolymorphic
   : public MUnaryInstruction,
     public SingleObjectPolicy::Data
 {
     Vector<Shape *, 4, JitAllocPolicy> shapes_;
-
-    MGuardShapePolymorphic(TempAllocator &alloc, MDefinition *obj)
+    Vector<ObjectGroup *, 4, JitAllocPolicy> unboxedGroups_;
+
+    MGuardReceiverPolymorphic(TempAllocator &alloc, MDefinition *obj)
       : MUnaryInstruction(obj),
-        shapes_(alloc)
+        shapes_(alloc),
+        unboxedGroups_(alloc)
     {
         setGuard();
         setMovable();
         setResultType(MIRType_Object);
     }
 
   public:
-    INSTRUCTION_HEADER(GuardShapePolymorphic)
-
-    static MGuardShapePolymorphic *New(TempAllocator &alloc, MDefinition *obj) {
-        return new(alloc) MGuardShapePolymorphic(alloc, obj);
+    INSTRUCTION_HEADER(GuardReceiverPolymorphic)
+
+    static MGuardReceiverPolymorphic *New(TempAllocator &alloc, MDefinition *obj) {
+        return new(alloc) MGuardReceiverPolymorphic(alloc, obj);
     }
 
     MDefinition *obj() const {
         return getOperand(0);
     }
+
     bool addShape(Shape *shape) {
         return shapes_.append(shape);
     }
     size_t numShapes() const {
         return shapes_.length();
     }
     Shape *getShape(size_t i) const {
         return shapes_[i];
     }
 
+    bool addUnboxedGroup(ObjectGroup *group) {
+        return unboxedGroups_.append(group);
+    }
+    size_t numUnboxedGroups() const {
+        return unboxedGroups_.length();
+    }
+    ObjectGroup *getUnboxedGroup(size_t i) const {
+        return unboxedGroups_[i];
+    }
+
     bool congruentTo(const MDefinition *ins) const MOZ_OVERRIDE;
 
     AliasSet getAliasSet() const MOZ_OVERRIDE {
         return AliasSet::Load(AliasSet::ObjectFields);
     }
 };
 
 // Guard on an object's group, inclusively or exclusively.
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -155,17 +155,17 @@ namespace jit {
     _(PostWriteBarrier)                                                     \
     _(GetPropertyCache)                                                     \
     _(GetPropertyPolymorphic)                                               \
     _(SetPropertyPolymorphic)                                               \
     _(GetElementCache)                                                      \
     _(SetElementCache)                                                      \
     _(BindNameCache)                                                        \
     _(GuardShape)                                                           \
-    _(GuardShapePolymorphic)                                                \
+    _(GuardReceiverPolymorphic)                                             \
     _(GuardObjectGroup)                                                     \
     _(GuardObjectIdentity)                                                  \
     _(GuardClass)                                                           \
     _(ArrayLength)                                                          \
     _(SetArrayLength)                                                       \
     _(TypedArrayLength)                                                     \
     _(TypedArrayElements)                                                   \
     _(TypedObjectDescr)                                                     \
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -280,36 +280,44 @@ class MacroAssembler : public MacroAssem
     template <typename Source>
     void guardType(const Source &address, TypeSet::Type type, Register scratch, Label *miss);
 
     void guardTypeSetMightBeIncomplete(Register obj, Register scratch, Label *label);
 
     void loadObjShape(Register objReg, Register dest) {
         loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
     }
+    void loadObjGroup(Register objReg, Register dest) {
+        loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest);
+    }
     void loadBaseShape(Register objReg, Register dest) {
-        loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
-
+        loadObjShape(objReg, dest);
         loadPtr(Address(dest, Shape::offsetOfBase()), dest);
     }
     void loadObjClass(Register objReg, Register dest) {
-        loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest);
+        loadObjGroup(objReg, dest);
         loadPtr(Address(dest, ObjectGroup::offsetOfClasp()), dest);
     }
     void branchTestObjClass(Condition cond, Register obj, Register scratch, const js::Class *clasp,
                             Label *label) {
-        loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
+        loadObjGroup(obj, scratch);
         branchPtr(cond, Address(scratch, ObjectGroup::offsetOfClasp()), ImmPtr(clasp), label);
     }
     void branchTestObjShape(Condition cond, Register obj, const Shape *shape, Label *label) {
         branchPtr(cond, Address(obj, JSObject::offsetOfShape()), ImmGCPtr(shape), label);
     }
     void branchTestObjShape(Condition cond, Register obj, Register shape, Label *label) {
         branchPtr(cond, Address(obj, JSObject::offsetOfShape()), shape, label);
     }
+    void branchTestObjGroup(Condition cond, Register obj, ObjectGroup *group, Label *label) {
+        branchPtr(cond, Address(obj, JSObject::offsetOfGroup()), ImmGCPtr(group), label);
+    }
+    void branchTestObjGroup(Condition cond, Register obj, Register group, Label *label) {
+        branchPtr(cond, Address(obj, JSObject::offsetOfGroup()), group, label);
+    }
     void branchTestProxyHandlerFamily(Condition cond, Register proxy, Register scratch,
                                       const void *handlerp, Label *label) {
         Address handlerAddr(proxy, ProxyObject::offsetOfHandler());
         loadPtr(handlerAddr, scratch);
         Address familyAddr(scratch, BaseProxyHandler::offsetOfFamily());
         branchPtr(cond, familyAddr, ImmPtr(handlerp), label);
     }
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4107,39 +4107,16 @@ JSObject::addSizeOfExcludingThis(mozilla
     } else {
         // This must be the last case.
         info->objectsMallocHeapMisc +=
             js::SizeOfDataIfCDataObject(mallocSizeOf, const_cast<JSObject *>(this));
 #endif
     }
 }
 
-bool
-JSObject::hasIdempotentProtoChain() const
-{
-    // Return false if obj (or an object on its proto chain) is non-native or
-    // has a resolve or lookup hook.
-    JSObject *obj = const_cast<JSObject *>(this);
-    while (true) {
-        if (!obj->isNative())
-            return false;
-
-        JSResolveOp resolve = obj->getClass()->resolve;
-        if (resolve && resolve != js::fun_resolve && resolve != js::str_resolve)
-            return false;
-
-        if (obj->getOps()->lookupProperty)
-            return false;
-
-        obj = obj->getProto();
-        if (!obj)
-            return true;
-    }
-}
-
 void
 JSObject::markChildren(JSTracer *trc)
 {
     MarkObjectGroup(trc, &group_, "group");
 
     MarkShape(trc, &shape_, "shape");
 
     const Class *clasp = group_->clasp();
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -308,18 +308,16 @@ class JSObject : public js::gc::Cell
 
     size_t tenuredSizeOfThis() const {
         MOZ_ASSERT(isTenured());
         return js::gc::Arena::thingSize(asTenured().getAllocKind());
     }
 
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info);
 
-    bool hasIdempotentProtoChain() const;
-
     /*
      * Marks this object as having a singleton type, and leave the group lazy.
      * Constructs a new, unique shape for the object.
      */
     static inline bool setSingleton(js::ExclusiveContext *cx, js::HandleObject obj);
 
     inline js::ObjectGroup* getGroup(JSContext *cx);