LCOV - code coverage report
Current view: top level - js/src/jit - CacheIR.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 773 2191 35.3 %
Date: 2018-08-07 16:35:00 Functions: 0 0 -
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "jit/CacheIR.h"
       8             : 
       9             : #include "mozilla/DebugOnly.h"
      10             : #include "mozilla/FloatingPoint.h"
      11             : 
      12             : #include "jit/BaselineCacheIRCompiler.h"
      13             : #include "jit/BaselineIC.h"
      14             : #include "jit/CacheIRSpewer.h"
      15             : #include "vm/SelfHosting.h"
      16             : 
      17             : #include "jit/MacroAssembler-inl.h"
      18             : #include "vm/EnvironmentObject-inl.h"
      19             : #include "vm/JSContext-inl.h"
      20             : #include "vm/JSObject-inl.h"
      21             : #include "vm/UnboxedObject-inl.h"
      22             : 
      23             : using namespace js;
      24             : using namespace js::jit;
      25             : 
      26             : using mozilla::DebugOnly;
      27             : using mozilla::Maybe;
      28             : 
      29             : const char* js::jit::CacheKindNames[] = {
      30             : #define DEFINE_KIND(kind) #kind,
      31             :     CACHE_IR_KINDS(DEFINE_KIND)
      32             : #undef DEFINE_KIND
      33             : };
      34             : 
      35             : void
      36           0 : CacheIRWriter::assertSameCompartment(JSObject* obj) {
      37           0 :     assertSameCompartmentDebugOnly(cx_, obj);
      38           0 : }
      39             : 
      40           0 : IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
      41           0 :                          ICState::Mode mode)
      42             :   : writer(cx),
      43             :     cx_(cx),
      44             :     script_(script),
      45             :     pc_(pc),
      46             :     cacheKind_(cacheKind),
      47           0 :     mode_(mode)
      48           0 : {}
      49             : 
      50           0 : GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
      51             :                                        CacheKind cacheKind, ICState::Mode mode,
      52             :                                        bool* isTemporarilyUnoptimizable, HandleValue val,
      53             :                                        HandleValue idVal, HandleValue receiver,
      54           0 :                                        GetPropertyResultFlags resultFlags)
      55             :   : IRGenerator(cx, script, pc, cacheKind, mode),
      56             :     val_(val),
      57             :     idVal_(idVal),
      58             :     receiver_(receiver),
      59             :     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
      60             :     resultFlags_(resultFlags),
      61           0 :     preliminaryObjectAction_(PreliminaryObjectAction::None)
      62           0 : {}
      63             : 
      64             : static void
      65           0 : EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
      66             :                    Shape* shape)
      67             : {
      68           0 :     if (holder->isFixedSlot(shape->slot())) {
      69           0 :         writer.loadFixedSlotResult(holderOp, NativeObject::getFixedSlotOffset(shape->slot()));
      70             :     } else {
      71           0 :         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
      72           0 :         writer.loadDynamicSlotResult(holderOp, dynamicSlotOffset);
      73             :     }
      74           0 : }
      75             : 
      76             : // DOM proxies
      77             : // -----------
      78             : //
      79             : // DOM proxies are proxies that are used to implement various DOM objects like
      80             : // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
      81             : // object that stores extra properties added to the object. The following
      82             : // CacheIR instructions are only used with DOM proxies:
      83             : //
      84             : // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
      85             : //   returns either an UndefinedValue (no expando), ObjectValue (the expando
      86             : //   object), or PrivateValue(ExpandoAndGeneration*).
      87             : //
      88             : // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
      89             : //   slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
      90             : //   generation, then returns expandoAndGeneration->expando. This Value is
      91             : //   either an UndefinedValue or ObjectValue.
      92             : //
      93             : // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
      94             : //   expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
      95             : //   returns the expandoAndGeneration->expando Value.
      96             : //
      97             : // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
      98             : //   guards it's either UndefinedValue or an object with the expected shape.
      99             : 
     100             : enum class ProxyStubType {
     101             :     None,
     102             :     DOMExpando,
     103             :     DOMShadowed,
     104             :     DOMUnshadowed,
     105             :     Generic
     106             : };
     107             : 
     108             : static ProxyStubType
     109           0 : GetProxyStubType(JSContext* cx, HandleObject obj, HandleId id)
     110             : {
     111           0 :     if (!obj->is<ProxyObject>())
     112             :         return ProxyStubType::None;
     113             : 
     114           0 :     if (!IsCacheableDOMProxy(obj))
     115             :         return ProxyStubType::Generic;
     116             : 
     117           0 :     DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
     118           0 :     if (shadows == ShadowCheckFailed) {
     119           0 :         cx->clearPendingException();
     120           0 :         return ProxyStubType::None;
     121             :     }
     122             : 
     123           0 :     if (DOMProxyIsShadowing(shadows)) {
     124           0 :         if (shadows == ShadowsViaDirectExpando || shadows == ShadowsViaIndirectExpando)
     125             :             return ProxyStubType::DOMExpando;
     126           0 :         return ProxyStubType::DOMShadowed;
     127             :     }
     128             : 
     129           0 :     MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
     130             :     return ProxyStubType::DOMUnshadowed;
     131             : }
     132             : 
     133             : static bool
     134           0 : ValueToNameOrSymbolId(JSContext* cx, HandleValue idval, MutableHandleId id,
     135             :                       bool* nameOrSymbol)
     136             : {
     137           0 :     *nameOrSymbol = false;
     138             : 
     139           0 :     if (!idval.isString() && !idval.isSymbol())
     140             :         return true;
     141             : 
     142           0 :     if (!ValueToId<CanGC>(cx, idval, id))
     143             :         return false;
     144             : 
     145           0 :     if (!JSID_IS_STRING(id) && !JSID_IS_SYMBOL(id)) {
     146           0 :         id.set(JSID_VOID);
     147           0 :         return true;
     148             :     }
     149             : 
     150             :     uint32_t dummy;
     151           0 :     if (JSID_IS_STRING(id) && JSID_TO_ATOM(id)->isIndex(&dummy)) {
     152           0 :         id.set(JSID_VOID);
     153           0 :         return true;
     154             :     }
     155             : 
     156           0 :     *nameOrSymbol = true;
     157           0 :     return true;
     158             : }
     159             : 
     160             : bool
     161           0 : GetPropIRGenerator::tryAttachStub()
     162             : {
     163             :     // Idempotent ICs should call tryAttachIdempotentStub instead.
     164           0 :     MOZ_ASSERT(!idempotent());
     165             : 
     166           0 :     AutoAssertNoPendingException aanpe(cx_);
     167             : 
     168             :     // Non-object receivers are a degenerate case, so don't try to attach
     169             :     // stubs. The stubs we do emit will still perform runtime checks and
     170             :     // fallback as needed.
     171           0 :     if (isSuper() && !receiver_.isObject())
     172             :         return false;
     173             : 
     174           0 :     ValOperandId valId(writer.setInputOperandId(0));
     175           0 :     if (cacheKind_ != CacheKind::GetProp) {
     176           0 :         MOZ_ASSERT_IF(cacheKind_ == CacheKind::GetPropSuper, getSuperReceiverValueId().id() == 1);
     177           0 :         MOZ_ASSERT_IF(cacheKind_ != CacheKind::GetPropSuper, getElemKeyValueId().id() == 1);
     178           0 :         writer.setInputOperandId(1);
     179             :     }
     180           0 :     if (cacheKind_ == CacheKind::GetElemSuper) {
     181           0 :         MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
     182           0 :         writer.setInputOperandId(2);
     183             :     }
     184             : 
     185           0 :     RootedId id(cx_);
     186             :     bool nameOrSymbol;
     187           0 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
     188           0 :         cx_->clearPendingException();
     189           0 :         return false;
     190             :     }
     191             : 
     192           0 :     if (val_.isObject()) {
     193           0 :         RootedObject obj(cx_, &val_.toObject());
     194           0 :         ObjOperandId objId = writer.guardIsObject(valId);
     195           0 :         if (nameOrSymbol) {
     196           0 :             if (tryAttachObjectLength(obj, objId, id))
     197             :                 return true;
     198           0 :             if (tryAttachNative(obj, objId, id))
     199             :                 return true;
     200           0 :             if (tryAttachUnboxed(obj, objId, id))
     201             :                 return true;
     202           0 :             if (tryAttachUnboxedExpando(obj, objId, id))
     203             :                 return true;
     204           0 :             if (tryAttachTypedObject(obj, objId, id))
     205             :                 return true;
     206           0 :             if (tryAttachModuleNamespace(obj, objId, id))
     207             :                 return true;
     208           0 :             if (tryAttachWindowProxy(obj, objId, id))
     209             :                 return true;
     210           0 :             if (tryAttachCrossCompartmentWrapper(obj, objId, id))
     211             :                 return true;
     212           0 :             if (tryAttachXrayCrossCompartmentWrapper(obj, objId, id))
     213             :                 return true;
     214           0 :             if (tryAttachFunction(obj, objId, id))
     215             :                 return true;
     216           0 :             if (tryAttachProxy(obj, objId, id))
     217             :                 return true;
     218             : 
     219           0 :             trackAttached(IRGenerator::NotAttached);
     220           0 :             return false;
     221             :         }
     222             : 
     223           0 :         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
     224             : 
     225           0 :         if (tryAttachProxyElement(obj, objId))
     226             :             return true;
     227             : 
     228             :         uint32_t index;
     229           0 :         Int32OperandId indexId;
     230           0 :         if (maybeGuardInt32Index(idVal_, getElemKeyValueId(), &index, &indexId)) {
     231           0 :             if (tryAttachTypedElement(obj, objId, index, indexId))
     232             :                 return true;
     233           0 :             if (tryAttachDenseElement(obj, objId, index, indexId))
     234             :                 return true;
     235           0 :             if (tryAttachDenseElementHole(obj, objId, index, indexId))
     236             :                 return true;
     237           0 :             if (tryAttachUnboxedElementHole(obj, objId, index, indexId))
     238             :                 return true;
     239           0 :             if (tryAttachArgumentsObjectArg(obj, objId, indexId))
     240             :                 return true;
     241             : 
     242           0 :             trackAttached(IRGenerator::NotAttached);
     243           0 :             return false;
     244             :         }
     245             : 
     246           0 :         trackAttached(IRGenerator::NotAttached);
     247           0 :         return false;
     248             :     }
     249             : 
     250           0 :     if (nameOrSymbol) {
     251           0 :         if (tryAttachPrimitive(valId, id))
     252             :             return true;
     253           0 :         if (tryAttachStringLength(valId, id))
     254             :             return true;
     255           0 :         if (tryAttachMagicArgumentsName(valId, id))
     256             :             return true;
     257             : 
     258           0 :         trackAttached(IRGenerator::NotAttached);
     259           0 :         return false;
     260             :     }
     261             : 
     262           0 :     if (idVal_.isInt32()) {
     263           0 :         ValOperandId indexId = getElemKeyValueId();
     264           0 :         if (tryAttachStringChar(valId, indexId))
     265             :             return true;
     266           0 :         if (tryAttachMagicArgument(valId, indexId))
     267             :             return true;
     268             : 
     269           0 :         trackAttached(IRGenerator::NotAttached);
     270           0 :         return false;
     271             :     }
     272             : 
     273           0 :     trackAttached(IRGenerator::NotAttached);
     274           0 :     return false;
     275             : }
     276             : 
     277             : bool
     278           0 : GetPropIRGenerator::tryAttachIdempotentStub()
     279             : {
     280             :     // For idempotent ICs, only attach stubs which we can be sure have no side
     281             :     // effects and produce a result which the MIR in the calling code is able
     282             :     // to handle, since we do not have a pc to explicitly monitor the result.
     283             : 
     284           0 :     MOZ_ASSERT(idempotent());
     285             : 
     286           0 :     RootedObject obj(cx_, &val_.toObject());
     287           0 :     RootedId id(cx_, NameToId(idVal_.toString()->asAtom().asPropertyName()));
     288             : 
     289           0 :     ValOperandId valId(writer.setInputOperandId(0));
     290           0 :     ObjOperandId objId = writer.guardIsObject(valId);
     291           0 :     if (tryAttachNative(obj, objId, id))
     292             :         return true;
     293             : 
     294             :     // Object lengths are supported only if int32 results are allowed.
     295           0 :     if (tryAttachObjectLength(obj, objId, id))
     296             :         return true;
     297             : 
     298             :     // Also support native data properties on DOMProxy prototypes.
     299           0 :     if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed)
     300           0 :         return tryAttachDOMProxyUnshadowed(obj, objId, id);
     301             : 
     302             :     return false;
     303             : }
     304             : 
     305             : static bool
     306           0 : IsCacheableProtoChain(JSObject* obj, JSObject* holder)
     307             : {
     308           0 :     while (obj != holder) {
     309             :         /*
     310             :          * We cannot assume that we find the holder object on the prototype
     311             :          * chain and must check for null proto. The prototype chain can be
     312             :          * altered during the lookupProperty call.
     313             :          */
     314           0 :         JSObject* proto = obj->staticPrototype();
     315           0 :         if (!proto || !proto->isNative())
     316             :             return false;
     317             :         obj = proto;
     318             :     }
     319             :     return true;
     320             : }
     321             : 
     322             : static bool
     323           0 : IsCacheableGetPropReadSlot(JSObject* obj, JSObject* holder, PropertyResult prop)
     324             : {
     325           0 :     if (!prop || !IsCacheableProtoChain(obj, holder))
     326             :         return false;
     327             : 
     328           0 :     Shape* shape = prop.shape();
     329           0 :     if (!shape->isDataProperty())
     330             :         return false;
     331             : 
     332           0 :     return true;
     333             : }
     334             : 
     335             : static bool
     336           0 : IsCacheableGetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
     337             : {
     338           0 :     if (!shape || !IsCacheableProtoChain(obj, holder))
     339             :         return false;
     340             : 
     341           0 :     if (!shape->hasGetterValue() || !shape->getterValue().isObject())
     342             :         return false;
     343             : 
     344           0 :     if (!shape->getterValue().toObject().is<JSFunction>())
     345             :         return false;
     346             : 
     347           0 :     JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
     348           0 :     if (!getter.isNativeWithCppEntry())
     349             :         return false;
     350             : 
     351           0 :     if (getter.isClassConstructor())
     352             :         return false;
     353             : 
     354             :     // Check for a getter that has jitinfo and whose jitinfo says it's
     355             :     // OK with both inner and outer objects.
     356           0 :     if (getter.hasJitInfo() && !getter.jitInfo()->needsOuterizedThisObject())
     357             :         return true;
     358             : 
     359             :     // For getters that need the WindowProxy (instead of the Window) as this
     360             :     // object, don't cache if obj is the Window, since our cache will pass that
     361             :     // instead of the WindowProxy.
     362           0 :     return !IsWindow(obj);
     363             : }
     364             : 
     365             : static bool
     366           0 : IsCacheableGetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
     367             :                                bool* isTemporarilyUnoptimizable = nullptr)
     368             : {
     369           0 :     if (!shape || !IsCacheableProtoChain(obj, holder))
     370             :         return false;
     371             : 
     372           0 :     if (!shape->hasGetterValue() || !shape->getterValue().isObject())
     373             :         return false;
     374             : 
     375           0 :     if (!shape->getterValue().toObject().is<JSFunction>())
     376             :         return false;
     377             : 
     378             :     // See IsCacheableGetPropCallNative.
     379           0 :     if (IsWindow(obj))
     380             :         return false;
     381             : 
     382           0 :     JSFunction& getter = shape->getterValue().toObject().as<JSFunction>();
     383           0 :     if (getter.isNativeWithCppEntry())
     384             :         return false;
     385             : 
     386             :     // Natives with jit entry can use the scripted path.
     387           0 :     if (getter.isNativeWithJitEntry())
     388             :         return true;
     389             : 
     390           0 :     if (!getter.hasScript()) {
     391           0 :         if (isTemporarilyUnoptimizable)
     392           0 :             *isTemporarilyUnoptimizable = true;
     393             :         return false;
     394             :     }
     395             : 
     396           0 :     if (getter.isClassConstructor())
     397             :         return false;
     398             : 
     399           0 :     return true;
     400             : }
     401             : 
     402             : static bool
     403           0 : CheckHasNoSuchOwnProperty(JSContext* cx, JSObject* obj, jsid id)
     404             : {
     405           0 :     if (obj->isNative()) {
     406             :         // Don't handle proto chains with resolve hooks.
     407           0 :         if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj))
     408             :             return false;
     409           0 :         if (obj->as<NativeObject>().contains(cx, id))
     410             :             return false;
     411           0 :     } else if (obj->is<UnboxedPlainObject>()) {
     412           0 :         if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx, id))
     413             :             return false;
     414           0 :     } else if (obj->is<TypedObject>()) {
     415           0 :         if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id))
     416             :             return false;
     417             :     } else {
     418             :         return false;
     419             :     }
     420             : 
     421             :     return true;
     422             : }
     423             : 
     424             : static bool
     425           0 : CheckHasNoSuchProperty(JSContext* cx, JSObject* obj, jsid id)
     426             : {
     427           0 :     JSObject* curObj = obj;
     428             :     do {
     429           0 :         if (!CheckHasNoSuchOwnProperty(cx, curObj, id))
     430             :             return false;
     431             : 
     432           0 :         if (!curObj->isNative()) {
     433             :             // Non-native objects are only handled as the original receiver.
     434           0 :             if (curObj != obj)
     435             :                 return false;
     436             :         }
     437             : 
     438           0 :         curObj = curObj->staticPrototype();
     439           0 :     } while (curObj);
     440             : 
     441             :     return true;
     442             : }
     443             : 
     444             : // Return whether obj is in some PreliminaryObjectArray and has a structure
     445             : // that might change in the future.
     446             : static bool
     447           0 : IsPreliminaryObject(JSObject* obj)
     448             : {
     449           0 :     if (obj->isSingleton())
     450             :         return false;
     451             : 
     452           0 :     AutoSweepObjectGroup sweep(obj->group());
     453           0 :     TypeNewScript* newScript = obj->group()->newScript(sweep);
     454           0 :     if (newScript && !newScript->analyzed())
     455             :         return true;
     456             : 
     457           0 :     if (obj->group()->maybePreliminaryObjects(sweep))
     458             :         return true;
     459             : 
     460           0 :     return false;
     461             : }
     462             : 
     463             : static bool
     464           0 : IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
     465             :                       jsbytecode* pc, GetPropertyResultFlags resultFlags)
     466             : {
     467           0 :     if (shape)
     468             :         return false;
     469             : 
     470           0 :     MOZ_ASSERT(!holder);
     471             : 
     472             :     // Idempotent ICs may only attach missing-property stubs if undefined
     473             :     // results are explicitly allowed, since no monitoring is done of the
     474             :     // cache result.
     475           0 :     if (!pc && !(resultFlags & GetPropertyResultFlags::AllowUndefined))
     476             :         return false;
     477             : 
     478             :     // If we're doing a name lookup, we have to throw a ReferenceError. If
     479             :     // extra warnings are enabled, we may have to report a warning.
     480             :     // Note that Ion does not generate idempotent caches for JSOP_GETBOUNDNAME.
     481           0 :     if ((pc && *pc == JSOP_GETBOUNDNAME) || cx->realm()->behaviors().extraWarnings(cx))
     482             :         return false;
     483             : 
     484           0 :     return CheckHasNoSuchProperty(cx, obj, id);
     485             : }
     486             : 
     487             : enum NativeGetPropCacheability {
     488             :     CanAttachNone,
     489             :     CanAttachReadSlot,
     490             :     CanAttachCallGetter,
     491             : };
     492             : 
     493             : static NativeGetPropCacheability
     494           0 : CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id,
     495             :                        MutableHandleNativeObject holder, MutableHandleShape shape,
     496             :                        jsbytecode* pc, GetPropertyResultFlags resultFlags,
     497             :                        bool* isTemporarilyUnoptimizable)
     498             : {
     499           0 :     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
     500             : 
     501             :     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     502             :     // of turn. We don't mind doing this even when purity isn't required, because we
     503             :     // only miss out on shape hashification, which is only a temporary perf cost.
     504             :     // The limits were arbitrarily set, anyways.
     505           0 :     JSObject* baseHolder = nullptr;
     506           0 :     PropertyResult prop;
     507           0 :     if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop))
     508             :         return CanAttachNone;
     509             : 
     510           0 :     MOZ_ASSERT(!holder);
     511           0 :     if (baseHolder) {
     512           0 :         if (!baseHolder->isNative())
     513             :             return CanAttachNone;
     514           0 :         holder.set(&baseHolder->as<NativeObject>());
     515             :     }
     516           0 :     shape.set(prop.maybeShape());
     517             : 
     518           0 :     if (IsCacheableGetPropReadSlot(obj, holder, prop))
     519             :         return CanAttachReadSlot;
     520             : 
     521           0 :     if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc, resultFlags))
     522             :         return CanAttachReadSlot;
     523             : 
     524             :     // Idempotent ICs cannot call getters, see tryAttachIdempotentStub.
     525           0 :     if (pc && (resultFlags & GetPropertyResultFlags::Monitored)) {
     526           0 :         if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
     527             :             return CanAttachCallGetter;
     528             : 
     529           0 :         if (IsCacheableGetPropCallNative(obj, holder, shape))
     530             :             return CanAttachCallGetter;
     531             :     }
     532             : 
     533             :     return CanAttachNone;
     534             : }
     535             : 
     536             : static void
     537           0 : GuardGroupProto(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
     538             : {
     539             :     // Uses the group to determine if the prototype is unchanged. If the
     540             :     // group's prototype is mutable, we must check the actual prototype,
     541             :     // otherwise checking the group is sufficient. This can be used if object
     542             :     // is not ShapedObject or if Shape has UNCACHEABLE_PROTO flag set.
     543             : 
     544           0 :     ObjectGroup* group = obj->groupRaw();
     545             : 
     546           0 :     if (group->hasUncacheableProto())
     547           0 :         writer.guardProto(objId, obj->staticPrototype());
     548             :     else
     549           0 :         writer.guardGroupForProto(objId, group);
     550           0 : }
     551             : 
     552             : // Guard that a given object has same class and same OwnProperties (excluding
     553             : // dense elements and dynamic properties). Returns an OperandId for the unboxed
     554             : // expando if it exists.
     555             : static void
     556           0 : TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId,
     557             :                      Maybe<ObjOperandId>* expandoId)
     558             : {
     559           0 :     if (obj->is<UnboxedPlainObject>()) {
     560           0 :         writer.guardGroupForLayout(objId, obj->group());
     561             : 
     562           0 :         if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
     563           0 :             expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
     564           0 :             writer.guardShapeForOwnProperties(expandoId->ref(), expando->lastProperty());
     565             :         } else {
     566           0 :             writer.guardNoUnboxedExpando(objId);
     567             :         }
     568           0 :     } else if (obj->is<TypedObject>()) {
     569           0 :         writer.guardGroupForLayout(objId, obj->group());
     570           0 :     } else if (obj->is<ProxyObject>()) {
     571           0 :         writer.guardShapeForClass(objId, obj->as<ProxyObject>().shape());
     572             :     } else {
     573           0 :         MOZ_ASSERT(obj->is<NativeObject>());
     574           0 :         writer.guardShapeForOwnProperties(objId, obj->as<NativeObject>().lastProperty());
     575             :     }
     576           0 : }
     577             : 
     578             : // Similar to |TestMatchingReceiver|, but specialized for NativeObject.
     579             : static void
     580             : TestMatchingNativeReceiver(CacheIRWriter& writer, NativeObject* obj, ObjOperandId objId)
     581             : {
     582           0 :     writer.guardShapeForOwnProperties(objId, obj->lastProperty());
     583             : }
     584             : 
     585             : // Similar to |TestMatchingReceiver|, but specialized for ProxyObject.
     586             : static void
     587             : TestMatchingProxyReceiver(CacheIRWriter& writer, ProxyObject* obj, ObjOperandId objId)
     588             : {
     589           0 :     writer.guardShapeForClass(objId, obj->shape());
     590             : }
     591             : 
     592             : // Adds additional guards if TestMatchingReceiver* does not also imply the
     593             : // prototype.
     594             : static void
     595           0 : GeneratePrototypeGuardsForReceiver(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
     596             : {
     597             :     // If receiver was marked UNCACHEABLE_PROTO, the previous shape guard
     598             :     // doesn't ensure the prototype is unchanged. In this case we must use the
     599             :     // group to check the prototype.
     600           0 :     if (obj->hasUncacheableProto()) {
     601           0 :         MOZ_ASSERT(obj->is<NativeObject>());
     602           0 :         GuardGroupProto(writer, obj, objId);
     603             :     }
     604             : 
     605             : #ifdef DEBUG
     606             :     // The following cases already guaranteed the prototype is unchanged.
     607           0 :     if (obj->is<UnboxedPlainObject>())
     608           0 :         MOZ_ASSERT(!obj->group()->hasUncacheableProto());
     609           0 :     else if (obj->is<TypedObject>())
     610           0 :         MOZ_ASSERT(!obj->group()->hasUncacheableProto());
     611           0 :     else if (obj->is<ProxyObject>())
     612           0 :         MOZ_ASSERT(!obj->hasUncacheableProto());
     613             : #endif // DEBUG
     614           0 : }
     615             : 
     616             : static void
     617           0 : GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId)
     618             : {
     619             :     // Assuming target property is on |holder|, generate appropriate guards to
     620             :     // ensure |holder| is still on the prototype chain of |obj| and we haven't
     621             :     // introduced any shadowing definitions.
     622             :     //
     623             :     // For each item in the proto chain before holder, we must ensure that
     624             :     // [[GetPrototypeOf]] still has the expected result, and that
     625             :     // [[GetOwnProperty]] has no definition of the target property.
     626             :     //
     627             :     //
     628             :     // Shape Teleporting Optimization
     629             :     // ------------------------------
     630             :     //
     631             :     // Starting with the assumption (and guideline to developers) that mutating
     632             :     // prototypes is an uncommon and fair-to-penalize operation we move cost
     633             :     // from the access side to the mutation side.
     634             :     //
     635             :     // Consider the following proto chain, with B defining a property 'x':
     636             :     //
     637             :     //      D  ->  C  ->  B{x: 3}  ->  A  -> null
     638             :     //
     639             :     // When accessing |D.x| we refer to D as the "receiver", and B as the
     640             :     // "holder". To optimize this access we need to ensure that neither D nor C
     641             :     // has since defined a shadowing property 'x'. Since C is a prototype that
     642             :     // we assume is rarely mutated we would like to avoid checking each time if
     643             :     // new properties are added. To do this we require that everytime C is
     644             :     // mutated that in addition to generating a new shape for itself, it will
     645             :     // walk the proto chain and generate new shapes for those objects on the
     646             :     // chain (B and A). As a result, checking the shape of D and B is
     647             :     // sufficient. Note that we do not care if the shape or properties of A
     648             :     // change since the lookup of 'x' will stop at B.
     649             :     //
     650             :     // The second condition we must verify is that the prototype chain was not
     651             :     // mutated. The same mechanism as above is used. When the prototype link is
     652             :     // changed, we generate a new shape for the object. If the object whose
     653             :     // link we are mutating is itself a prototype, we regenerate shapes down
     654             :     // the chain. This means the same two shape checks as above are sufficient.
     655             :     //
     656             :     // Unfortunately we don't stop there and add further caveats. We may set
     657             :     // the UNCACHEABLE_PROTO flag on the shape of an object to indicate that it
     658             :     // will not generate a new shape if its prototype link is modified. If the
     659             :     // object is itself a prototype we follow the shape chain and regenerate
     660             :     // shapes (if they aren't themselves uncacheable).
     661             :     //
     662             :     // Let's consider the effect of the UNCACHEABLE_PROTO flag on our example:
     663             :     // - D is uncacheable: Add check that D still links to C
     664             :     // - C is uncacheable: Modifying C.__proto__ will still reshape B (if B is
     665             :     //                     not uncacheable)
     666             :     // - B is uncacheable: Add shape check C since B will not reshape OR check
     667             :     //                     proto of D and C
     668             :     //
     669             :     // See:
     670             :     //  - ReshapeForProtoMutation
     671             :     //  - ReshapeForShadowedProp
     672             : 
     673           0 :     MOZ_ASSERT(holder);
     674           0 :     MOZ_ASSERT(obj != holder);
     675             : 
     676             :     // Only DELEGATE objects participate in teleporting so peel off the first
     677             :     // object in the chain if needed and handle it directly.
     678           0 :     JSObject* pobj = obj;
     679           0 :     if (!obj->isDelegate()) {
     680             :         // TestMatchingReceiver does not always ensure the prototype is
     681             :         // unchanged, so generate extra guards as needed.
     682           0 :         GeneratePrototypeGuardsForReceiver(writer, obj, objId);
     683             : 
     684           0 :         pobj = obj->staticPrototype();
     685             :     }
     686           0 :     MOZ_ASSERT(pobj->isDelegate());
     687             : 
     688             :     // In the common case, holder has a cacheable prototype and will regenerate
     689             :     // its shape if any (delegate) objects in the proto chain are updated.
     690           0 :     if (!holder->hasUncacheableProto())
     691           0 :         return;
     692             : 
     693             :     // If already at the holder, no further proto checks are needed.
     694           0 :     if (pobj == holder)
     695             :         return;
     696             : 
     697             :     // NOTE: We could be clever and look for a middle prototype to shape check
     698             :     //       and elide some (but not all) of the group checks. Unless we have
     699             :     //       real-world examples, let's avoid the complexity.
     700             : 
     701             :     // Synchronize pobj and protoId.
     702           0 :     MOZ_ASSERT(pobj == obj || pobj == obj->staticPrototype());
     703             :     ObjOperandId protoId = (pobj == obj) ? objId
     704           0 :                                          : writer.loadProto(objId);
     705             : 
     706             :     // Guard prototype links from |pobj| to |holder|.
     707           0 :     while (pobj != holder) {
     708           0 :         pobj = pobj->staticPrototype();
     709           0 :         protoId = writer.loadProto(protoId);
     710             : 
     711           0 :         writer.guardSpecificObject(protoId, pobj);
     712             :     }
     713             : }
     714             : 
     715             : static void
     716           0 : GeneratePrototypeHoleGuards(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
     717             : {
     718           0 :     if (obj->hasUncacheableProto())
     719           0 :         GuardGroupProto(writer, obj, objId);
     720             : 
     721           0 :     JSObject* pobj = obj->staticPrototype();
     722           0 :     while (pobj) {
     723           0 :         ObjOperandId protoId = writer.loadObject(pobj);
     724             : 
     725             :         // If shape doesn't imply proto, additional guards are needed.
     726           0 :         if (pobj->hasUncacheableProto())
     727           0 :             GuardGroupProto(writer, pobj, protoId);
     728             : 
     729             :         // Make sure the shape matches, to avoid non-dense elements or anything
     730             :         // else that is being checked by CanAttachDenseElementHole.
     731           0 :         writer.guardShape(protoId, pobj->as<NativeObject>().lastProperty());
     732             : 
     733             :         // Also make sure there are no dense elements.
     734           0 :         writer.guardNoDenseElements(protoId);
     735             : 
     736           0 :         pobj = pobj->staticPrototype();
     737             :     }
     738           0 : }
     739             : 
     740             : // Similar to |TestMatchingReceiver|, but for the holder object (when it
     741             : // differs from the receiver). The holder may also be the expando of the
     742             : // receiver if it exists.
     743             : static void
     744           0 : TestMatchingHolder(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
     745             : {
     746             :     // The GeneratePrototypeGuards + TestMatchingHolder checks only support
     747             :     // prototype chains composed of NativeObject (excluding the receiver
     748             :     // itself).
     749           0 :     MOZ_ASSERT(obj->is<NativeObject>());
     750             : 
     751           0 :     writer.guardShapeForOwnProperties(objId, obj->as<NativeObject>().lastProperty());
     752           0 : }
     753             : 
     754             : static bool
     755           0 : UncacheableProtoOnChain(JSObject* obj)
     756             : {
     757             :     while (true) {
     758           0 :         if (obj->hasUncacheableProto())
     759             :             return true;
     760             : 
     761           0 :         obj = obj->staticPrototype();
     762           0 :         if (!obj)
     763             :             return false;
     764             :     }
     765             : }
     766             : 
     767             : static void
     768           0 : ShapeGuardProtoChain(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
     769             : {
     770             :     while (true) {
     771             :         // Guard on the proto if the shape does not imply the proto.
     772           0 :         bool guardProto = obj->hasUncacheableProto();
     773             : 
     774           0 :         obj = obj->staticPrototype();
     775           0 :         if (!obj && !guardProto)
     776             :             return;
     777             : 
     778           0 :         objId = writer.loadProto(objId);
     779             : 
     780           0 :         if (guardProto)
     781           0 :             writer.guardSpecificObject(objId, obj);
     782             : 
     783        2786 :         if (!obj)
     784             :             return;
     785             : 
     786        5572 :         writer.guardShape(objId, obj->as<NativeObject>().shape());
     787        2786 :     }
     788             : }
     789             : 
     790             : // For cross compartment guards we shape-guard the prototype chain to avoid
     791             : // referencing the holder object.
     792             : //
     793             : // This peels off the first layer because it's guarded against obj == holder.
     794             : static void
     795           0 : ShapeGuardProtoChainForCrossCompartmentHolder(CacheIRWriter& writer, JSObject* obj,
     796             :                                               ObjOperandId objId, JSObject* holder,
     797             :                                               Maybe<ObjOperandId>* holderId)
     798             : {
     799         115 :     MOZ_ASSERT(obj != holder);
     800           0 :     MOZ_ASSERT(holder);
     801             :     while (true) {
     802           0 :         obj = obj->staticPrototype();
     803           0 :         MOZ_ASSERT(obj);
     804             : 
     805         160 :         objId = writer.loadProto(objId);
     806           0 :         if (obj == holder) {
     807         115 :             TestMatchingHolder(writer, obj, objId);
     808         115 :             holderId->emplace(objId);
     809         115 :             return;
     810             :         } else {
     811          90 :             writer.guardShapeForOwnProperties(objId, obj->as<NativeObject>().shape());
     812             :         }
     813             :     }
     814             : }
     815             : 
     816             : enum class SlotReadType {
     817             :     Normal,
     818             :     CrossCompartment
     819             : };
     820             : 
     821             : template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
     822             : static void
     823        6341 : EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     824             :                   ObjOperandId objId, Maybe<ObjOperandId>* holderId)
     825             : {
     826       12682 :     Maybe<ObjOperandId> expandoId;
     827        6341 :     TestMatchingReceiver(writer, obj, objId, &expandoId);
     828             : 
     829        6341 :     if (obj != holder) {
     830           0 :         if (holder) {
     831             :             if (MaybeCrossCompartment == SlotReadType::CrossCompartment) {
     832             :                 // Guard proto chain integrity.
     833             :                 // We use a variant of guards that avoid baking in any cross-compartment
     834             :                 // object pointers.
     835         115 :                 ShapeGuardProtoChainForCrossCompartmentHolder(writer, obj, objId, holder,
     836             :                                                               holderId);
     837             :             } else {
     838             :                 // Guard proto chain integrity.
     839        1374 :                 GeneratePrototypeGuards(writer, obj, holder, objId);
     840             : 
     841             :                 // Guard on the holder's shape.
     842        1374 :                 holderId->emplace(writer.loadObject(holder));
     843        1374 :                 TestMatchingHolder(writer, holder, holderId->ref());
     844             :             }
     845             :         } else {
     846             :             // The property does not exist. Guard on everything in the prototype
     847             :             // chain. This is guaranteed to see only Native objects because of
     848             :             // CanAttachNativeGetProp().
     849           0 :             ShapeGuardProtoChain(writer, obj, objId);
     850             :         }
     851           0 :     } else if (obj->is<UnboxedPlainObject>()) {
     852           1 :         holderId->emplace(*expandoId);
     853             :     } else {
     854        4012 :         holderId->emplace(objId);
     855             :     }
     856        6341 : }
     857             : 
     858             : template <SlotReadType MaybeCrossCompartment = SlotReadType::Normal>
     859             : static void
     860        5891 : EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     861             :                    Shape* shape, ObjOperandId objId)
     862             : {
     863       11782 :     Maybe<ObjOperandId> holderId;
     864        5891 :     EmitReadSlotGuard<MaybeCrossCompartment>(writer, obj, holder, objId, &holderId);
     865             : 
     866           0 :     if (obj == holder && obj->is<UnboxedPlainObject>())
     867           0 :         holder = obj->as<UnboxedPlainObject>().maybeExpando();
     868             : 
     869             :     // Slot access.
     870        5891 :     if (holder) {
     871       10656 :         MOZ_ASSERT(holderId->valid());
     872           0 :         EmitLoadSlotResult(writer, *holderId, &holder->as<NativeObject>(), shape);
     873             :     } else {
     874         563 :         MOZ_ASSERT(holderId.isNothing());
     875             :         writer.loadUndefinedResult();
     876             :     }
     877        5891 : }
     878             : 
     879             : static void
     880           0 : EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape,
     881             :                    bool wrapResult = false)
     882             : {
     883             :     // Slot access.
     884        5891 :     if (holder) {
     885        5328 :         MOZ_ASSERT(shape);
     886        5328 :         if (wrapResult)
     887             :             writer.wrapResult();
     888             :         writer.typeMonitorResult();
     889             :     } else {
     890             :         // Normally for this op, the result would have to be monitored by TI.
     891             :         // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
     892             :         // that undefined is already registered with the type-set, this can be avoided.
     893             :         writer.returnFromIC();
     894             :     }
     895        5891 : }
     896             : 
     897             : static void
     898           0 : EmitCallGetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     899             :                              Shape* shape, ObjOperandId receiverId)
     900             : {
     901         510 :     if (IsCacheableGetPropCallNative(obj, holder, shape)) {
     902         276 :         JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
     903         276 :         MOZ_ASSERT(target->isNativeWithCppEntry());
     904           0 :         writer.callNativeGetterResult(receiverId, target);
     905             :         writer.typeMonitorResult();
     906             :         return;
     907             :     }
     908             : 
     909         234 :     MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
     910             : 
     911         234 :     JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
     912         234 :     MOZ_ASSERT(target->hasJitEntry());
     913           0 :     writer.callScriptedGetterResult(receiverId, target);
     914             :     writer.typeMonitorResult();
     915             : }
     916             : 
     917             : static void
     918         491 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape,
     919             :                      ObjOperandId objId, ObjOperandId receiverId, ICState::Mode mode)
     920             : {
     921             :     // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
     922             :     // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
     923             :     // require outerizing).
     924           0 :     if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
     925         832 :         Maybe<ObjOperandId> expandoId;
     926         416 :         TestMatchingReceiver(writer, obj, objId, &expandoId);
     927             : 
     928           0 :         if (obj != holder) {
     929         307 :             GeneratePrototypeGuards(writer, obj, holder, objId);
     930             : 
     931             :             // Guard on the holder's shape.
     932         307 :             ObjOperandId holderId = writer.loadObject(holder);
     933         307 :             TestMatchingHolder(writer, holder, holderId);
     934             :         }
     935             :     } else {
     936          75 :         writer.guardHasGetterSetter(objId, shape);
     937             :     }
     938             : 
     939         491 :     EmitCallGetterResultNoGuards(writer, obj, holder, shape, receiverId);
     940         491 : }
     941             : 
     942             : static void
     943             : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     944             :                      Shape* shape, ObjOperandId objId, ICState::Mode mode)
     945             : {
     946           0 :     EmitCallGetterResult(writer, obj, holder, shape, objId, objId, mode);
     947             : }
     948             : 
     949             : void
     950         283 : GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing)
     951             : {
     952           0 :     MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
     953             : 
     954             :     // The stub handles the missing-properties case only if we're seeing one
     955             :     // now, to make sure Ion ICs correctly monitor the undefined type.
     956             : 
     957           0 :     if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
     958         408 :         writer.megamorphicLoadSlotResult(objId, JSID_TO_ATOM(id)->asPropertyName(),
     959           0 :                                          handleMissing);
     960             :     } else {
     961           0 :         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
     962           0 :         writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId(), handleMissing);
     963             :     }
     964         566 :     writer.typeMonitorResult();
     965             : 
     966         283 :     trackAttached(handleMissing ? "MegamorphicMissingNativeSlot" : "MegamorphicNativeSlot");
     967           0 : }
     968             : 
     969             : bool
     970           0 : GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
     971             : {
     972           0 :     RootedShape shape(cx_);
     973           0 :     RootedNativeObject holder(cx_);
     974             : 
     975       28800 :     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
     976             :                                                             resultFlags_,
     977           0 :                                                             isTemporarilyUnoptimizable_);
     978           0 :     switch (type) {
     979             :       case CanAttachNone:
     980             :         return false;
     981             :       case CanAttachReadSlot:
     982           0 :         if (mode_ == ICState::Mode::Megamorphic) {
     983           0 :             attachMegamorphicNativeSlot(objId, id, holder == nullptr);
     984           0 :             return true;
     985             :         }
     986             : 
     987           0 :         maybeEmitIdGuard(id);
     988        5573 :         if (holder) {
     989           0 :             EnsureTrackPropertyTypes(cx_, holder, id);
     990             :             // See the comment in StripPreliminaryObjectStubs.
     991           0 :             if (IsPreliminaryObject(obj))
     992           0 :                 preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
     993             :             else
     994           0 :                 preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
     995             :         }
     996       16719 :         EmitReadSlotResult(writer, obj, holder, shape, objId);
     997       16719 :         EmitReadSlotReturn(writer, obj, holder, shape);
     998             : 
     999           0 :         trackAttached("NativeSlot");
    1000           0 :         return true;
    1001             :       case CanAttachCallGetter: {
    1002             :         // |super.prop| accesses use a |this| value that differs from lookup object
    1003         982 :         MOZ_ASSERT(!idempotent());
    1004           0 :         ObjOperandId receiverId = isSuper() ? writer.guardIsObject(getSuperReceiverValueId())
    1005         500 :                                             : objId;
    1006         491 :         maybeEmitIdGuard(id);
    1007        1964 :         EmitCallGetterResult(writer, obj, holder, shape, objId, receiverId, mode_);
    1008             : 
    1009           1 :         trackAttached("NativeGetter");
    1010             :         return true;
    1011             :       }
    1012             :     }
    1013             : 
    1014           0 :     MOZ_CRASH("Bad NativeGetPropCacheability");
    1015             : }
    1016             : 
    1017             : bool
    1018           0 : GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id)
    1019             : {
    1020             :     // Attach a stub when the receiver is a WindowProxy and we can do the lookup
    1021             :     // on the Window (the global object).
    1022             : 
    1023           0 :     if (!IsWindowProxy(obj))
    1024             :         return false;
    1025             : 
    1026             :     // If we're megamorphic prefer a generic proxy stub that handles a lot more
    1027             :     // cases.
    1028           0 :     if (mode_ == ICState::Mode::Megamorphic)
    1029             :         return false;
    1030             : 
    1031             :     // This must be a WindowProxy for the current Window/global. Else it would
    1032             :     // be a cross-compartment wrapper and IsWindowProxy returns false for
    1033             :     // those.
    1034           0 :     MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
    1035           0 :     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
    1036             : 
    1037             :     // Now try to do the lookup on the Window (the current global).
    1038           0 :     HandleObject windowObj = cx_->global();
    1039           0 :     RootedShape shape(cx_);
    1040           0 :     RootedNativeObject holder(cx_);
    1041           0 :     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
    1042             :                                                             resultFlags_,
    1043           0 :                                                             isTemporarilyUnoptimizable_);
    1044           0 :     switch (type) {
    1045             :       case CanAttachNone:
    1046             :         return false;
    1047             : 
    1048             :       case CanAttachReadSlot: {
    1049           0 :         maybeEmitIdGuard(id);
    1050           0 :         writer.guardClass(objId, GuardClassKind::WindowProxy);
    1051             : 
    1052           0 :         ObjOperandId windowObjId = writer.loadObject(windowObj);
    1053           0 :         EmitReadSlotResult(writer, windowObj, holder, shape, windowObjId);
    1054           0 :         EmitReadSlotReturn(writer, windowObj, holder, shape);
    1055             : 
    1056           0 :         trackAttached("WindowProxySlot");
    1057             :         return true;
    1058             :       }
    1059             : 
    1060             :       case CanAttachCallGetter: {
    1061           0 :         if (!IsCacheableGetPropCallNative(windowObj, holder, shape))
    1062             :             return false;
    1063             : 
    1064             :         // Make sure the native getter is okay with the IC passing the Window
    1065             :         // instead of the WindowProxy as |this| value.
    1066           0 :         JSFunction* callee = &shape->getterObject()->as<JSFunction>();
    1067           0 :         MOZ_ASSERT(callee->isNative());
    1068           0 :         if (!callee->hasJitInfo() || callee->jitInfo()->needsOuterizedThisObject())
    1069             :             return false;
    1070             : 
    1071             :         // If a |super| access, it is not worth the complexity to attach an IC.
    1072           0 :         if (isSuper())
    1073             :             return false;
    1074             : 
    1075             :         // Guard the incoming object is a WindowProxy and inline a getter call based
    1076             :         // on the Window object.
    1077           0 :         maybeEmitIdGuard(id);
    1078           0 :         writer.guardClass(objId, GuardClassKind::WindowProxy);
    1079           0 :         ObjOperandId windowObjId = writer.loadObject(windowObj);
    1080           0 :         EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId, mode_);
    1081             : 
    1082           0 :         trackAttached("WindowProxyGetter");
    1083           0 :         return true;
    1084             :       }
    1085             :     }
    1086             : 
    1087           0 :     MOZ_CRASH("Unreachable");
    1088             : }
    1089             : 
    1090             : bool
    1091           0 : GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId,
    1092             :                                                      HandleId id)
    1093             : {
    1094             :     // We can only optimize this very wrapper-handler, because others might
    1095             :     // have a security policy.
    1096           0 :     if (!IsWrapper(obj) || Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton)
    1097             :         return false;
    1098             : 
    1099             :     // If we're megamorphic prefer a generic proxy stub that handles a lot more
    1100             :     // cases.
    1101         434 :     if (mode_ == ICState::Mode::Megamorphic)
    1102             :         return false;
    1103             : 
    1104         848 :     RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
    1105         848 :     MOZ_ASSERT(unwrapped == UnwrapOneChecked(obj));
    1106             : 
    1107             :     // If we allowed different zones we would have to wrap strings.
    1108        1272 :     if (unwrapped->compartment()->zone() != cx_->compartment()->zone())
    1109             :         return false;
    1110             : 
    1111             :     // Take the unwrapped object's global, and wrap in a
    1112             :     // this-compartment wrapper. This is what will be stored in the IC
    1113             :     // keep the compartment alive.
    1114           0 :     RootedObject wrappedTargetGlobal(cx_, &unwrapped->deprecatedGlobal());
    1115           0 :     if (!cx_->compartment()->wrap(cx_, &wrappedTargetGlobal))
    1116             :         return false;
    1117             : 
    1118         364 :     bool isWindowProxy = false;
    1119        1092 :     RootedShape shape(cx_);
    1120           0 :     RootedNativeObject holder(cx_);
    1121             : 
    1122             :     // Enter realm of target since some checks have side-effects
    1123             :     // such as de-lazifying type info.
    1124             :     {
    1125           0 :         AutoRealm ar(cx_, unwrapped);
    1126             : 
    1127             :         // The first CCW for iframes is almost always wrapping another WindowProxy
    1128             :         // so we optimize for that case as well.
    1129         364 :         isWindowProxy = IsWindowProxy(unwrapped);
    1130         364 :         if (isWindowProxy) {
    1131           0 :             MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->realm()->maybeGlobal());
    1132           0 :             unwrapped = cx_->global();
    1133           0 :             MOZ_ASSERT(unwrapped);
    1134             :         }
    1135             : 
    1136             :         NativeGetPropCacheability canCache =
    1137           0 :             CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_,
    1138         364 :                                 resultFlags_, isTemporarilyUnoptimizable_);
    1139         364 :         if (canCache != CanAttachReadSlot)
    1140           0 :             return false;
    1141             : 
    1142         241 :         if (holder) {
    1143             :             // Need to be in the compartment of the holder to
    1144             :             // call EnsureTrackPropertyTypes
    1145         460 :             EnsureTrackPropertyTypes(cx_, holder, id);
    1146           0 :             if (unwrapped == holder) {
    1147             :                 // See the comment in StripPreliminaryObjectStubs.
    1148         115 :                 if (IsPreliminaryObject(unwrapped))
    1149          42 :                     preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
    1150             :                 else
    1151           0 :                     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    1152             :             }
    1153             :         } else {
    1154             :             // UNCACHEABLE_PROTO may result in guards against specific (cross-compartment)
    1155             :             // prototype objects, so don't try to attach IC if we see the flag at all.
    1156          11 :             if (UncacheableProtoOnChain(unwrapped)) {
    1157             :                 return false;
    1158             :             }
    1159             :         }
    1160             :     }
    1161             : 
    1162           0 :     maybeEmitIdGuard(id);
    1163         241 :     writer.guardIsProxy(objId);
    1164         241 :     writer.guardHasProxyHandler(objId, Wrapper::wrapperHandler(obj));
    1165             : 
    1166             :     // Load the object wrapped by the CCW
    1167           0 :     ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
    1168             : 
    1169             :     // If the compartment of the wrapped object is different we should fail.
    1170         723 :     writer.guardCompartment(wrapperTargetId, wrappedTargetGlobal, unwrapped->compartment());
    1171             : 
    1172           0 :     ObjOperandId unwrappedId = wrapperTargetId;
    1173           0 :     if (isWindowProxy) {
    1174             :         // For the WindowProxy case also unwrap the inner window.
    1175             :         // We avoid loadObject, because storing cross compartment objects in
    1176             :         // stubs / JIT code is tricky.
    1177           0 :         writer.guardClass(wrapperTargetId, GuardClassKind::WindowProxy);
    1178           0 :         unwrappedId = writer.loadWrapperTarget(wrapperTargetId);
    1179             :     }
    1180             : 
    1181         723 :     EmitReadSlotResult<SlotReadType::CrossCompartment>(writer, unwrapped, holder, shape, unwrappedId);
    1182         723 :     EmitReadSlotReturn(writer, unwrapped, holder, shape, /* wrapResult = */ true);
    1183             : 
    1184           0 :     trackAttached("CCWSlot");
    1185         241 :     return true;
    1186             : }
    1187             : 
    1188             : static bool
    1189           0 : GetXrayExpandoShapeWrapper(JSContext* cx, HandleObject xray, MutableHandleObject wrapper)
    1190             : {
    1191           0 :     Value v = GetProxyReservedSlot(xray, GetXrayJitInfo()->xrayHolderSlot);
    1192           0 :     if (v.isObject()) {
    1193           0 :         NativeObject* holder = &v.toObject().as<NativeObject>();
    1194           0 :         v = holder->getFixedSlot(GetXrayJitInfo()->holderExpandoSlot);
    1195           0 :         if (v.isObject()) {
    1196           0 :             RootedNativeObject expando(cx, &UncheckedUnwrap(&v.toObject())->as<NativeObject>());
    1197           0 :             wrapper.set(NewWrapperWithObjectShape(cx, expando));
    1198           0 :             return wrapper != nullptr;
    1199             :         }
    1200             :     }
    1201           0 :     wrapper.set(nullptr);
    1202           0 :     return true;
    1203             : }
    1204             : 
    1205             : bool
    1206         490 : GetPropIRGenerator::tryAttachXrayCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId,
    1207             :                                                          HandleId id)
    1208             : {
    1209         980 :     if (!IsProxy(obj))
    1210             :         return false;
    1211             : 
    1212         216 :     XrayJitInfo* info = GetXrayJitInfo();
    1213         648 :     if (!info || !info->isCrossCompartmentXray(GetProxyHandler(obj)))
    1214             :         return false;
    1215             : 
    1216           0 :     if (!info->globalHasExclusiveExpandos(cx_->global()))
    1217             :         return false;
    1218             : 
    1219           0 :     RootedObject target(cx_, UncheckedUnwrap(obj));
    1220             : 
    1221           0 :     RootedObject expandoShapeWrapper(cx_);
    1222           0 :     if (!GetXrayExpandoShapeWrapper(cx_, obj, &expandoShapeWrapper)) {
    1223           0 :         cx_->recoverFromOutOfMemory();
    1224           0 :         return false;
    1225             :     }
    1226             : 
    1227             :     // Look for a getter we can call on the xray or its prototype chain.
    1228           0 :     Rooted<PropertyDescriptor> desc(cx_);
    1229           0 :     RootedObject holder(cx_, obj);
    1230           0 :     AutoObjectVector prototypes(cx_);
    1231           0 :     AutoObjectVector prototypeExpandoShapeWrappers(cx_);
    1232             :     while (true) {
    1233           0 :         if (!GetOwnPropertyDescriptor(cx_, holder, id, &desc)) {
    1234           0 :             cx_->clearPendingException();
    1235           0 :             return false;
    1236             :         }
    1237           0 :         if (desc.object())
    1238             :             break;
    1239           0 :         if (!GetPrototype(cx_, holder, &holder)) {
    1240           0 :             cx_->clearPendingException();
    1241           0 :             return false;
    1242             :         }
    1243           0 :         if (!holder || !IsProxy(holder) || !info->isCrossCompartmentXray(GetProxyHandler(holder)))
    1244             :             return false;
    1245           0 :         RootedObject prototypeExpandoShapeWrapper(cx_);
    1246           0 :         if (!GetXrayExpandoShapeWrapper(cx_, holder, &prototypeExpandoShapeWrapper) ||
    1247           0 :             !prototypes.append(holder) ||
    1248           0 :             !prototypeExpandoShapeWrappers.append(prototypeExpandoShapeWrapper))
    1249             :         {
    1250           0 :             cx_->recoverFromOutOfMemory();
    1251           0 :             return false;
    1252             :         }
    1253             :     }
    1254           0 :     if (!desc.isAccessorDescriptor())
    1255             :         return false;
    1256             : 
    1257           0 :     RootedObject getter(cx_, desc.getterObject());
    1258           0 :     if (!getter || !getter->is<JSFunction>() || !getter->as<JSFunction>().isNative())
    1259             :         return false;
    1260             : 
    1261           0 :     maybeEmitIdGuard(id);
    1262           0 :     writer.guardIsProxy(objId);
    1263           0 :     writer.guardHasProxyHandler(objId, GetProxyHandler(obj));
    1264             : 
    1265             :     // Load the object wrapped by the CCW
    1266           0 :     ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
    1267             : 
    1268             :     // Test the wrapped object's class. The properties held by xrays or their
    1269             :     // prototypes will be invariant for objects of a given class, except for
    1270             :     // changes due to xray expandos or xray prototype mutations.
    1271           0 :     writer.guardAnyClass(wrapperTargetId, target->getClass());
    1272             : 
    1273             :     // Make sure the expandos on the xray and its prototype chain match up with
    1274             :     // what we expect. The expando shape needs to be consistent, to ensure it
    1275             :     // has not had any shadowing properties added, and the expando cannot have
    1276             :     // any custom prototype (xray prototypes are stable otherwise).
    1277             :     //
    1278             :     // We can only do this for xrays with exclusive access to their expandos
    1279             :     // (as we checked earlier), which store a pointer to their expando
    1280             :     // directly. Xrays in other compartments may share their expandos with each
    1281             :     // other and a VM call is needed just to find the expando.
    1282           0 :     writer.guardXrayExpandoShapeAndDefaultProto(objId, expandoShapeWrapper);
    1283           0 :     for (size_t i = 0; i < prototypes.length(); i++) {
    1284           0 :         JSObject* proto = prototypes[i];
    1285           0 :         ObjOperandId protoId = writer.loadObject(proto);
    1286           0 :         writer.guardXrayExpandoShapeAndDefaultProto(protoId, prototypeExpandoShapeWrappers[i]);
    1287             :     }
    1288             : 
    1289           0 :     writer.callNativeGetterResult(objId, &getter->as<JSFunction>());
    1290           0 :     writer.typeMonitorResult();
    1291             : 
    1292           0 :     trackAttached("XrayGetter");
    1293           0 :     return true;
    1294             : }
    1295             : 
    1296             : bool
    1297           0 : GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    1298             :                                           bool handleDOMProxies)
    1299             : {
    1300         412 :     MOZ_ASSERT(obj->is<ProxyObject>());
    1301             : 
    1302           0 :     writer.guardIsProxy(objId);
    1303             : 
    1304         206 :     if (!handleDOMProxies) {
    1305             :         // Ensure that the incoming object is not a DOM proxy, so that we can get to
    1306             :         // the specialized stubs
    1307           0 :         writer.guardNotDOMProxy(objId);
    1308             :     }
    1309             : 
    1310         206 :     if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
    1311           0 :         MOZ_ASSERT(!isSuper());
    1312           0 :         maybeEmitIdGuard(id);
    1313           0 :         writer.callProxyGetResult(objId, id);
    1314             :     } else {
    1315             :         // Attach a stub that handles every id.
    1316           5 :         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
    1317           0 :         MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
    1318          10 :         MOZ_ASSERT(!isSuper());
    1319           0 :         writer.callProxyGetByValueResult(objId, getElemKeyValueId());
    1320             :     }
    1321             : 
    1322         412 :     writer.typeMonitorResult();
    1323             : 
    1324           0 :     trackAttached("GenericProxy");
    1325         206 :     return true;
    1326             : }
    1327             : 
    1328             : ObjOperandId
    1329           0 : IRGenerator::guardDOMProxyExpandoObjectAndShape(JSObject* obj, ObjOperandId objId,
    1330             :                                                 const Value& expandoVal, JSObject* expandoObj)
    1331             : {
    1332           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    1333             : 
    1334           0 :     TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
    1335             : 
    1336             :     // Shape determines Class, so now it must be a DOM proxy.
    1337           0 :     ValOperandId expandoValId;
    1338           0 :     if (expandoVal.isObject())
    1339           0 :         expandoValId = writer.loadDOMExpandoValue(objId);
    1340             :     else
    1341           0 :         expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
    1342             : 
    1343             :     // Guard the expando is an object and shape guard.
    1344           0 :     ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
    1345           0 :     TestMatchingHolder(writer, expandoObj, expandoObjId);
    1346           0 :     return expandoObjId;
    1347             : }
    1348             : 
    1349             : bool
    1350           0 : GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id)
    1351             : {
    1352           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    1353             : 
    1354           0 :     RootedValue expandoVal(cx_, GetProxyPrivate(obj));
    1355           0 :     RootedObject expandoObj(cx_);
    1356           0 :     if (expandoVal.isObject()) {
    1357           0 :         expandoObj = &expandoVal.toObject();
    1358             :     } else {
    1359           0 :         MOZ_ASSERT(!expandoVal.isUndefined(),
    1360             :                    "How did a missing expando manage to shadow things?");
    1361           0 :         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
    1362           0 :         MOZ_ASSERT(expandoAndGeneration);
    1363           0 :         expandoObj = &expandoAndGeneration->expando.toObject();
    1364             :     }
    1365             : 
    1366             :     // Try to do the lookup on the expando object.
    1367           0 :     RootedNativeObject holder(cx_);
    1368           0 :     RootedShape propShape(cx_);
    1369             :     NativeGetPropCacheability canCache =
    1370           0 :         CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_,
    1371           0 :                                resultFlags_, isTemporarilyUnoptimizable_);
    1372           0 :     if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter)
    1373             :         return false;
    1374           0 :     if (!holder)
    1375             :         return false;
    1376             : 
    1377           0 :     MOZ_ASSERT(holder == expandoObj);
    1378             : 
    1379           0 :     maybeEmitIdGuard(id);
    1380             :     ObjOperandId expandoObjId =
    1381           0 :         guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
    1382             : 
    1383           0 :     if (canCache == CanAttachReadSlot) {
    1384             :         // Load from the expando's slots.
    1385           0 :         EmitLoadSlotResult(writer, expandoObjId, &expandoObj->as<NativeObject>(), propShape);
    1386           0 :         writer.typeMonitorResult();
    1387             :     } else {
    1388             :         // Call the getter. Note that we pass objId, the DOM proxy, as |this|
    1389             :         // and not the expando object.
    1390           0 :         MOZ_ASSERT(canCache == CanAttachCallGetter);
    1391           0 :         EmitCallGetterResultNoGuards(writer, expandoObj, expandoObj, propShape, objId);
    1392             :     }
    1393             : 
    1394           0 :     trackAttached("DOMProxyExpando");
    1395           0 :     return true;
    1396             : }
    1397             : 
    1398             : bool
    1399           0 : GetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id)
    1400             : {
    1401           0 :     MOZ_ASSERT(!isSuper());
    1402           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    1403             : 
    1404           0 :     maybeEmitIdGuard(id);
    1405           0 :     TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
    1406           0 :     writer.callProxyGetResult(objId, id);
    1407           0 :     writer.typeMonitorResult();
    1408             : 
    1409           0 :     trackAttached("DOMProxyShadowed");
    1410           0 :     return true;
    1411             : }
    1412             : 
    1413             : // Callers are expected to have already guarded on the shape of the
    1414             : // object, which guarantees the object is a DOM proxy.
    1415             : static void
    1416           0 : CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer, JSObject* obj, jsid id,
    1417             :                                   ObjOperandId objId)
    1418             : {
    1419           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    1420             : 
    1421           0 :     Value expandoVal = GetProxyPrivate(obj);
    1422             : 
    1423          10 :     ValOperandId expandoId;
    1424           0 :     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
    1425           1 :         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
    1426           1 :         expandoId = writer.loadDOMExpandoValueGuardGeneration(objId, expandoAndGeneration);
    1427           0 :         expandoVal = expandoAndGeneration->expando;
    1428             :     } else {
    1429           0 :         expandoId = writer.loadDOMExpandoValue(objId);
    1430             :     }
    1431             : 
    1432          10 :     if (expandoVal.isUndefined()) {
    1433             :         // Guard there's no expando object.
    1434           0 :         writer.guardType(expandoId, JSVAL_TYPE_UNDEFINED);
    1435           0 :     } else if (expandoVal.isObject()) {
    1436             :         // Guard the proxy either has no expando object or, if it has one, that
    1437             :         // the shape matches the current expando object.
    1438           1 :         NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>();
    1439           0 :         MOZ_ASSERT(!expandoObj.containsPure(id));
    1440           1 :         writer.guardDOMExpandoMissingOrGuardShape(expandoId, expandoObj.lastProperty());
    1441             :     } else {
    1442           0 :         MOZ_CRASH("Invalid expando value");
    1443             :     }
    1444           0 : }
    1445             : 
    1446             : bool
    1447           0 : GetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id)
    1448             : {
    1449          10 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    1450             : 
    1451           0 :     RootedObject checkObj(cx_, obj->staticPrototype());
    1452           0 :     if (!checkObj)
    1453             :         return false;
    1454             : 
    1455           0 :     RootedNativeObject holder(cx_);
    1456          30 :     RootedShape shape(cx_);
    1457          50 :     NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
    1458             :                                                                 pc_, resultFlags_,
    1459          10 :                                                                 isTemporarilyUnoptimizable_);
    1460          10 :     if (canCache == CanAttachNone)
    1461             :         return false;
    1462             : 
    1463          10 :     maybeEmitIdGuard(id);
    1464             : 
    1465             :     // Guard that our expando object hasn't started shadowing this property.
    1466          20 :     TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
    1467           0 :     CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
    1468             : 
    1469          10 :     if (holder) {
    1470             :         // Found the property on the prototype chain. Treat it like a native
    1471             :         // getprop.
    1472          20 :         GeneratePrototypeGuards(writer, obj, holder, objId);
    1473             : 
    1474             :         // Guard on the holder of the property.
    1475           0 :         ObjOperandId holderId = writer.loadObject(holder);
    1476          10 :         TestMatchingHolder(writer, holder, holderId);
    1477             : 
    1478          10 :         if (canCache == CanAttachReadSlot) {
    1479           6 :             EmitLoadSlotResult(writer, holderId, holder, shape);
    1480           3 :             writer.typeMonitorResult();
    1481             :         } else {
    1482             :             // EmitCallGetterResultNoGuards expects |obj| to be the object the
    1483             :             // property is on to do some checks. Since we actually looked at
    1484             :             // checkObj, and no extra guards will be generated, we can just
    1485             :             // pass that instead.
    1486           7 :             MOZ_ASSERT(canCache == CanAttachCallGetter);
    1487          14 :             MOZ_ASSERT(!isSuper());
    1488           0 :             EmitCallGetterResultNoGuards(writer, checkObj, holder, shape, objId);
    1489             :         }
    1490             :     } else {
    1491             :         // Property was not found on the prototype chain. Deoptimize down to
    1492             :         // proxy get call.
    1493           0 :         MOZ_ASSERT(!isSuper());
    1494           0 :         writer.callProxyGetResult(objId, id);
    1495           0 :         writer.typeMonitorResult();
    1496             :     }
    1497             : 
    1498           0 :     trackAttached("DOMProxyUnshadowed");
    1499          10 :     return true;
    1500             : }
    1501             : 
    1502             : bool
    1503         490 : GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id)
    1504             : {
    1505           0 :     ProxyStubType type = GetProxyStubType(cx_, obj, id);
    1506         490 :     if (type == ProxyStubType::None)
    1507             :         return false;
    1508             : 
    1509             :     // The proxy stubs don't currently support |super| access.
    1510         432 :     if (isSuper())
    1511             :         return false;
    1512             : 
    1513         216 :     if (mode_ == ICState::Mode::Megamorphic)
    1514          12 :         return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
    1515             : 
    1516         204 :     switch (type) {
    1517             :       case ProxyStubType::None:
    1518             :         break;
    1519             :       case ProxyStubType::DOMExpando:
    1520           0 :         if (tryAttachDOMProxyExpando(obj, objId, id))
    1521             :             return true;
    1522           0 :         if (*isTemporarilyUnoptimizable_) {
    1523             :             // Scripted getter without JIT code. Just wait.
    1524             :             return false;
    1525             :         }
    1526             :         MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
    1527             :       case ProxyStubType::DOMShadowed:
    1528           0 :         return tryAttachDOMProxyShadowed(obj, objId, id);
    1529             :       case ProxyStubType::DOMUnshadowed:
    1530          10 :         if (tryAttachDOMProxyUnshadowed(obj, objId, id))
    1531             :             return true;
    1532           0 :         if (*isTemporarilyUnoptimizable_) {
    1533             :             // Scripted getter without JIT code. Just wait.
    1534             :             return false;
    1535             :         }
    1536           0 :         return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
    1537             :       case ProxyStubType::Generic:
    1538         194 :         return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ false);
    1539             :     }
    1540             : 
    1541           0 :     MOZ_CRASH("Unexpected ProxyStubType");
    1542             : }
    1543             : 
    1544             : bool
    1545           0 : GetPropIRGenerator::tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id)
    1546             : {
    1547        1706 :     if (!obj->is<UnboxedPlainObject>())
    1548             :         return false;
    1549             : 
    1550         244 :     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
    1551         122 :     if (!property)
    1552             :         return false;
    1553             : 
    1554           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint)
    1555             :         return false;
    1556             : 
    1557           0 :     maybeEmitIdGuard(id);
    1558         121 :     writer.guardGroupForLayout(objId, obj->group());
    1559           0 :     writer.loadUnboxedPropertyResult(objId, property->type,
    1560         242 :                                      UnboxedPlainObject::offsetOfData() + property->offset);
    1561           0 :     if (property->type == JSVAL_TYPE_OBJECT)
    1562          49 :         writer.typeMonitorResult();
    1563             :     else
    1564           0 :         writer.returnFromIC();
    1565             : 
    1566         121 :     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    1567             : 
    1568           0 :     trackAttached("Unboxed");
    1569         121 :     return true;
    1570             : }
    1571             : 
    1572             : bool
    1573           0 : GetPropIRGenerator::tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id)
    1574             : {
    1575        1464 :     if (!obj->is<UnboxedPlainObject>())
    1576             :         return false;
    1577             : 
    1578           0 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    1579           1 :     if (!expando)
    1580             :         return false;
    1581             : 
    1582           0 :     Shape* shape = expando->lookup(cx_, id);
    1583           0 :     if (!shape || !shape->isDataProperty())
    1584             :         return false;
    1585             : 
    1586           0 :     maybeEmitIdGuard(id);
    1587           2 :     EmitReadSlotResult(writer, obj, obj, shape, objId);
    1588           2 :     EmitReadSlotReturn(writer, obj, obj, shape);
    1589             : 
    1590           0 :     trackAttached("UnboxedExpando");
    1591           1 :     return true;
    1592             : }
    1593             : 
    1594             : static TypedThingLayout
    1595           0 : GetTypedThingLayout(const Class* clasp)
    1596             : {
    1597           0 :     if (IsTypedArrayClass(clasp))
    1598             :         return Layout_TypedArray;
    1599           0 :     if (IsOutlineTypedObjectClass(clasp))
    1600             :         return Layout_OutlineTypedObject;
    1601           0 :     if (IsInlineTypedObjectClass(clasp))
    1602             :         return Layout_InlineTypedObject;
    1603           0 :     MOZ_CRASH("Bad object class");
    1604             : }
    1605             : 
    1606             : bool
    1607           0 : GetPropIRGenerator::tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id)
    1608             : {
    1609        1462 :     if (!obj->is<TypedObject>())
    1610             :         return false;
    1611             : 
    1612           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint || cx_->zone()->detachedTypedObjects)
    1613             :         return false;
    1614             : 
    1615           0 :     TypedObject* typedObj = &obj->as<TypedObject>();
    1616           0 :     if (!typedObj->typeDescr().is<StructTypeDescr>())
    1617             :         return false;
    1618             : 
    1619           0 :     StructTypeDescr* structDescr = &typedObj->typeDescr().as<StructTypeDescr>();
    1620             :     size_t fieldIndex;
    1621           0 :     if (!structDescr->fieldIndex(id, &fieldIndex))
    1622             :         return false;
    1623             : 
    1624           0 :     TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
    1625           0 :     if (!fieldDescr->is<SimpleTypeDescr>())
    1626             :         return false;
    1627             : 
    1628           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    1629             : 
    1630           0 :     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
    1631           0 :     uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>());
    1632             : 
    1633           0 :     maybeEmitIdGuard(id);
    1634           0 :     writer.guardNoDetachedTypedObjects();
    1635           0 :     writer.guardGroupForLayout(objId, obj->group());
    1636           0 :     writer.loadTypedObjectResult(objId, fieldOffset, layout, typeDescr);
    1637             : 
    1638             :     // Only monitor the result if the type produced by this stub might vary.
    1639           0 :     bool monitorLoad = false;
    1640           0 :     if (SimpleTypeDescrKeyIsScalar(typeDescr)) {
    1641           0 :         Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr);
    1642           0 :         monitorLoad = type == Scalar::Uint32;
    1643             :     } else {
    1644           0 :         ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr);
    1645           0 :         monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
    1646             :     }
    1647             : 
    1648           0 :     if (monitorLoad)
    1649           0 :         writer.typeMonitorResult();
    1650             :     else
    1651           0 :         writer.returnFromIC();
    1652             : 
    1653           0 :     trackAttached("TypedObject");
    1654           0 :     return true;
    1655             : }
    1656             : 
    1657             : bool
    1658           0 : GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id)
    1659             : {
    1660       36485 :     if (!JSID_IS_ATOM(id, cx_->names().length))
    1661             :         return false;
    1662             : 
    1663         258 :     if (!(resultFlags_ & GetPropertyResultFlags::AllowInt32))
    1664             :         return false;
    1665             : 
    1666         258 :     if (obj->is<ArrayObject>()) {
    1667             :         // Make sure int32 is added to the TypeSet before we attach a stub, so
    1668             :         // the stub can return int32 values without monitoring the result.
    1669           0 :         if (obj->as<ArrayObject>().length() > INT32_MAX)
    1670             :             return false;
    1671             : 
    1672           0 :         maybeEmitIdGuard(id);
    1673           0 :         writer.guardClass(objId, GuardClassKind::Array);
    1674         102 :         writer.loadInt32ArrayLengthResult(objId);
    1675         204 :         writer.returnFromIC();
    1676             : 
    1677           0 :         trackAttached("ArrayLength");
    1678           0 :         return true;
    1679             :     }
    1680             : 
    1681           0 :     if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
    1682           0 :         maybeEmitIdGuard(id);
    1683           8 :         if (obj->is<MappedArgumentsObject>()) {
    1684           0 :             writer.guardClass(objId, GuardClassKind::MappedArguments);
    1685             :         } else {
    1686           8 :             MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
    1687           0 :             writer.guardClass(objId, GuardClassKind::UnmappedArguments);
    1688             :         }
    1689           4 :         writer.loadArgumentsObjectLengthResult(objId);
    1690           8 :         writer.returnFromIC();
    1691             : 
    1692           4 :         trackAttached("ArgumentsObjectLength");
    1693           4 :         return true;
    1694             :     }
    1695             : 
    1696             :     return false;
    1697             : }
    1698             : 
    1699             : bool
    1700           0 : GetPropIRGenerator::tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id)
    1701             : {
    1702             :     // Function properties are lazily resolved so they might not be defined yet.
    1703             :     // And we might end up in a situation where we always have a fresh function
    1704             :     // object during the IC generation.
    1705         980 :     if (!obj->is<JSFunction>())
    1706             :         return false;
    1707             : 
    1708           1 :     JSObject* holder = nullptr;
    1709           0 :     PropertyResult prop;
    1710             :     // This property exists already, don't attach the stub.
    1711           0 :     if (LookupPropertyPure(cx_, obj, id, &holder, &prop))
    1712             :         return false;
    1713             : 
    1714           1 :     JSFunction* fun = &obj->as<JSFunction>();
    1715             : 
    1716           5 :     if (JSID_IS_ATOM(id, cx_->names().length)) {
    1717             :         // length was probably deleted from the function.
    1718           0 :         if (fun->hasResolvedLength())
    1719             :             return false;
    1720             : 
    1721             :         // Lazy functions don't store the length.
    1722           0 :         if (fun->isInterpretedLazy())
    1723             :             return false;
    1724             : 
    1725           0 :         maybeEmitIdGuard(id);
    1726           0 :         writer.guardClass(objId, GuardClassKind::JSFunction);
    1727           0 :         writer.loadFunctionLengthResult(objId);
    1728           0 :         writer.returnFromIC();
    1729             : 
    1730           0 :         trackAttached("FunctionLength");
    1731           0 :         return true;
    1732             :     }
    1733             : 
    1734             :     return false;
    1735             : }
    1736             : 
    1737             : bool
    1738           0 : GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id)
    1739             : {
    1740           0 :     if (!obj->is<ModuleNamespaceObject>())
    1741             :         return false;
    1742             : 
    1743           0 :     Rooted<ModuleNamespaceObject*> ns(cx_, &obj->as<ModuleNamespaceObject>());
    1744           0 :     RootedModuleEnvironmentObject env(cx_);
    1745           0 :     RootedShape shape(cx_);
    1746           0 :     if (!ns->bindings().lookup(id, env.address(), shape.address()))
    1747             :         return false;
    1748             : 
    1749             :     // Don't emit a stub until the target binding has been initialized.
    1750           0 :     if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
    1751             :         return false;
    1752             : 
    1753           0 :     if (IsIonEnabled(cx_))
    1754           0 :         EnsureTrackPropertyTypes(cx_, env, shape->propid());
    1755             : 
    1756             :     // Check for the specific namespace object.
    1757           0 :     maybeEmitIdGuard(id);
    1758           0 :     writer.guardSpecificObject(objId, ns);
    1759             : 
    1760           0 :     ObjOperandId envId = writer.loadObject(env);
    1761           0 :     EmitLoadSlotResult(writer, envId, env, shape);
    1762           0 :     writer.typeMonitorResult();
    1763             : 
    1764           0 :     trackAttached("ModuleNamespace");
    1765           0 :     return true;
    1766             : }
    1767             : 
    1768             : bool
    1769           0 : GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId, HandleId id)
    1770             : {
    1771             :     JSValueType primitiveType;
    1772         369 :     RootedNativeObject proto(cx_);
    1773           0 :     if (val_.isString()) {
    1774           0 :         if (JSID_IS_ATOM(id, cx_->names().length)) {
    1775             :             // String length is special-cased, see js::GetProperty.
    1776             :             return false;
    1777             :         }
    1778           0 :         primitiveType = JSVAL_TYPE_STRING;
    1779           0 :         proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_String));
    1780           0 :     } else if (val_.isNumber()) {
    1781           0 :         primitiveType = JSVAL_TYPE_DOUBLE;
    1782           0 :         proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_Number));
    1783           0 :     } else if (val_.isBoolean()) {
    1784           0 :         primitiveType = JSVAL_TYPE_BOOLEAN;
    1785           0 :         proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_Boolean));
    1786          54 :     } else if (val_.isSymbol()) {
    1787           0 :         primitiveType = JSVAL_TYPE_SYMBOL;
    1788           0 :         proto = MaybeNativeObject(cx_->global()->maybeGetPrototype(JSProto_Symbol));
    1789             :     } else {
    1790          81 :         MOZ_ASSERT(val_.isNullOrUndefined() || val_.isMagic());
    1791             :         return false;
    1792             :     }
    1793           0 :     if (!proto)
    1794             :         return false;
    1795             : 
    1796           0 :     RootedShape shape(cx_);
    1797         228 :     RootedNativeObject holder(cx_);
    1798         380 :     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_,
    1799             :                                                             resultFlags_,
    1800          76 :                                                             isTemporarilyUnoptimizable_);
    1801           0 :     if (type != CanAttachReadSlot)
    1802             :         return false;
    1803             : 
    1804          76 :     if (holder) {
    1805             :         // Instantiate this property, for use during Ion compilation.
    1806           0 :         if (IsIonEnabled(cx_))
    1807         152 :             EnsureTrackPropertyTypes(cx_, holder, id);
    1808             :     }
    1809             : 
    1810           0 :     writer.guardType(valId, primitiveType);
    1811          76 :     maybeEmitIdGuard(id);
    1812             : 
    1813           0 :     ObjOperandId protoId = writer.loadObject(proto);
    1814         228 :     EmitReadSlotResult(writer, proto, holder, shape, protoId);
    1815         228 :     EmitReadSlotReturn(writer, proto, holder, shape);
    1816             : 
    1817           0 :     trackAttached("Primitive");
    1818          76 :     return true;
    1819             : }
    1820             : 
    1821             : bool
    1822           0 : GetPropIRGenerator::tryAttachStringLength(ValOperandId valId, HandleId id)
    1823             : {
    1824           0 :     if (!val_.isString() || !JSID_IS_ATOM(id, cx_->names().length))
    1825             :         return false;
    1826             : 
    1827           0 :     StringOperandId strId = writer.guardIsString(valId);
    1828           0 :     maybeEmitIdGuard(id);
    1829          18 :     writer.loadStringLengthResult(strId);
    1830          36 :     writer.returnFromIC();
    1831             : 
    1832           0 :     trackAttached("StringLength");
    1833          18 :     return true;
    1834             : }
    1835             : 
    1836             : bool
    1837          43 : GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId)
    1838             : {
    1839           0 :     MOZ_ASSERT(idVal_.isInt32());
    1840             : 
    1841          86 :     if (!val_.isString())
    1842             :         return false;
    1843             : 
    1844           0 :     int32_t index = idVal_.toInt32();
    1845           5 :     if (index < 0)
    1846             :         return false;
    1847             : 
    1848           0 :     JSString* str = val_.toString();
    1849           0 :     if (size_t(index) >= str->length())
    1850             :         return false;
    1851             : 
    1852             :     // This follows JSString::getChar, otherwise we fail to attach getChar in a lot of cases.
    1853           5 :     if (str->isRope()) {
    1854           0 :         JSRope* rope = &str->asRope();
    1855             : 
    1856             :         // Make sure the left side contains the index.
    1857           0 :         if (size_t(index) >= rope->leftChild()->length())
    1858             :             return false;
    1859             : 
    1860           0 :         str = rope->leftChild();
    1861             :     }
    1862             : 
    1863          15 :     if (!str->isLinear() ||
    1864           0 :         str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT)
    1865             :     {
    1866             :         return false;
    1867             :     }
    1868             : 
    1869           0 :     StringOperandId strId = writer.guardIsString(valId);
    1870           0 :     Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
    1871           5 :     writer.loadStringCharResult(strId, int32IndexId);
    1872          10 :     writer.returnFromIC();
    1873             : 
    1874           0 :     trackAttached("StringChar");
    1875           5 :     return true;
    1876             : }
    1877             : 
    1878             : bool
    1879           0 : GetPropIRGenerator::tryAttachMagicArgumentsName(ValOperandId valId, HandleId id)
    1880             : {
    1881          58 :     if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
    1882             :         return false;
    1883             : 
    1884           0 :     if (!JSID_IS_ATOM(id, cx_->names().length) && !JSID_IS_ATOM(id, cx_->names().callee))
    1885             :         return false;
    1886             : 
    1887           0 :     maybeEmitIdGuard(id);
    1888           0 :     writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
    1889          54 :     writer.guardFrameHasNoArgumentsObject();
    1890             : 
    1891           0 :     if (JSID_IS_ATOM(id, cx_->names().length)) {
    1892           0 :         writer.loadFrameNumActualArgsResult();
    1893          27 :         writer.returnFromIC();
    1894             :     } else {
    1895           0 :         MOZ_ASSERT(JSID_IS_ATOM(id, cx_->names().callee));
    1896           0 :         writer.loadFrameCalleeResult();
    1897           0 :         writer.typeMonitorResult();
    1898             :     }
    1899             : 
    1900           0 :     trackAttached("MagicArgumentsName");
    1901          27 :     return true;
    1902             : }
    1903             : 
    1904             : bool
    1905          38 : GetPropIRGenerator::tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId)
    1906             : {
    1907           0 :     MOZ_ASSERT(idVal_.isInt32());
    1908             : 
    1909          76 :     if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
    1910             :         return false;
    1911             : 
    1912           0 :     writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
    1913          76 :     writer.guardFrameHasNoArgumentsObject();
    1914             : 
    1915           0 :     Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
    1916          38 :     writer.loadFrameArgumentResult(int32IndexId);
    1917          76 :     writer.typeMonitorResult();
    1918             : 
    1919           0 :     trackAttached("MagicArgument");
    1920          38 :     return true;
    1921             : }
    1922             : 
    1923             : bool
    1924           3 : GetPropIRGenerator::tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId,
    1925             :                                                 Int32OperandId indexId)
    1926             : {
    1927           9 :     if (!obj->is<ArgumentsObject>() || obj->as<ArgumentsObject>().hasOverriddenElement())
    1928             :         return false;
    1929             : 
    1930           6 :     if (!(resultFlags_ & GetPropertyResultFlags::Monitored))
    1931             :         return false;
    1932             : 
    1933           6 :     if (obj->is<MappedArgumentsObject>()) {
    1934           0 :         writer.guardClass(objId, GuardClassKind::MappedArguments);
    1935             :     } else {
    1936           0 :         MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
    1937           3 :         writer.guardClass(objId, GuardClassKind::UnmappedArguments);
    1938             :     }
    1939             : 
    1940           3 :     writer.loadArgumentsObjectArgResult(objId, indexId);
    1941           6 :     writer.typeMonitorResult();
    1942             : 
    1943           0 :     trackAttached("ArgumentsObjectArg");
    1944           3 :     return true;
    1945             : }
    1946             : 
    1947             : bool
    1948         141 : GetPropIRGenerator::tryAttachDenseElement(HandleObject obj, ObjOperandId objId,
    1949             :                                           uint32_t index, Int32OperandId indexId)
    1950             : {
    1951         282 :     if (!obj->isNative())
    1952             :         return false;
    1953             : 
    1954           0 :     NativeObject* nobj = &obj->as<NativeObject>();
    1955           0 :     if (!nobj->containsDenseElement(index))
    1956             :         return false;
    1957             : 
    1958           0 :     TestMatchingNativeReceiver(writer, nobj, objId);
    1959         128 :     writer.loadDenseElementResult(objId, indexId);
    1960         256 :     writer.typeMonitorResult();
    1961             : 
    1962           0 :     trackAttached("DenseElement");
    1963         128 :     return true;
    1964             : }
    1965             : 
    1966             : static bool
    1967          13 : CanAttachDenseElementHole(NativeObject* obj, bool ownProp, bool allowIndexedReceiver = false)
    1968             : {
    1969             :     // Make sure the objects on the prototype don't have any indexed properties
    1970             :     // or that such properties can't appear without a shape change.
    1971             :     // Otherwise returning undefined for holes would obviously be incorrect,
    1972             :     // because we would have to lookup a property on the prototype instead.
    1973             :     do {
    1974             :         // The first two checks are also relevant to the receiver object.
    1975          64 :         if (!allowIndexedReceiver && obj->isIndexed())
    1976             :             return false;
    1977          29 :         allowIndexedReceiver = false;
    1978             : 
    1979          58 :         if (ClassCanHaveExtraProperties(obj->getClass()))
    1980             :             return false;
    1981             : 
    1982             :         // Don't need to check prototype for OwnProperty checks
    1983          29 :         if (ownProp)
    1984             :             return true;
    1985             : 
    1986          29 :         JSObject* proto = obj->staticPrototype();
    1987          29 :         if (!proto)
    1988             :             break;
    1989             : 
    1990          19 :         if (!proto->isNative())
    1991             :             return false;
    1992             : 
    1993             :         // Make sure objects on the prototype don't have dense elements.
    1994          38 :         if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
    1995             :             return false;
    1996             : 
    1997          19 :         obj = &proto->as<NativeObject>();
    1998             :     } while (true);
    1999             : 
    2000             :     return true;
    2001             : }
    2002             : 
    2003             : bool
    2004          13 : GetPropIRGenerator::tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
    2005             :                                               uint32_t index, Int32OperandId indexId)
    2006             : {
    2007          26 :     if (!obj->isNative())
    2008             :         return false;
    2009             : 
    2010          13 :     NativeObject* nobj = &obj->as<NativeObject>();
    2011          13 :     if (nobj->containsDenseElement(index))
    2012             :         return false;
    2013           0 :     if (!CanAttachDenseElementHole(nobj, false))
    2014             :         return false;
    2015             : 
    2016             :     // Guard on the shape, to prevent non-dense elements from appearing.
    2017           0 :     TestMatchingNativeReceiver(writer, nobj, objId);
    2018           0 :     GeneratePrototypeHoleGuards(writer, nobj, objId);
    2019          10 :     writer.loadDenseElementHoleResult(objId, indexId);
    2020          20 :     writer.typeMonitorResult();
    2021             : 
    2022           0 :     trackAttached("DenseElementHole");
    2023          10 :     return true;
    2024             : }
    2025             : 
    2026             : static bool
    2027           0 : IsPrimitiveArrayTypedObject(JSObject* obj)
    2028             : {
    2029         147 :     if (!obj->is<TypedObject>())
    2030             :         return false;
    2031           0 :     TypeDescr& descr = obj->as<TypedObject>().typeDescr();
    2032           0 :     return descr.is<ArrayTypeDescr>() &&
    2033           0 :            descr.as<ArrayTypeDescr>().elementType().is<ScalarTypeDescr>();
    2034             : }
    2035             : 
    2036             : static Scalar::Type
    2037           0 : PrimitiveArrayTypedObjectType(JSObject* obj)
    2038             : {
    2039           0 :     MOZ_ASSERT(IsPrimitiveArrayTypedObject(obj));
    2040           0 :     TypeDescr& descr = obj->as<TypedObject>().typeDescr();
    2041           0 :     return descr.as<ArrayTypeDescr>().elementType().as<ScalarTypeDescr>().type();
    2042             : }
    2043             : 
    2044             : 
    2045             : static Scalar::Type
    2046           0 : TypedThingElementType(JSObject* obj)
    2047             : {
    2048           0 :     return obj->is<TypedArrayObject>()
    2049           0 :            ? obj->as<TypedArrayObject>().type()
    2050           0 :            : PrimitiveArrayTypedObjectType(obj);
    2051             : }
    2052             : 
    2053             : static bool
    2054             : TypedThingRequiresFloatingPoint(JSObject* obj)
    2055             : {
    2056           0 :     Scalar::Type type = TypedThingElementType(obj);
    2057             :     return type == Scalar::Uint32 ||
    2058           0 :            type == Scalar::Float32 ||
    2059             :            type == Scalar::Float64;
    2060             : }
    2061             : 
    2062             : bool
    2063         141 : GetPropIRGenerator::tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
    2064             :                                           uint32_t index, Int32OperandId indexId)
    2065             : {
    2066         423 :     if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
    2067             :         return false;
    2068             : 
    2069           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
    2070             :         return false;
    2071             : 
    2072             :     // Ensure the index is in-bounds so the element type gets monitored.
    2073           0 :     if (obj->is<TypedArrayObject>() && index >= obj->as<TypedArrayObject>().length())
    2074             :         return false;
    2075             : 
    2076             :     // Don't attach typed object stubs if the underlying storage could be
    2077             :     // detached, as the stub will always bail out.
    2078           0 :     if (IsPrimitiveArrayTypedObject(obj) && cx_->zone()->detachedTypedObjects)
    2079             :         return false;
    2080             : 
    2081           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    2082             : 
    2083           0 :     if (IsPrimitiveArrayTypedObject(obj)) {
    2084           0 :         writer.guardNoDetachedTypedObjects();
    2085           0 :         writer.guardGroupForLayout(objId, obj->group());
    2086             :     } else {
    2087           0 :         writer.guardShapeForClass(objId, obj->as<TypedArrayObject>().shape());
    2088             :     }
    2089             : 
    2090           0 :     writer.loadTypedElementResult(objId, indexId, layout, TypedThingElementType(obj));
    2091             : 
    2092             :     // Reading from Uint32Array may produce an int32 now but a double value
    2093             :     // later, so ensure we monitor the result.
    2094           0 :     if (TypedThingElementType(obj) == Scalar::Type::Uint32)
    2095           0 :         writer.typeMonitorResult();
    2096             :     else
    2097           0 :         writer.returnFromIC();
    2098             : 
    2099           0 :     trackAttached("TypedElement");
    2100           0 :     return true;
    2101             : }
    2102             : 
    2103             : bool
    2104           3 : GetPropIRGenerator::tryAttachUnboxedElementHole(HandleObject obj, ObjOperandId objId,
    2105             :                                                 uint32_t index, Int32OperandId indexId)
    2106             : {
    2107           0 :     if (!obj->is<UnboxedPlainObject>())
    2108             :         return false;
    2109             : 
    2110             :     // Only support unboxed objects with no elements (i.e. no expando)
    2111           0 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    2112           0 :     if (expando)
    2113             :         return false;
    2114             : 
    2115           0 :     if (JSObject* proto = obj->staticPrototype()) {
    2116             :         // Start the check at the first object on the [[Prototype]],
    2117             :         // which must be native now.
    2118           0 :         if (!proto->isNative())
    2119             :             return false;
    2120             : 
    2121           0 :         if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
    2122             :             return false;
    2123             : 
    2124           0 :         if (!CanAttachDenseElementHole(&proto->as<NativeObject>(), false))
    2125             :             return false;
    2126             :     }
    2127             : 
    2128             :     // Guard on the group and prevent expandos from appearing.
    2129           0 :     Maybe<ObjOperandId> tempId;
    2130           0 :     TestMatchingReceiver(writer, obj, objId, &tempId);
    2131             : 
    2132             :     // Guard that the prototype chain has no elements.
    2133           0 :     GeneratePrototypeHoleGuards(writer, obj, objId);
    2134             : 
    2135           0 :     writer.loadUndefinedResult();
    2136             :     // No monitor: We know undefined must be in the typeset already.
    2137           0 :     writer.returnFromIC();
    2138             : 
    2139           0 :     trackAttached("UnboxedElementHole");
    2140           0 :     return true;
    2141             : }
    2142             : 
    2143             : bool
    2144         148 : GetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId)
    2145             : {
    2146         296 :     if (!obj->is<ProxyObject>())
    2147             :         return false;
    2148             : 
    2149             :     // The proxy stubs don't currently support |super| access.
    2150          14 :     if (isSuper())
    2151             :         return false;
    2152             : 
    2153           7 :     writer.guardIsProxy(objId);
    2154             : 
    2155             :     // We are not guarding against DOM proxies here, because there is no other
    2156             :     // specialized DOM IC we could attach.
    2157             :     // We could call maybeEmitIdGuard here and then emit CallProxyGetResult,
    2158             :     // but for GetElem we prefer to attach a stub that can handle any Value
    2159             :     // so we don't attach a new stub for every id.
    2160           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
    2161           0 :     MOZ_ASSERT(!isSuper());
    2162           7 :     writer.callProxyGetByValueResult(objId, getElemKeyValueId());
    2163          14 :     writer.typeMonitorResult();
    2164             : 
    2165           0 :     trackAttached("ProxyElement");
    2166           7 :     return true;
    2167             : }
    2168             : 
    2169             : void
    2170           1 : GetPropIRGenerator::trackAttached(const char* name)
    2171             : {
    2172             : #ifdef JS_CACHEIR_SPEW
    2173           0 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    2174           0 :         sp.valueProperty("base", val_);
    2175           0 :         sp.valueProperty("property", idVal_);
    2176             :     }
    2177             : #endif
    2178           0 : }
    2179             : 
    2180             : void
    2181        1794 : IRGenerator::emitIdGuard(ValOperandId valId, jsid id)
    2182             : {
    2183           0 :     if (JSID_IS_SYMBOL(id)) {
    2184           0 :         SymbolOperandId symId = writer.guardIsSymbol(valId);
    2185         291 :         writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
    2186             :     } else {
    2187        1503 :         MOZ_ASSERT(JSID_IS_ATOM(id));
    2188        1503 :         StringOperandId strId = writer.guardIsString(valId);
    2189           0 :         writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
    2190             :     }
    2191           0 : }
    2192             : 
    2193             : void
    2194        6865 : GetPropIRGenerator::maybeEmitIdGuard(jsid id)
    2195             : {
    2196        6865 :     if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
    2197             :         // Constant PropertyName, no guards necessary.
    2198           0 :         MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
    2199             :         return;
    2200             :     }
    2201             : 
    2202           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
    2203         991 :     emitIdGuard(getElemKeyValueId(), id);
    2204             : }
    2205             : 
    2206             : void
    2207        1441 : SetPropIRGenerator::maybeEmitIdGuard(jsid id)
    2208             : {
    2209        1441 :     if (cacheKind_ == CacheKind::SetProp) {
    2210             :         // Constant PropertyName, no guards necessary.
    2211           0 :         MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
    2212             :         return;
    2213             :     }
    2214             : 
    2215         331 :     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    2216           0 :     emitIdGuard(setElemKeyValueId(), id);
    2217             : }
    2218             : 
    2219           0 : GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    2220             :                                        ICState::Mode mode, HandleObject env,
    2221        3302 :                                        HandlePropertyName name)
    2222             :   : IRGenerator(cx, script, pc, CacheKind::GetName, mode),
    2223             :     env_(env),
    2224        6604 :     name_(name)
    2225           0 : {}
    2226             : 
    2227             : bool
    2228        3302 : GetNameIRGenerator::tryAttachStub()
    2229             : {
    2230           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
    2231             : 
    2232           0 :     AutoAssertNoPendingException aanpe(cx_);
    2233             : 
    2234           0 :     ObjOperandId envId(writer.setInputOperandId(0));
    2235       13208 :     RootedId id(cx_, NameToId(name_));
    2236             : 
    2237        3302 :     if (tryAttachGlobalNameValue(envId, id))
    2238             :         return true;
    2239           0 :     if (tryAttachGlobalNameGetter(envId, id))
    2240             :         return true;
    2241        3251 :     if (tryAttachEnvironmentName(envId, id))
    2242             :         return true;
    2243             : 
    2244           0 :     trackAttached(IRGenerator::NotAttached);
    2245        3192 :     return false;
    2246             : }
    2247             : 
    2248             : bool
    2249          63 : CanAttachGlobalName(JSContext* cx, Handle<LexicalEnvironmentObject*> globalLexical, HandleId id,
    2250             :                     MutableHandleNativeObject holder, MutableHandleShape shape)
    2251             : {
    2252             :     // The property must be found, and it must be found as a normal data property.
    2253         126 :     RootedNativeObject current(cx, globalLexical);
    2254             :     while (true) {
    2255           0 :         shape.set(current->lookup(cx, id));
    2256         129 :         if (shape)
    2257             :             break;
    2258             : 
    2259          66 :         if (current == globalLexical) {
    2260         124 :             current = &globalLexical->global();
    2261             :         } else {
    2262             :             // In the browser the global prototype chain should be immutable.
    2263           4 :             if (!current->staticPrototypeIsImmutable())
    2264             :                 return false;
    2265             : 
    2266           4 :             JSObject* proto = current->staticPrototype();
    2267           8 :             if (!proto || !proto->is<NativeObject>())
    2268             :                 return false;
    2269             : 
    2270           0 :             current = &proto->as<NativeObject>();
    2271             :         }
    2272             :     }
    2273             : 
    2274           0 :     holder.set(current);
    2275          63 :     return true;
    2276             : }
    2277             : 
    2278             : bool
    2279           0 : GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
    2280             : {
    2281        9898 :     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    2282             :         return false;
    2283             : 
    2284           0 :     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
    2285          51 :     MOZ_ASSERT(globalLexical->isGlobal());
    2286             : 
    2287         153 :     RootedNativeObject holder(cx_);
    2288           0 :     RootedShape shape(cx_);
    2289         102 :     if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
    2290             :         return false;
    2291             : 
    2292             :     // The property must be found, and it must be found as a normal data property.
    2293          51 :     if (!shape->isDataProperty())
    2294             :         return false;
    2295             : 
    2296             :     // This might still be an uninitialized lexical.
    2297           0 :     if (holder->getSlot(shape->slot()).isMagic())
    2298             :         return false;
    2299             : 
    2300             :     // Instantiate this global property, for use during Ion compilation.
    2301          39 :     if (IsIonEnabled(cx_))
    2302           0 :         EnsureTrackPropertyTypes(cx_, holder, id);
    2303             : 
    2304          39 :     if (holder == globalLexical) {
    2305             :         // There is no need to guard on the shape. Lexical bindings are
    2306             :         // non-configurable, and this stub cannot be shared across globals.
    2307           2 :         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
    2308           1 :         writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
    2309             :     } else {
    2310             :         // Check the prototype chain from the global to the holder
    2311             :         // prototype. Ignore the global lexical scope as it doesn't figure
    2312             :         // into the prototype chain. We guard on the global lexical
    2313             :         // scope's shape independently.
    2314         114 :         if (!IsCacheableGetPropReadSlot(&globalLexical->global(), holder, PropertyResult(shape)))
    2315           0 :             return false;
    2316             : 
    2317             :         // Shape guard for global lexical.
    2318          38 :         writer.guardShape(objId, globalLexical->lastProperty());
    2319             : 
    2320             :         // Guard on the shape of the GlobalObject.
    2321          38 :         ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
    2322           0 :         writer.guardShape(globalId, globalLexical->global().lastProperty());
    2323             : 
    2324          38 :         ObjOperandId holderId = globalId;
    2325          76 :         if (holder != &globalLexical->global()) {
    2326             :             // Shape guard holder.
    2327           0 :             holderId = writer.loadObject(holder);
    2328           0 :             writer.guardShape(holderId, holder->lastProperty());
    2329             :         }
    2330             : 
    2331           0 :         EmitLoadSlotResult(writer, holderId, holder, shape);
    2332             :     }
    2333             : 
    2334          78 :     writer.typeMonitorResult();
    2335             : 
    2336           0 :     trackAttached("GlobalNameValue");
    2337          39 :     return true;
    2338             : }
    2339             : 
    2340             : bool
    2341           0 : GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id)
    2342             : {
    2343        9742 :     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    2344             :         return false;
    2345             : 
    2346           0 :     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
    2347          12 :     MOZ_ASSERT(globalLexical->isGlobal());
    2348             : 
    2349           0 :     RootedNativeObject holder(cx_);
    2350          36 :     RootedShape shape(cx_);
    2351          24 :     if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
    2352             :         return false;
    2353             : 
    2354          12 :     if (holder == globalLexical)
    2355             :         return false;
    2356             : 
    2357          36 :     if (!IsCacheableGetPropCallNative(&globalLexical->global(), holder, shape))
    2358             :         return false;
    2359             : 
    2360          12 :     if (IsIonEnabled(cx_))
    2361          24 :         EnsureTrackPropertyTypes(cx_, holder, id);
    2362             : 
    2363             :     // Shape guard for global lexical.
    2364          12 :     writer.guardShape(objId, globalLexical->lastProperty());
    2365             : 
    2366             :     // Guard on the shape of the GlobalObject.
    2367           0 :     ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
    2368           0 :     writer.guardShape(globalId, globalLexical->global().lastProperty());
    2369             : 
    2370          24 :     if (holder != &globalLexical->global()) {
    2371             :         // Shape guard holder.
    2372           1 :         ObjOperandId holderId = writer.loadObject(holder);
    2373           0 :         writer.guardShape(holderId, holder->lastProperty());
    2374             :     }
    2375             : 
    2376          36 :     EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
    2377             : 
    2378           0 :     trackAttached("GlobalNameGetter");
    2379          12 :     return true;
    2380             : }
    2381             : 
    2382             : static bool
    2383         146 : NeedEnvironmentShapeGuard(JSObject* envObj)
    2384             : {
    2385         146 :     if (!envObj->is<CallObject>())
    2386             :         return true;
    2387             : 
    2388             :     // We can skip a guard on the call object if the script's bindings are
    2389             :     // guaranteed to be immutable (and thus cannot introduce shadowing
    2390             :     // variables). The function might have been relazified under rare
    2391             :     // conditions. In that case, we pessimistically create the guard.
    2392           0 :     CallObject* callObj = &envObj->as<CallObject>();
    2393          34 :     JSFunction* fun = &callObj->callee();
    2394          68 :     if (!fun->hasScript() || fun->nonLazyScript()->funHasExtensibleScope())
    2395             :         return true;
    2396             : 
    2397          11 :     return false;
    2398             : }
    2399             : 
    2400             : bool
    2401           0 : GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
    2402             : {
    2403           0 :     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    2404             :         return false;
    2405             : 
    2406           0 :     RootedObject env(cx_, env_);
    2407           0 :     RootedShape shape(cx_);
    2408           0 :     RootedNativeObject holder(cx_);
    2409             : 
    2410         233 :     while (env) {
    2411         292 :         if (env->is<GlobalObject>()) {
    2412          69 :             shape = env->as<GlobalObject>().lookup(cx_, id);
    2413           0 :             if (shape)
    2414             :                 break;
    2415             :             return false;
    2416             :         }
    2417             : 
    2418         369 :         if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>())
    2419             :             return false;
    2420             : 
    2421           0 :         MOZ_ASSERT(!env->hasUncacheableProto());
    2422             : 
    2423             :         // Check for an 'own' property on the env. There is no need to
    2424             :         // check the prototype as non-with scopes do not inherit properties
    2425             :         // from any prototype.
    2426         369 :         shape = env->as<NativeObject>().lookup(cx_, id);
    2427         123 :         if (shape)
    2428             :             break;
    2429             : 
    2430         174 :         env = env->enclosingEnvironment();
    2431             :     }
    2432             : 
    2433         118 :     holder = &env->as<NativeObject>();
    2434           0 :     if (!IsCacheableGetPropReadSlot(holder, holder, PropertyResult(shape)))
    2435             :         return false;
    2436           0 :     if (holder->getSlot(shape->slot()).isMagic())
    2437             :         return false;
    2438             : 
    2439          59 :     ObjOperandId lastObjId = objId;
    2440           0 :     env = env_;
    2441         233 :     while (env) {
    2442         146 :         if (NeedEnvironmentShapeGuard(env))
    2443           0 :             writer.guardShape(lastObjId, env->maybeShape());
    2444             : 
    2445         146 :         if (env == holder)
    2446             :             break;
    2447             : 
    2448           0 :         lastObjId = writer.loadEnclosingEnvironment(lastObjId);
    2449         174 :         env = env->enclosingEnvironment();
    2450             :     }
    2451             : 
    2452         177 :     if (holder->isFixedSlot(shape->slot())) {
    2453           0 :         writer.loadEnvironmentFixedSlotResult(lastObjId, NativeObject::getFixedSlotOffset(shape->slot()));
    2454             :     } else {
    2455           0 :         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
    2456           0 :         writer.loadEnvironmentDynamicSlotResult(lastObjId, dynamicSlotOffset);
    2457             :     }
    2458         118 :     writer.typeMonitorResult();
    2459             : 
    2460           0 :     trackAttached("EnvironmentName");
    2461          59 :     return true;
    2462             : }
    2463             : 
    2464             : void
    2465           1 : GetNameIRGenerator::trackAttached(const char* name)
    2466             : {
    2467             : #ifdef JS_CACHEIR_SPEW
    2468           0 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    2469           0 :         sp.valueProperty("base", ObjectValue(*env_));
    2470           0 :         sp.valueProperty("property", StringValue(name_));
    2471             :     }
    2472             : #endif
    2473        3302 : }
    2474             : 
    2475           0 : BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    2476             :                                          ICState::Mode mode, HandleObject env,
    2477          45 :                                          HandlePropertyName name)
    2478             :   : IRGenerator(cx, script, pc, CacheKind::BindName, mode),
    2479             :     env_(env),
    2480          90 :     name_(name)
    2481           0 : {}
    2482             : 
    2483             : bool
    2484          45 : BindNameIRGenerator::tryAttachStub()
    2485             : {
    2486           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
    2487             : 
    2488           0 :     AutoAssertNoPendingException aanpe(cx_);
    2489             : 
    2490           0 :     ObjOperandId envId(writer.setInputOperandId(0));
    2491         180 :     RootedId id(cx_, NameToId(name_));
    2492             : 
    2493           0 :     if (tryAttachGlobalName(envId, id))
    2494             :         return true;
    2495          45 :     if (tryAttachEnvironmentName(envId, id))
    2496             :         return true;
    2497             : 
    2498           0 :     trackAttached(IRGenerator::NotAttached);
    2499          45 :     return false;
    2500             : }
    2501             : 
    2502             : bool
    2503           0 : BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId, HandleId id)
    2504             : {
    2505         180 :     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    2506             :         return false;
    2507             : 
    2508           0 :     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
    2509           0 :     MOZ_ASSERT(globalLexical->isGlobal());
    2510             : 
    2511           0 :     JSObject* result = nullptr;
    2512           0 :     if (Shape* shape = globalLexical->lookup(cx_, id)) {
    2513             :         // If this is an uninitialized lexical or a const, we need to return a
    2514             :         // RuntimeLexicalErrorObject.
    2515           0 :         if (globalLexical->getSlot(shape->slot()).isMagic() || !shape->writable())
    2516             :             return false;
    2517           0 :         result = globalLexical;
    2518             :     } else {
    2519           0 :         result = &globalLexical->global();
    2520             :     }
    2521             : 
    2522           0 :     if (result == globalLexical) {
    2523             :         // Lexical bindings are non-configurable so we can just return the
    2524             :         // global lexical.
    2525           0 :         writer.loadObjectResult(objId);
    2526             :     } else {
    2527             :         // If the property exists on the global and is non-configurable, it cannot be
    2528             :         // shadowed by the lexical scope so we can just return the global without a
    2529             :         // shape guard.
    2530           0 :         Shape* shape = result->as<GlobalObject>().lookup(cx_, id);
    2531           0 :         if (!shape || shape->configurable())
    2532           0 :             writer.guardShape(objId, globalLexical->lastProperty());
    2533           0 :         ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
    2534           0 :         writer.loadObjectResult(globalId);
    2535             :     }
    2536           0 :     writer.returnFromIC();
    2537             : 
    2538           0 :     trackAttached("GlobalName");
    2539           0 :     return true;
    2540             : }
    2541             : 
    2542             : bool
    2543           0 : BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
    2544             : {
    2545          90 :     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    2546             :         return false;
    2547             : 
    2548           0 :     RootedObject env(cx_, env_);
    2549           0 :     RootedShape shape(cx_);
    2550             :     while (true) {
    2551           0 :         if (!env->is<GlobalObject>() && !env->is<EnvironmentObject>())
    2552             :             return false;
    2553           0 :         if (env->is<WithEnvironmentObject>())
    2554             :             return false;
    2555             : 
    2556           0 :         MOZ_ASSERT(!env->hasUncacheableProto());
    2557             : 
    2558             :         // When we reach an unqualified variables object (like the global) we
    2559             :         // have to stop looking and return that object.
    2560           0 :         if (env->isUnqualifiedVarObj())
    2561             :             break;
    2562             : 
    2563             :         // Check for an 'own' property on the env. There is no need to
    2564             :         // check the prototype as non-with scopes do not inherit properties
    2565             :         // from any prototype.
    2566           0 :         shape = env->as<NativeObject>().lookup(cx_, id);
    2567           0 :         if (shape)
    2568             :             break;
    2569             : 
    2570           0 :         env = env->enclosingEnvironment();
    2571             :     }
    2572             : 
    2573             :     // If this is an uninitialized lexical or a const, we need to return a
    2574             :     // RuntimeLexicalErrorObject.
    2575           0 :     RootedNativeObject holder(cx_, &env->as<NativeObject>());
    2576           0 :     if (shape &&
    2577           0 :         holder->is<EnvironmentObject>() &&
    2578           0 :         (holder->getSlot(shape->slot()).isMagic() || !shape->writable()))
    2579             :     {
    2580             :         return false;
    2581             :     }
    2582             : 
    2583           0 :     ObjOperandId lastObjId = objId;
    2584           0 :     env = env_;
    2585           0 :     while (env) {
    2586           0 :         if (NeedEnvironmentShapeGuard(env) && !env->is<GlobalObject>())
    2587           0 :             writer.guardShape(lastObjId, env->maybeShape());
    2588             : 
    2589           0 :         if (env == holder)
    2590             :             break;
    2591             : 
    2592           0 :         lastObjId = writer.loadEnclosingEnvironment(lastObjId);
    2593           0 :         env = env->enclosingEnvironment();
    2594             :     }
    2595           0 :     writer.loadObjectResult(lastObjId);
    2596           0 :     writer.returnFromIC();
    2597             : 
    2598           0 :     trackAttached("EnvironmentName");
    2599           0 :     return true;
    2600             : }
    2601             : 
    2602             : void
    2603           1 : BindNameIRGenerator::trackAttached(const char* name)
    2604             : {
    2605             : #ifdef JS_CACHEIR_SPEW
    2606           0 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    2607           0 :         sp.valueProperty("base", ObjectValue(*env_));
    2608           0 :         sp.valueProperty("property", StringValue(name_));
    2609             :     }
    2610             : #endif
    2611          45 : }
    2612             : 
    2613           0 : HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    2614             :                                        CacheKind cacheKind, ICState::Mode mode,
    2615         598 :                                        HandleValue idVal, HandleValue val)
    2616             :   : IRGenerator(cx, script, pc, cacheKind, mode),
    2617             :     val_(val),
    2618        1196 :     idVal_(idVal)
    2619         598 : { }
    2620             : 
    2621             : bool
    2622          17 : HasPropIRGenerator::tryAttachDense(HandleObject obj, ObjOperandId objId,
    2623             :                                    uint32_t index, Int32OperandId indexId)
    2624             : {
    2625          34 :     if (!obj->isNative())
    2626             :         return false;
    2627             : 
    2628           0 :     NativeObject* nobj = &obj->as<NativeObject>();
    2629           0 :     if (!nobj->containsDenseElement(index))
    2630             :         return false;
    2631             : 
    2632             :     // Guard shape to ensure object class is NativeObject.
    2633           0 :     TestMatchingNativeReceiver(writer, nobj, objId);
    2634          17 :     writer.loadDenseElementExistsResult(objId, indexId);
    2635          34 :     writer.returnFromIC();
    2636             : 
    2637           0 :     trackAttached("DenseHasProp");
    2638          17 :     return true;
    2639             : }
    2640             : 
    2641             : bool
    2642           0 : HasPropIRGenerator::tryAttachDenseHole(HandleObject obj, ObjOperandId objId,
    2643             :                                        uint32_t index, Int32OperandId indexId)
    2644             : {
    2645           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2646             : 
    2647           0 :     if (!obj->isNative())
    2648             :         return false;
    2649             : 
    2650           0 :     NativeObject* nobj = &obj->as<NativeObject>();
    2651           0 :     if (nobj->containsDenseElement(index))
    2652             :         return false;
    2653           0 :     if (!CanAttachDenseElementHole(nobj, hasOwn))
    2654             :         return false;
    2655             : 
    2656             :     // Guard shape to ensure class is NativeObject and to prevent non-dense
    2657             :     // elements being added. Also ensures prototype doesn't change if dynamic
    2658             :     // checks aren't emitted.
    2659           0 :     TestMatchingNativeReceiver(writer, nobj, objId);
    2660             : 
    2661             :     // Generate prototype guards if needed. This includes monitoring that
    2662             :     // properties were not added in the chain.
    2663           0 :     if (!hasOwn)
    2664           0 :         GeneratePrototypeHoleGuards(writer, nobj, objId);
    2665             : 
    2666           0 :     writer.loadDenseElementHoleExistsResult(objId, indexId);
    2667           0 :     writer.returnFromIC();
    2668             : 
    2669           0 :     trackAttached("DenseHasPropHole");
    2670           0 :     return true;
    2671             : }
    2672             : 
    2673             : bool
    2674           0 : HasPropIRGenerator::tryAttachSparse(HandleObject obj, ObjOperandId objId,
    2675             :                                     Int32OperandId indexId)
    2676             : {
    2677           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2678             : 
    2679           0 :     if (!obj->isNative())
    2680             :         return false;
    2681           0 :     if (!obj->as<NativeObject>().isIndexed())
    2682             :         return false;
    2683           0 :     if (!CanAttachDenseElementHole(&obj->as<NativeObject>(), hasOwn,
    2684             :         /* allowIndexedReceiver = */ true))
    2685             :     {
    2686             :         return false;
    2687             :     }
    2688             : 
    2689             :     // Guard that this is a native object.
    2690           0 :     writer.guardIsNativeObject(objId);
    2691             : 
    2692             :     // Generate prototype guards if needed. This includes monitoring that
    2693             :     // properties were not added in the chain.
    2694           0 :     if (!hasOwn) {
    2695             :         // If GeneratePrototypeHoleGuards below won't add guards for prototype,
    2696             :         // we should add our own since we aren't guarding shape.
    2697           0 :         if (!obj->hasUncacheableProto())
    2698           0 :             GuardGroupProto(writer, obj, objId);
    2699             : 
    2700           0 :         GeneratePrototypeHoleGuards(writer, obj, objId);
    2701             :     }
    2702             : 
    2703             :     // Because of the prototype guard we know that the prototype chain
    2704             :     // does not include any dense or sparse (i.e indexed) properties.
    2705           0 :     writer.callObjectHasSparseElementResult(objId, indexId);
    2706           0 :     writer.returnFromIC();
    2707             : 
    2708           0 :     trackAttached("Sparse");
    2709           0 :     return true;
    2710             : }
    2711             : 
    2712             : 
    2713             : bool
    2714           0 : HasPropIRGenerator::tryAttachNamedProp(HandleObject obj, ObjOperandId objId,
    2715             :                                        HandleId key, ValOperandId keyId)
    2716             : {
    2717           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2718             : 
    2719         533 :     JSObject* holder = nullptr;
    2720         533 :     PropertyResult prop;
    2721             : 
    2722         533 :     if (hasOwn) {
    2723           0 :         if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
    2724             :             return false;
    2725             : 
    2726           0 :         holder = obj;
    2727             :     } else {
    2728        1038 :         if (!LookupPropertyPure(cx_, obj, key, &holder, &prop))
    2729             :             return false;
    2730             :     }
    2731           0 :     if (!prop)
    2732             :         return false;
    2733             : 
    2734         212 :     if (tryAttachMegamorphic(objId, keyId))
    2735             :         return true;
    2736         561 :     if (tryAttachNative(obj, objId, key, keyId, prop, holder))
    2737             :         return true;
    2738          26 :     if (tryAttachUnboxed(obj, objId, key, keyId))
    2739             :         return true;
    2740           0 :     if (tryAttachTypedObject(obj, objId, key, keyId))
    2741             :         return true;
    2742           0 :     if (tryAttachUnboxedExpando(obj, objId, key, keyId))
    2743             :         return true;
    2744             : 
    2745           0 :     return false;
    2746             : }
    2747             : 
    2748             : bool
    2749         528 : HasPropIRGenerator::tryAttachMegamorphic(ObjOperandId objId, ValOperandId keyId)
    2750             : {
    2751           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2752             : 
    2753           0 :     if (mode_ != ICState::Mode::Megamorphic)
    2754             :         return false;
    2755             : 
    2756          56 :     writer.megamorphicHasPropResult(objId, keyId, hasOwn);
    2757         112 :     writer.returnFromIC();
    2758           0 :     trackAttached("MegamorphicHasProp");
    2759          56 :     return true;
    2760             : }
    2761             : 
    2762             : bool
    2763         187 : HasPropIRGenerator::tryAttachNative(JSObject* obj, ObjOperandId objId, jsid key,
    2764             :                                     ValOperandId keyId, PropertyResult prop, JSObject* holder)
    2765             : {
    2766         187 :     if (!prop.isNativeProperty())
    2767             :         return false;
    2768             : 
    2769           0 :     if (!IsCacheableProtoChain(obj, holder))
    2770             :         return false;
    2771             : 
    2772         174 :     Maybe<ObjOperandId> tempId;
    2773           0 :     emitIdGuard(keyId, key);
    2774           0 :     EmitReadSlotGuard(writer, obj, holder, objId, &tempId);
    2775         348 :     writer.loadBooleanResult(true);
    2776         348 :     writer.returnFromIC();
    2777             : 
    2778           0 :     trackAttached("NativeHasProp");
    2779         174 :     return true;
    2780             : }
    2781             : 
    2782             : bool
    2783          13 : HasPropIRGenerator::tryAttachUnboxed(JSObject* obj, ObjOperandId objId,
    2784             :                                      jsid key, ValOperandId keyId)
    2785             : {
    2786          13 :     if (!obj->is<UnboxedPlainObject>())
    2787             :         return false;
    2788             : 
    2789           0 :     const UnboxedLayout::Property* prop = obj->as<UnboxedPlainObject>().layout().lookup(key);
    2790           0 :     if (!prop)
    2791             :         return false;
    2792             : 
    2793           0 :     emitIdGuard(keyId, key);
    2794           0 :     writer.guardGroupForLayout(objId, obj->group());
    2795          26 :     writer.loadBooleanResult(true);
    2796          26 :     writer.returnFromIC();
    2797             : 
    2798           0 :     trackAttached("UnboxedHasProp");
    2799          13 :     return true;
    2800             : }
    2801             : 
    2802             : bool
    2803           0 : HasPropIRGenerator::tryAttachUnboxedExpando(JSObject* obj, ObjOperandId objId,
    2804             :                                             jsid key, ValOperandId keyId)
    2805             : {
    2806           0 :     if (!obj->is<UnboxedPlainObject>())
    2807             :         return false;
    2808             : 
    2809           0 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    2810           0 :     if (!expando)
    2811             :         return false;
    2812             : 
    2813           0 :     Shape* shape = expando->lookup(cx_, key);
    2814           0 :     if (!shape)
    2815             :         return false;
    2816             : 
    2817           0 :     Maybe<ObjOperandId> tempId;
    2818           0 :     emitIdGuard(keyId, key);
    2819           0 :     EmitReadSlotGuard(writer, obj, obj, objId, &tempId);
    2820           0 :     writer.loadBooleanResult(true);
    2821           0 :     writer.returnFromIC();
    2822             : 
    2823           0 :     trackAttached("UnboxedExpandoHasProp");
    2824           0 :     return true;
    2825             : }
    2826             : 
    2827             : bool
    2828           0 : HasPropIRGenerator::tryAttachTypedArray(HandleObject obj, ObjOperandId objId,
    2829             :                                         Int32OperandId indexId)
    2830             : {
    2831           0 :     if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
    2832             :         return false;
    2833             : 
    2834           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    2835             : 
    2836           0 :     if (IsPrimitiveArrayTypedObject(obj))
    2837           0 :         writer.guardGroupForLayout(objId, obj->group());
    2838             :     else
    2839           0 :         writer.guardShapeForClass(objId, obj->as<TypedArrayObject>().shape());
    2840             : 
    2841           0 :     writer.loadTypedElementExistsResult(objId, indexId, layout);
    2842             : 
    2843           0 :     writer.returnFromIC();
    2844             : 
    2845           0 :     trackAttached("TypedArrayObject");
    2846           0 :     return true;
    2847             : }
    2848             : 
    2849             : bool
    2850           0 : HasPropIRGenerator::tryAttachTypedObject(JSObject* obj, ObjOperandId objId,
    2851             :                                          jsid key, ValOperandId keyId)
    2852             : {
    2853           0 :     if (!obj->is<TypedObject>())
    2854             :         return false;
    2855             : 
    2856           0 :     if (!obj->as<TypedObject>().typeDescr().hasProperty(cx_->names(), key))
    2857             :         return false;
    2858             : 
    2859           0 :     emitIdGuard(keyId, key);
    2860           0 :     writer.guardGroupForLayout(objId, obj->group());
    2861           0 :     writer.loadBooleanResult(true);
    2862           0 :     writer.returnFromIC();
    2863             : 
    2864           0 :     trackAttached("TypedObjectHasProp");
    2865           0 :     return true;
    2866             : }
    2867             : 
    2868             : bool
    2869           0 : HasPropIRGenerator::tryAttachSlotDoesNotExist(JSObject* obj, ObjOperandId objId,
    2870             :                                               jsid key, ValOperandId keyId)
    2871             : {
    2872           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2873             : 
    2874           0 :     emitIdGuard(keyId, key);
    2875           0 :     if (hasOwn) {
    2876          18 :         Maybe<ObjOperandId> tempId;
    2877           0 :         TestMatchingReceiver(writer, obj, objId, &tempId);
    2878             :     } else {
    2879         552 :         Maybe<ObjOperandId> tempId;
    2880           0 :         EmitReadSlotGuard(writer, obj, nullptr, objId, &tempId);
    2881             :     }
    2882         570 :     writer.loadBooleanResult(false);
    2883         570 :     writer.returnFromIC();
    2884             : 
    2885           0 :     trackAttached("DoesNotExist");
    2886         285 :     return true;
    2887             : }
    2888             : 
    2889             : bool
    2890         321 : HasPropIRGenerator::tryAttachDoesNotExist(HandleObject obj, ObjOperandId objId,
    2891             :                                           HandleId key, ValOperandId keyId)
    2892             : {
    2893           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2894             : 
    2895             :     // Check that property doesn't exist on |obj| or it's prototype chain. These
    2896             :     // checks allow Native/Unboxed/Typed objects with a NativeObject prototype
    2897             :     // chain. They return false if unknown such as resolve hooks or proxies.
    2898         321 :     if (hasOwn) {
    2899          22 :         if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
    2900             :             return false;
    2901             :     } else {
    2902         620 :         if (!CheckHasNoSuchProperty(cx_, obj, key))
    2903             :             return false;
    2904             :     }
    2905             : 
    2906           1 :     if (tryAttachMegamorphic(objId, keyId))
    2907             :         return true;
    2908         570 :     if (tryAttachSlotDoesNotExist(obj, objId, key, keyId))
    2909             :         return true;
    2910             : 
    2911           0 :     return false;
    2912             : }
    2913             : 
    2914             : bool
    2915           0 : HasPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
    2916             :                                           ValOperandId keyId)
    2917             : {
    2918           0 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2919             : 
    2920           0 :     if (!obj->is<ProxyObject>())
    2921             :         return false;
    2922             : 
    2923           0 :     writer.guardIsProxy(objId);
    2924           3 :     writer.callProxyHasPropResult(objId, keyId, hasOwn);
    2925           6 :     writer.returnFromIC();
    2926             : 
    2927           0 :     trackAttached("ProxyHasProp");
    2928           3 :     return true;
    2929             : }
    2930             : 
    2931             : bool
    2932           0 : HasPropIRGenerator::tryAttachStub()
    2933             : {
    2934         598 :     MOZ_ASSERT(cacheKind_ == CacheKind::In ||
    2935             :                cacheKind_ == CacheKind::HasOwn);
    2936             : 
    2937        1794 :     AutoAssertNoPendingException aanpe(cx_);
    2938             : 
    2939             :     // NOTE: Argument order is PROPERTY, OBJECT
    2940           0 :     ValOperandId keyId(writer.setInputOperandId(0));
    2941        1196 :     ValOperandId valId(writer.setInputOperandId(1));
    2942             : 
    2943           0 :     if (!val_.isObject()) {
    2944          45 :         trackAttached(IRGenerator::NotAttached);
    2945          45 :         return false;
    2946             :     }
    2947        1659 :     RootedObject obj(cx_, &val_.toObject());
    2948         553 :     ObjOperandId objId = writer.guardIsObject(valId);
    2949             : 
    2950             :     // Optimize Proxies
    2951           0 :     if (tryAttachProxyElement(obj, objId, keyId))
    2952             :         return true;
    2953             : 
    2954        1650 :     RootedId id(cx_);
    2955             :     bool nameOrSymbol;
    2956           0 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    2957           0 :         cx_->clearPendingException();
    2958           0 :         return false;
    2959             :     }
    2960             : 
    2961         550 :     if (nameOrSymbol) {
    2962           0 :         if (tryAttachNamedProp(obj, objId, id, keyId))
    2963             :             return true;
    2964         642 :         if (tryAttachDoesNotExist(obj, objId, id, keyId))
    2965             :             return true;
    2966             : 
    2967           0 :         trackAttached(IRGenerator::NotAttached);
    2968           0 :         return false;
    2969             :     }
    2970             : 
    2971             :     uint32_t index;
    2972          17 :     Int32OperandId indexId;
    2973           0 :     if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
    2974          34 :         if (tryAttachDense(obj, objId, index, indexId))
    2975             :             return true;
    2976           0 :         if (tryAttachDenseHole(obj, objId, index, indexId))
    2977             :             return true;
    2978           0 :         if (tryAttachTypedArray(obj, objId, indexId))
    2979             :             return true;
    2980           0 :         if (tryAttachSparse(obj, objId, indexId))
    2981             :             return true;
    2982             : 
    2983           0 :         trackAttached(IRGenerator::NotAttached);
    2984           0 :         return false;
    2985             :     }
    2986             : 
    2987           0 :     trackAttached(IRGenerator::NotAttached);
    2988           0 :     return false;
    2989             : }
    2990             : 
    2991             : void
    2992           1 : HasPropIRGenerator::trackAttached(const char* name)
    2993             : {
    2994             : #ifdef JS_CACHEIR_SPEW
    2995           0 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    2996           0 :         sp.valueProperty("base", val_);
    2997           0 :         sp.valueProperty("property", idVal_);
    2998             :     }
    2999             : #endif
    3000         598 : }
    3001             : 
    3002             : bool
    3003           0 : IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
    3004             :                                   uint32_t* int32Index, Int32OperandId* int32IndexId)
    3005             : {
    3006         401 :     if (index.isNumber()) {
    3007             :         int32_t indexSigned;
    3008         394 :         if (index.isInt32()) {
    3009           0 :             indexSigned = index.toInt32();
    3010             :         } else {
    3011             :             // We allow negative zero here.
    3012           0 :             if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned))
    3013             :                 return false;
    3014           0 :             if (!cx_->runtime()->jitSupportsFloatingPoint)
    3015             :                 return false;
    3016             :         }
    3017             : 
    3018           0 :         if (indexSigned < 0)
    3019             :             return false;
    3020             : 
    3021           0 :         *int32Index = uint32_t(indexSigned);
    3022           0 :         *int32IndexId = writer.guardIsInt32Index(indexId);
    3023           0 :         return true;
    3024             :     }
    3025             : 
    3026           0 :     if (index.isString()) {
    3027           0 :         int32_t indexSigned = GetIndexFromString(index.toString());
    3028           0 :         if (indexSigned < 0)
    3029             :             return false;
    3030             : 
    3031           2 :         StringOperandId strId = writer.guardIsString(indexId);
    3032           2 :         *int32Index = uint32_t(indexSigned);
    3033           2 :         *int32IndexId = writer.guardAndGetIndexFromString(strId);
    3034           2 :         return true;
    3035             :     }
    3036             : 
    3037             :     return false;
    3038             : }
    3039             : 
    3040        2984 : SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    3041             :                                        CacheKind cacheKind, ICState::Mode mode,
    3042             :                                        bool* isTemporarilyUnoptimizable,
    3043             :                                        HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal,
    3044        2984 :                                        bool needsTypeBarrier, bool maybeHasExtraIndexedProps)
    3045             :   : IRGenerator(cx, script, pc, cacheKind, mode),
    3046             :     lhsVal_(lhsVal),
    3047             :     idVal_(idVal),
    3048             :     rhsVal_(rhsVal),
    3049             :     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
    3050             :     typeCheckInfo_(cx, needsTypeBarrier),
    3051             :     preliminaryObjectAction_(PreliminaryObjectAction::None),
    3052             :     attachedTypedArrayOOBStub_(false),
    3053        8952 :     maybeHasExtraIndexedProps_(maybeHasExtraIndexedProps)
    3054           0 : {}
    3055             : 
    3056             : bool
    3057           0 : SetPropIRGenerator::tryAttachStub()
    3058             : {
    3059           0 :     AutoAssertNoPendingException aanpe(cx_);
    3060             : 
    3061           0 :     ValOperandId objValId(writer.setInputOperandId(0));
    3062           0 :     ValOperandId rhsValId;
    3063           0 :     if (cacheKind_ == CacheKind::SetProp) {
    3064           0 :         rhsValId = ValOperandId(writer.setInputOperandId(1));
    3065             :     } else {
    3066         651 :         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    3067           0 :         MOZ_ASSERT(setElemKeyValueId().id() == 1);
    3068         651 :         writer.setInputOperandId(1);
    3069           0 :         rhsValId = ValOperandId(writer.setInputOperandId(2));
    3070             :     }
    3071             : 
    3072        5787 :     RootedId id(cx_);
    3073             :     bool nameOrSymbol;
    3074           0 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    3075           0 :         cx_->clearPendingException();
    3076           0 :         return false;
    3077             :     }
    3078             : 
    3079           0 :     if (lhsVal_.isObject()) {
    3080        5787 :         RootedObject obj(cx_, &lhsVal_.toObject());
    3081             : 
    3082           0 :         ObjOperandId objId = writer.guardIsObject(objValId);
    3083           0 :         if (IsPropertySetOp(JSOp(*pc_))) {
    3084        1053 :             if (tryAttachMegamorphicSetElement(obj, objId, rhsValId))
    3085             :                 return true;
    3086             :         }
    3087           0 :         if (nameOrSymbol) {
    3088        3298 :             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
    3089             :                 return true;
    3090        2260 :             if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
    3091             :                 return true;
    3092           0 :             if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
    3093             :                 return true;
    3094           0 :             if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
    3095             :                 return true;
    3096           0 :             if (IsPropertySetOp(JSOp(*pc_))) {
    3097        1570 :                 if (tryAttachSetArrayLength(obj, objId, id, rhsValId))
    3098             :                     return true;
    3099        1564 :                 if (tryAttachSetter(obj, objId, id, rhsValId))
    3100             :                     return true;
    3101        1544 :                 if (tryAttachWindowProxy(obj, objId, id, rhsValId))
    3102             :                     return true;
    3103        1544 :                 if (tryAttachProxy(obj, objId, id, rhsValId))
    3104             :                     return true;
    3105             :             }
    3106             :             return false;
    3107             :         }
    3108             : 
    3109         486 :         if (IsPropertySetOp(JSOp(*pc_))) {
    3110           0 :             if (tryAttachProxyElement(obj, objId, rhsValId))
    3111             :                 return true;
    3112             :         }
    3113             : 
    3114             :         uint32_t index;
    3115         243 :         Int32OperandId indexId;
    3116           0 :         if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
    3117         476 :             if (tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId))
    3118             :                 return true;
    3119         420 :             if (tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId))
    3120             :                 return true;
    3121          12 :             if (tryAttachSetTypedElement(obj, objId, index, indexId, rhsValId))
    3122             :                 return true;
    3123           6 :             return false;
    3124             :         }
    3125             :         return false;
    3126             :     }
    3127             :     return false;
    3128             : }
    3129             : 
    3130             : static void
    3131           0 : EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId, NativeObject* nobj, Shape* shape,
    3132             :                        ValOperandId rhsId)
    3133             : {
    3134           0 :     if (nobj->isFixedSlot(shape->slot())) {
    3135         916 :         size_t offset = NativeObject::getFixedSlotOffset(shape->slot());
    3136           0 :         writer.storeFixedSlot(objId, offset, rhsId);
    3137             :     } else {
    3138          60 :         size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value);
    3139          60 :         writer.storeDynamicSlot(objId, offset, rhsId);
    3140             :     }
    3141         518 :     writer.returnFromIC();
    3142           0 : }
    3143             : 
    3144             : static Shape*
    3145        1537 : LookupShapeForSetSlot(JSOp op, NativeObject* obj, jsid id)
    3146             : {
    3147        1537 :     Shape* shape = obj->lookupPure(id);
    3148        1537 :     if (!shape || !shape->isDataProperty() || !shape->writable())
    3149             :         return nullptr;
    3150             : 
    3151             :     // If this is an op like JSOP_INITELEM / [[DefineOwnProperty]], the
    3152             :     // property's attributes may have to be changed too, so make sure it's a
    3153             :     // simple data property.
    3154         608 :     if (IsPropertyInitOp(op) && (!shape->configurable() || !shape->enumerable()))
    3155             :         return nullptr;
    3156             : 
    3157         608 :     return shape;
    3158             : }
    3159             : 
    3160             : static bool
    3161        1649 : CanAttachNativeSetSlot(JSContext* cx, JSOp op, HandleObject obj, HandleId id,
    3162             :                        bool* isTemporarilyUnoptimizable, MutableHandleShape propShape)
    3163             : {
    3164        3298 :     if (!obj->isNative())
    3165             :         return false;
    3166             : 
    3167           0 :     propShape.set(LookupShapeForSetSlot(op, &obj->as<NativeObject>(), id));
    3168           1 :     if (!propShape)
    3169             :         return false;
    3170             : 
    3171         608 :     ObjectGroup* group = JSObject::getGroup(cx, obj);
    3172         608 :     if (!group) {
    3173           0 :         cx->recoverFromOutOfMemory();
    3174           0 :         return false;
    3175             :     }
    3176             : 
    3177             :     // For some property writes, such as the initial overwrite of global
    3178             :     // properties, TI will not mark the property as having been
    3179             :     // overwritten. Don't attach a stub in this case, so that we don't
    3180             :     // execute another write to the property without TI seeing that write.
    3181        1216 :     EnsureTrackPropertyTypes(cx, obj, id);
    3182        1216 :     if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
    3183          89 :         *isTemporarilyUnoptimizable = true;
    3184          89 :         return false;
    3185             :     }
    3186             : 
    3187             :     return true;
    3188             : }
    3189             : 
    3190             : bool
    3191        1649 : SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
    3192             :                                            ValOperandId rhsId)
    3193             : {
    3194           0 :     RootedShape propShape(cx_);
    3195           0 :     if (!CanAttachNativeSetSlot(cx_, JSOp(*pc_), obj, id, isTemporarilyUnoptimizable_, &propShape))
    3196             :         return false;
    3197             : 
    3198           0 :     if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp) {
    3199           3 :         writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId,
    3200           2 :                                     typeCheckInfo_.needsTypeBarrier());
    3201           0 :         writer.returnFromIC();
    3202           1 :         trackAttached("MegamorphicNativeSlot");
    3203           1 :         return true;
    3204             :     }
    3205             : 
    3206           0 :     maybeEmitIdGuard(id);
    3207             : 
    3208             :     // If we need a property type barrier (always in Baseline, sometimes in
    3209             :     // Ion), guard on both the shape and the group. If Ion knows the property
    3210             :     // types match, we don't need the group guard.
    3211           0 :     NativeObject* nobj = &obj->as<NativeObject>();
    3212           0 :     if (typeCheckInfo_.needsTypeBarrier())
    3213         518 :         writer.guardGroupForTypeBarrier(objId, nobj->group());
    3214           0 :     TestMatchingNativeReceiver(writer, nobj, objId);
    3215             : 
    3216           0 :     if (IsPreliminaryObject(obj))
    3217           0 :         preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
    3218             :     else
    3219           0 :         preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    3220             : 
    3221         518 :     typeCheckInfo_.set(nobj->group(), id);
    3222         518 :     EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId);
    3223             : 
    3224           0 :     trackAttached("NativeSlot");
    3225         518 :     return true;
    3226             : }
    3227             : 
    3228             : bool
    3229        1130 : SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId,
    3230             :                                                    HandleId id, ValOperandId rhsId)
    3231             : {
    3232        2260 :     if (!obj->is<UnboxedPlainObject>())
    3233             :         return false;
    3234             : 
    3235           0 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    3236          99 :     if (!expando)
    3237             :         return false;
    3238             : 
    3239           0 :     Shape* propShape = LookupShapeForSetSlot(JSOp(*pc_), expando, id);
    3240           0 :     if (!propShape)
    3241             :         return false;
    3242             : 
    3243           0 :     maybeEmitIdGuard(id);
    3244             : 
    3245           0 :     Maybe<ObjOperandId> expandoId;
    3246           0 :     TestMatchingReceiver(writer, obj, objId, &expandoId);
    3247             : 
    3248             :     // Property types must be added to the unboxed object's group, not the
    3249             :     // expando's group (it has unknown properties).
    3250           0 :     typeCheckInfo_.set(obj->group(), id);
    3251           0 :     EmitStoreSlotAndReturn(writer, expandoId.ref(), expando, propShape, rhsId);
    3252             : 
    3253           0 :     trackAttached("UnboxedExpando");
    3254           0 :     return true;
    3255             : }
    3256             : 
    3257             : static void
    3258          67 : EmitGuardUnboxedPropertyType(CacheIRWriter& writer, JSValueType propType, ValOperandId valId)
    3259             : {
    3260          67 :     if (propType == JSVAL_TYPE_OBJECT) {
    3261             :         // Unboxed objects store NullValue as nullptr object.
    3262          33 :         writer.guardIsObjectOrNull(valId);
    3263             :     } else {
    3264           0 :         writer.guardType(valId, propType);
    3265             :     }
    3266          67 : }
    3267             : 
    3268             : bool
    3269        1130 : SetPropIRGenerator::tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
    3270             :                                              ValOperandId rhsId)
    3271             : {
    3272        2260 :     if (!obj->is<UnboxedPlainObject>())
    3273             :         return false;
    3274             : 
    3275         297 :     if (!cx_->runtime()->jitSupportsFloatingPoint)
    3276             :         return false;
    3277             : 
    3278           0 :     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
    3279           0 :     if (!property)
    3280             :         return false;
    3281             : 
    3282           0 :     maybeEmitIdGuard(id);
    3283           0 :     writer.guardGroupForLayout(objId, obj->group());
    3284          67 :     EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
    3285           0 :     writer.storeUnboxedProperty(objId, property->type,
    3286           0 :                                 UnboxedPlainObject::offsetOfData() + property->offset,
    3287          67 :                                 rhsId);
    3288           0 :     writer.returnFromIC();
    3289             : 
    3290         134 :     typeCheckInfo_.set(obj->group(), id);
    3291          67 :     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    3292             : 
    3293           0 :     trackAttached("Unboxed");
    3294          67 :     return true;
    3295             : }
    3296             : 
    3297             : bool
    3298        1063 : SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
    3299             :                                                  ValOperandId rhsId)
    3300             : {
    3301        2126 :     if (!obj->is<TypedObject>())
    3302             :         return false;
    3303             : 
    3304           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint || cx_->zone()->detachedTypedObjects)
    3305             :         return false;
    3306             : 
    3307           0 :     if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>())
    3308             :         return false;
    3309             : 
    3310           0 :     StructTypeDescr* structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>();
    3311             :     size_t fieldIndex;
    3312           0 :     if (!structDescr->fieldIndex(id, &fieldIndex))
    3313             :         return false;
    3314             : 
    3315           0 :     TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
    3316           0 :     if (!fieldDescr->is<SimpleTypeDescr>())
    3317             :         return false;
    3318             : 
    3319           0 :     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
    3320           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    3321             : 
    3322           0 :     maybeEmitIdGuard(id);
    3323           0 :     writer.guardNoDetachedTypedObjects();
    3324           0 :     writer.guardGroupForLayout(objId, obj->group());
    3325             : 
    3326           0 :     typeCheckInfo_.set(obj->group(), id);
    3327             : 
    3328             :     // Scalar types can always be stored without a type update stub.
    3329           0 :     if (fieldDescr->is<ScalarTypeDescr>()) {
    3330           0 :         Scalar::Type type = fieldDescr->as<ScalarTypeDescr>().type();
    3331           0 :         writer.storeTypedObjectScalarProperty(objId, fieldOffset, layout, type, rhsId);
    3332           0 :         writer.returnFromIC();
    3333             : 
    3334           0 :         trackAttached("TypedObject");
    3335           0 :         return true;
    3336             :     }
    3337             : 
    3338             :     // For reference types, guard on the RHS type first, so that
    3339             :     // StoreTypedObjectReferenceProperty is infallible.
    3340           0 :     ReferenceTypeDescr::Type type = fieldDescr->as<ReferenceTypeDescr>().type();
    3341           0 :     switch (type) {
    3342             :       case ReferenceTypeDescr::TYPE_ANY:
    3343             :         break;
    3344             :       case ReferenceTypeDescr::TYPE_OBJECT:
    3345           0 :         writer.guardIsObjectOrNull(rhsId);
    3346           0 :         break;
    3347             :       case ReferenceTypeDescr::TYPE_STRING:
    3348           0 :         writer.guardType(rhsId, JSVAL_TYPE_STRING);
    3349           0 :         break;
    3350             :     }
    3351             : 
    3352           0 :     writer.storeTypedObjectReferenceProperty(objId, fieldOffset, layout, type, rhsId);
    3353           0 :     writer.returnFromIC();
    3354             : 
    3355           0 :     trackAttached("TypedObject");
    3356           0 :     return true;
    3357             : }
    3358             : 
    3359             : void
    3360           1 : SetPropIRGenerator::trackAttached(const char* name)
    3361             : {
    3362             : #ifdef JS_CACHEIR_SPEW
    3363        5790 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    3364           0 :         sp.valueProperty("base", lhsVal_);
    3365           0 :         sp.valueProperty("property", idVal_);
    3366           0 :         sp.valueProperty("value", rhsVal_);
    3367             :     }
    3368             : #endif
    3369           0 : }
    3370             : 
    3371             : static bool
    3372           0 : IsCacheableSetPropCallNative(JSObject* obj, JSObject* holder, Shape* shape)
    3373             : {
    3374         758 :     if (!shape || !IsCacheableProtoChain(obj, holder))
    3375             :         return false;
    3376             : 
    3377          44 :     if (!shape->hasSetterValue())
    3378             :         return false;
    3379             : 
    3380          28 :     if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
    3381             :         return false;
    3382             : 
    3383          14 :     JSFunction& setter = shape->setterObject()->as<JSFunction>();
    3384          14 :     if (!setter.isNativeWithCppEntry())
    3385             :         return false;
    3386             : 
    3387           8 :     if (setter.isClassConstructor())
    3388             :         return false;
    3389             : 
    3390          16 :     if (setter.hasJitInfo() && !setter.jitInfo()->needsOuterizedThisObject())
    3391             :         return true;
    3392             : 
    3393           0 :     return !IsWindow(obj);
    3394             : }
    3395             : 
    3396             : static bool
    3397         760 : IsCacheableSetPropCallScripted(JSObject* obj, JSObject* holder, Shape* shape,
    3398             :                                bool* isTemporarilyUnoptimizable = nullptr)
    3399             : {
    3400         760 :     if (!shape || !IsCacheableProtoChain(obj, holder))
    3401             :         return false;
    3402             : 
    3403          46 :     if (IsWindow(obj))
    3404             :         return false;
    3405             : 
    3406          46 :     if (!shape->hasSetterValue())
    3407             :         return false;
    3408             : 
    3409          32 :     if (!shape->setterObject() || !shape->setterObject()->is<JSFunction>())
    3410             :         return false;
    3411             : 
    3412           0 :     JSFunction& setter = shape->setterObject()->as<JSFunction>();
    3413          16 :     if (setter.isNativeWithCppEntry())
    3414             :         return false;
    3415             : 
    3416             :     // Natives with jit entry can use the scripted path.
    3417           0 :     if (setter.isNativeWithJitEntry())
    3418             :         return true;
    3419             : 
    3420          12 :     if (!setter.hasScript()) {
    3421           0 :         if (isTemporarilyUnoptimizable)
    3422           0 :             *isTemporarilyUnoptimizable = true;
    3423             :         return false;
    3424             :     }
    3425             : 
    3426          12 :     if (setter.isClassConstructor())
    3427             :         return false;
    3428             : 
    3429          12 :     return true;
    3430             : }
    3431             : 
    3432             : static bool
    3433           0 : CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
    3434             :                 MutableHandleObject holder, MutableHandleShape propShape,
    3435             :                 bool* isTemporarilyUnoptimizable)
    3436             : {
    3437             :     // Don't attach a setter stub for ops like JSOP_INITELEM.
    3438        1564 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
    3439             : 
    3440         782 :     PropertyResult prop;
    3441        2346 :     if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
    3442             :         return false;
    3443             : 
    3444           0 :     if (prop.isNonNativeProperty())
    3445             :         return false;
    3446             : 
    3447         754 :     propShape.set(prop.maybeShape());
    3448        3010 :     if (!IsCacheableSetPropCallScripted(obj, holder, propShape, isTemporarilyUnoptimizable) &&
    3449        2244 :         !IsCacheableSetPropCallNative(obj, holder, propShape))
    3450             :     {
    3451             :         return false;
    3452             :     }
    3453             : 
    3454             :     return true;
    3455             : }
    3456             : 
    3457             : static void
    3458           0 : EmitCallSetterNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
    3459             :                        Shape* shape, ObjOperandId objId, ValOperandId rhsId)
    3460             : {
    3461          10 :     if (IsCacheableSetPropCallNative(obj, holder, shape)) {
    3462           4 :         JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
    3463           4 :         MOZ_ASSERT(target->isNativeWithCppEntry());
    3464           0 :         writer.callNativeSetter(objId, target, rhsId);
    3465             :         writer.returnFromIC();
    3466             :         return;
    3467             :     }
    3468             : 
    3469           6 :     MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
    3470             : 
    3471           6 :     JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
    3472           6 :     MOZ_ASSERT(target->hasJitEntry());
    3473           0 :     writer.callScriptedSetter(objId, target, rhsId);
    3474             :     writer.returnFromIC();
    3475             : }
    3476             : 
    3477             : bool
    3478           0 : SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
    3479             :                                     ValOperandId rhsId)
    3480             : {
    3481           0 :     RootedObject holder(cx_);
    3482        2346 :     RootedShape propShape(cx_);
    3483        2346 :     if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &propShape, isTemporarilyUnoptimizable_))
    3484             :         return false;
    3485             : 
    3486           0 :     maybeEmitIdGuard(id);
    3487             : 
    3488             :     // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
    3489             :     // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
    3490             :     // require outerizing).
    3491           0 :     if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
    3492          20 :         Maybe<ObjOperandId> expandoId;
    3493          10 :         TestMatchingReceiver(writer, obj, objId, &expandoId);
    3494             : 
    3495           0 :         if (obj != holder) {
    3496          20 :             GeneratePrototypeGuards(writer, obj, holder, objId);
    3497             : 
    3498             :             // Guard on the holder's shape.
    3499          10 :             ObjOperandId holderId = writer.loadObject(holder);
    3500          10 :             TestMatchingHolder(writer, holder, holderId);
    3501             :         }
    3502             :     } else {
    3503           0 :         writer.guardHasGetterSetter(objId, propShape);
    3504             :     }
    3505             : 
    3506          30 :     EmitCallSetterNoGuards(writer, obj, holder, propShape, objId, rhsId);
    3507             : 
    3508           0 :     trackAttached("Setter");
    3509          10 :     return true;
    3510             : }
    3511             : 
    3512             : bool
    3513         785 : SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
    3514             :                                             ValOperandId rhsId)
    3515             : {
    3516             :     // Don't attach an array length stub for ops like JSOP_INITELEM.
    3517        1570 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
    3518             : 
    3519        2358 :     if (!obj->is<ArrayObject>() ||
    3520         800 :         !JSID_IS_ATOM(id, cx_->names().length) ||
    3521           0 :         !obj->as<ArrayObject>().lengthIsWritable())
    3522             :     {
    3523             :         return false;
    3524             :     }
    3525             : 
    3526           0 :     maybeEmitIdGuard(id);
    3527           0 :     writer.guardClass(objId, GuardClassKind::Array);
    3528           6 :     writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
    3529           6 :     writer.returnFromIC();
    3530             : 
    3531           0 :     trackAttached("SetArrayLength");
    3532           3 :     return true;
    3533             : }
    3534             : 
    3535             : bool
    3536         238 : SetPropIRGenerator::tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
    3537             :                                              Int32OperandId indexId, ValOperandId rhsId)
    3538             : {
    3539         476 :     if (!obj->isNative())
    3540             :         return false;
    3541             : 
    3542           0 :     NativeObject* nobj = &obj->as<NativeObject>();
    3543           0 :     if (!nobj->containsDenseElement(index) || nobj->getElementsHeader()->isFrozen())
    3544             :         return false;
    3545             : 
    3546           0 :     if (typeCheckInfo_.needsTypeBarrier())
    3547          28 :         writer.guardGroupForTypeBarrier(objId, nobj->group());
    3548          56 :     TestMatchingNativeReceiver(writer, nobj, objId);
    3549             : 
    3550          28 :     writer.storeDenseElement(objId, indexId, rhsId);
    3551           0 :     writer.returnFromIC();
    3552             : 
    3553             :     // Type inference uses JSID_VOID for the element types.
    3554          28 :     typeCheckInfo_.set(nobj->group(), JSID_VOID);
    3555             : 
    3556           0 :     trackAttached("SetDenseElement");
    3557          28 :     return true;
    3558             : }
    3559             : 
    3560             : static bool
    3561         261 : CanAttachAddElement(NativeObject* obj, bool isInit)
    3562             : {
    3563             :     // Make sure the objects on the prototype don't have any indexed properties
    3564             :     // or that such properties can't appear without a shape change.
    3565             :     do {
    3566             :         // The first two checks are also relevant to the receiver object.
    3567           0 :         if (obj->isIndexed())
    3568             :             return false;
    3569             : 
    3570         760 :         const Class* clasp = obj->getClass();
    3571         821 :         if (clasp != &ArrayObject::class_ &&
    3572          61 :             (clasp->getAddProperty() ||
    3573          61 :              clasp->getResolve() ||
    3574          61 :              clasp->getOpsLookupProperty() ||
    3575             :              clasp->getOpsSetProperty()))
    3576             :         {
    3577             :             return false;
    3578             :         }
    3579             : 
    3580             :         // If we're initializing a property instead of setting one, the objects
    3581             :         // on the prototype are not relevant.
    3582         380 :         if (isInit)
    3583             :             break;
    3584             : 
    3585         179 :         JSObject* proto = obj->staticPrototype();
    3586         179 :         if (!proto)
    3587             :             break;
    3588             : 
    3589         119 :         if (!proto->isNative())
    3590             :             return false;
    3591             : 
    3592             :         // We have to make sure the proto has no non-writable (frozen) elements
    3593             :         // because we're not allowed to shadow them. There are a few cases to
    3594             :         // consider:
    3595             :         //
    3596             :         // * If the proto is extensible, its Shape will change when it's made
    3597             :         //   non-extensible.
    3598             :         //
    3599             :         // * If the proto is already non-extensible, no new elements will be
    3600             :         //   added, so if there are no elements now it doesn't matter if the
    3601             :         //   object is frozen later on.
    3602         119 :         NativeObject* nproto = &proto->as<NativeObject>();
    3603         119 :         if (!nproto->isExtensible() && nproto->getDenseInitializedLength() > 0)
    3604             :             return false;
    3605             : 
    3606             :         obj = nproto;
    3607             :     } while (true);
    3608             : 
    3609             :     return true;
    3610             : }
    3611             : 
    3612             : bool
    3613         210 : SetPropIRGenerator::tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId,
    3614             :                                                  uint32_t index, Int32OperandId indexId,
    3615             :                                                  ValOperandId rhsId)
    3616             : {
    3617         630 :     if (!obj->isNative() || rhsVal_.isMagic(JS_ELEMENTS_HOLE))
    3618             :         return false;
    3619             : 
    3620         210 :     JSOp op = JSOp(*pc_);
    3621           0 :     MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
    3622             : 
    3623         210 :     if (op == JSOP_INITHIDDENELEM)
    3624             :         return false;
    3625             : 
    3626         210 :     NativeObject* nobj = &obj->as<NativeObject>();
    3627         210 :     if (!nobj->isExtensible())
    3628             :         return false;
    3629             : 
    3630         420 :     MOZ_ASSERT(!nobj->getElementsHeader()->isFrozen(),
    3631             :                "Extensible objects should not have frozen elements");
    3632             : 
    3633           0 :     uint32_t initLength = nobj->getDenseInitializedLength();
    3634             : 
    3635             :     // Optimize if we're adding an element at initLength or writing to a hole.
    3636             :     // Don't handle the adding case if the current accesss is in bounds, to
    3637             :     // ensure we always call noteArrayWriteHole.
    3638         210 :     bool isAdd = index == initLength;
    3639           0 :     bool isHoleInBounds = index < initLength && !nobj->containsDenseElement(index);
    3640         210 :     if (!isAdd && !isHoleInBounds)
    3641             :         return false;
    3642             : 
    3643             :     // Can't add new elements to arrays with non-writable length.
    3644         610 :     if (isAdd && nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
    3645             :         return false;
    3646             : 
    3647             :     // Typed arrays don't have dense elements.
    3648         408 :     if (nobj->is<TypedArrayObject>())
    3649             :         return false;
    3650             : 
    3651             :     // Check for other indexed properties or class hooks.
    3652           0 :     if (!CanAttachAddElement(nobj, IsPropertyInitOp(op)))
    3653             :         return false;
    3654             : 
    3655         204 :     if (typeCheckInfo_.needsTypeBarrier())
    3656           0 :         writer.guardGroupForTypeBarrier(objId, nobj->group());
    3657           0 :     TestMatchingNativeReceiver(writer, nobj, objId);
    3658             : 
    3659             :     // Also shape guard the proto chain, unless this is an INITELEM or we know
    3660             :     // the proto chain has no indexed props.
    3661         204 :     if (IsPropertySetOp(op) && maybeHasExtraIndexedProps_)
    3662           3 :         ShapeGuardProtoChain(writer, obj, objId);
    3663             : 
    3664         204 :     writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
    3665           0 :     writer.returnFromIC();
    3666             : 
    3667             :     // Type inference uses JSID_VOID for the element types.
    3668         204 :     typeCheckInfo_.set(nobj->group(), JSID_VOID);
    3669             : 
    3670           0 :     trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
    3671         204 :     return true;
    3672             : }
    3673             : 
    3674             : bool
    3675           6 : SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId,
    3676             :                                              uint32_t index, Int32OperandId indexId,
    3677             :                                              ValOperandId rhsId)
    3678             : {
    3679          18 :     if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
    3680             :         return false;
    3681             : 
    3682           0 :     if (!rhsVal_.isNumber())
    3683             :         return false;
    3684             : 
    3685           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
    3686             :         return false;
    3687             : 
    3688           0 :     bool handleOutOfBounds = false;
    3689           0 :     if (obj->is<TypedArrayObject>()) {
    3690           0 :         handleOutOfBounds = (index >= obj->as<TypedArrayObject>().length());
    3691             :     } else {
    3692             :         // Typed objects throw on out of bounds accesses. Don't attach
    3693             :         // a stub in this case.
    3694           0 :         if (index >= obj->as<TypedObject>().length())
    3695             :             return false;
    3696             : 
    3697             :         // Don't attach stubs if the underlying storage for typed objects
    3698             :         // in the zone could be detached, as the stub will always bail out.
    3699           0 :         if (cx_->zone()->detachedTypedObjects)
    3700             :             return false;
    3701             :     }
    3702             : 
    3703           0 :     Scalar::Type elementType = TypedThingElementType(obj);
    3704           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    3705             : 
    3706           0 :     if (IsPrimitiveArrayTypedObject(obj)) {
    3707           0 :         writer.guardNoDetachedTypedObjects();
    3708           0 :         writer.guardGroupForLayout(objId, obj->group());
    3709             :     } else {
    3710           0 :         writer.guardShapeForClass(objId, obj->as<TypedArrayObject>().shape());
    3711             :     }
    3712             : 
    3713           0 :     writer.storeTypedElement(objId, indexId, rhsId, layout, elementType, handleOutOfBounds);
    3714           0 :     writer.returnFromIC();
    3715             : 
    3716           0 :     if (handleOutOfBounds)
    3717           0 :         attachedTypedArrayOOBStub_ = true;
    3718             : 
    3719           0 :     trackAttached(handleOutOfBounds ? "SetTypedElementOOB" : "SetTypedElement");
    3720           0 :     return true;
    3721             : }
    3722             : 
    3723             : bool
    3724           0 : SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    3725             :                                           ValOperandId rhsId, bool handleDOMProxies)
    3726             : {
    3727          14 :     MOZ_ASSERT(obj->is<ProxyObject>());
    3728             : 
    3729           7 :     writer.guardIsProxy(objId);
    3730             : 
    3731           0 :     if (!handleDOMProxies) {
    3732             :         // Ensure that the incoming object is not a DOM proxy, so that we can
    3733             :         // get to the specialized stubs. If handleDOMProxies is true, we were
    3734             :         // unable to attach a specialized DOM stub, so we just handle all
    3735             :         // proxies here.
    3736           0 :         writer.guardNotDOMProxy(objId);
    3737             :     }
    3738             : 
    3739           0 :     if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
    3740           0 :         maybeEmitIdGuard(id);
    3741           0 :         writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
    3742             :     } else {
    3743             :         // Attach a stub that handles every id.
    3744           0 :         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    3745           0 :         MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
    3746           0 :         writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
    3747             :     }
    3748             : 
    3749          14 :     writer.returnFromIC();
    3750             : 
    3751           0 :     trackAttached("GenericProxy");
    3752           7 :     return true;
    3753             : }
    3754             : 
    3755             : bool
    3756           0 : SetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
    3757             :                                               ValOperandId rhsId)
    3758             : {
    3759           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    3760             : 
    3761           0 :     maybeEmitIdGuard(id);
    3762           0 :     TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
    3763           0 :     writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
    3764           0 :     writer.returnFromIC();
    3765             : 
    3766           0 :     trackAttached("DOMProxyShadowed");
    3767           0 :     return true;
    3768             : }
    3769             : 
    3770             : bool
    3771           0 : SetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
    3772             :                                                 ValOperandId rhsId)
    3773             : {
    3774           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    3775             : 
    3776           0 :     RootedObject proto(cx_, obj->staticPrototype());
    3777           0 :     if (!proto)
    3778             :         return false;
    3779             : 
    3780           0 :     RootedObject holder(cx_);
    3781           0 :     RootedShape propShape(cx_);
    3782           0 :     if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &propShape, isTemporarilyUnoptimizable_))
    3783             :         return false;
    3784             : 
    3785           0 :     maybeEmitIdGuard(id);
    3786             : 
    3787             :     // Guard that our expando object hasn't started shadowing this property.
    3788           0 :     TestMatchingProxyReceiver(writer, &obj->as<ProxyObject>(), objId);
    3789           0 :     CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
    3790             : 
    3791           0 :     GeneratePrototypeGuards(writer, obj, holder, objId);
    3792             : 
    3793             :     // Guard on the holder of the property.
    3794           0 :     ObjOperandId holderId = writer.loadObject(holder);
    3795           0 :     TestMatchingHolder(writer, holder, holderId);
    3796             : 
    3797             :     // EmitCallSetterNoGuards expects |obj| to be the object the property is
    3798             :     // on to do some checks. Since we actually looked at proto, and no extra
    3799             :     // guards will be generated, we can just pass that instead.
    3800           0 :     EmitCallSetterNoGuards(writer, proto, holder, propShape, objId, rhsId);
    3801             : 
    3802           0 :     trackAttached("DOMProxyUnshadowed");
    3803           0 :     return true;
    3804             : }
    3805             : 
    3806             : bool
    3807           0 : SetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
    3808             :                                              ValOperandId rhsId)
    3809             : {
    3810           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    3811             : 
    3812           0 :     RootedValue expandoVal(cx_, GetProxyPrivate(obj));
    3813           0 :     RootedObject expandoObj(cx_);
    3814           0 :     if (expandoVal.isObject()) {
    3815           0 :         expandoObj = &expandoVal.toObject();
    3816             :     } else {
    3817           0 :         MOZ_ASSERT(!expandoVal.isUndefined(),
    3818             :                    "How did a missing expando manage to shadow things?");
    3819           0 :         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
    3820           0 :         MOZ_ASSERT(expandoAndGeneration);
    3821           0 :         expandoObj = &expandoAndGeneration->expando.toObject();
    3822             :     }
    3823             : 
    3824           0 :     RootedShape propShape(cx_);
    3825           0 :     if (CanAttachNativeSetSlot(cx_, JSOp(*pc_), expandoObj, id, isTemporarilyUnoptimizable_,
    3826             :                                &propShape))
    3827             :     {
    3828           0 :         maybeEmitIdGuard(id);
    3829             :         ObjOperandId expandoObjId =
    3830           0 :             guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
    3831             : 
    3832           0 :         NativeObject* nativeExpandoObj = &expandoObj->as<NativeObject>();
    3833           0 :         writer.guardGroupForTypeBarrier(expandoObjId, nativeExpandoObj->group());
    3834           0 :         typeCheckInfo_.set(nativeExpandoObj->group(), id);
    3835             : 
    3836           0 :         EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, propShape, rhsId);
    3837           0 :         trackAttached("DOMProxyExpandoSlot");
    3838             :         return true;
    3839             :     }
    3840             : 
    3841           0 :     RootedObject holder(cx_);
    3842           0 :     if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape,
    3843             :                         isTemporarilyUnoptimizable_))
    3844             :     {
    3845             :         // Note that we don't actually use the expandoObjId here after the
    3846             :         // shape guard. The DOM proxy (objId) is passed to the setter as
    3847             :         // |this|.
    3848           0 :         maybeEmitIdGuard(id);
    3849           0 :         guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
    3850             : 
    3851           0 :         MOZ_ASSERT(holder == expandoObj);
    3852           0 :         EmitCallSetterNoGuards(writer, expandoObj, expandoObj, propShape, objId, rhsId);
    3853           0 :         trackAttached("DOMProxyExpandoSetter");
    3854           0 :         return true;
    3855             :     }
    3856             : 
    3857             :     return false;
    3858             : }
    3859             : 
    3860             : bool
    3861         772 : SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    3862             :                                    ValOperandId rhsId)
    3863             : {
    3864             :     // Don't attach a proxy stub for ops like JSOP_INITELEM.
    3865        1544 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
    3866             : 
    3867           0 :     ProxyStubType type = GetProxyStubType(cx_, obj, id);
    3868         772 :     if (type == ProxyStubType::None)
    3869             :         return false;
    3870             : 
    3871           7 :     if (mode_ == ICState::Mode::Megamorphic)
    3872           1 :         return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
    3873             : 
    3874           6 :     switch (type) {
    3875             :       case ProxyStubType::None:
    3876             :         break;
    3877             :       case ProxyStubType::DOMExpando:
    3878           0 :         if (tryAttachDOMProxyExpando(obj, objId, id, rhsId))
    3879             :             return true;
    3880           0 :         if (*isTemporarilyUnoptimizable_) {
    3881             :             // Scripted setter without JIT code. Just wait.
    3882             :             return false;
    3883             :         }
    3884             :         MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
    3885             :       case ProxyStubType::DOMShadowed:
    3886           0 :         return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
    3887             :       case ProxyStubType::DOMUnshadowed:
    3888           0 :         if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
    3889             :             return true;
    3890           0 :         if (*isTemporarilyUnoptimizable_) {
    3891             :             // Scripted setter without JIT code. Just wait.
    3892             :             return false;
    3893             :         }
    3894           0 :         return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
    3895             :       case ProxyStubType::Generic:
    3896           6 :         return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ false);
    3897             :     }
    3898             : 
    3899           0 :     MOZ_CRASH("Unexpected ProxyStubType");
    3900             : }
    3901             : 
    3902             : bool
    3903           0 : SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId)
    3904             : {
    3905             :     // Don't attach a proxy stub for ops like JSOP_INITELEM.
    3906           0 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
    3907             : 
    3908          84 :     if (!obj->is<ProxyObject>())
    3909             :         return false;
    3910             : 
    3911           0 :     writer.guardIsProxy(objId);
    3912             : 
    3913             :     // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
    3914             :     // proxies here as we don't have specialized DOM stubs for this.
    3915           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    3916           0 :     writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
    3917           0 :     writer.returnFromIC();
    3918             : 
    3919           0 :     trackAttached("ProxyElement");
    3920           0 :     return true;
    3921             : }
    3922             : 
    3923             : bool
    3924           0 : SetPropIRGenerator::tryAttachMegamorphicSetElement(HandleObject obj, ObjOperandId objId,
    3925             :                                                    ValOperandId rhsId)
    3926             : {
    3927        2106 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
    3928             : 
    3929        1053 :     if (mode_ != ICState::Mode::Megamorphic || cacheKind_ != CacheKind::SetElem)
    3930             :         return false;
    3931             : 
    3932             :     // The generic proxy stubs are faster.
    3933          74 :     if (obj->is<ProxyObject>())
    3934             :         return false;
    3935             : 
    3936          74 :     writer.megamorphicSetElement(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
    3937          74 :     writer.returnFromIC();
    3938             : 
    3939           0 :     trackAttached("MegamorphicSetElement");
    3940          37 :     return true;
    3941             : }
    3942             : 
    3943             : bool
    3944         772 : SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    3945             :                                          ValOperandId rhsId)
    3946             : {
    3947             :     // Attach a stub when the receiver is a WindowProxy and we can do the set
    3948             :     // on the Window (the global object).
    3949             : 
    3950           0 :     if (!IsWindowProxy(obj))
    3951             :         return false;
    3952             : 
    3953             :     // If we're megamorphic prefer a generic proxy stub that handles a lot more
    3954             :     // cases.
    3955           0 :     if (mode_ == ICState::Mode::Megamorphic)
    3956             :         return false;
    3957             : 
    3958             :     // This must be a WindowProxy for the current Window/global. Else it would
    3959             :     // be a cross-compartment wrapper and IsWindowProxy returns false for
    3960             :     // those.
    3961           0 :     MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
    3962           0 :     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
    3963             : 
    3964             :     // Now try to do the set on the Window (the current global).
    3965           0 :     Handle<GlobalObject*> windowObj = cx_->global();
    3966             : 
    3967           0 :     RootedShape propShape(cx_);
    3968           0 :     if (!CanAttachNativeSetSlot(cx_, JSOp(*pc_), windowObj, id, isTemporarilyUnoptimizable_,
    3969             :                                 &propShape))
    3970             :     {
    3971             :         return false;
    3972             :     }
    3973             : 
    3974           0 :     maybeEmitIdGuard(id);
    3975             : 
    3976           0 :     writer.guardClass(objId, GuardClassKind::WindowProxy);
    3977           0 :     ObjOperandId windowObjId = writer.loadObject(windowObj);
    3978             : 
    3979           0 :     writer.guardShape(windowObjId, windowObj->lastProperty());
    3980           0 :     writer.guardGroupForTypeBarrier(windowObjId, windowObj->group());
    3981           0 :     typeCheckInfo_.set(windowObj->group(), id);
    3982             : 
    3983           0 :     EmitStoreSlotAndReturn(writer, windowObjId, windowObj, propShape, rhsId);
    3984             : 
    3985           0 :     trackAttached("WindowProxySlot");
    3986           0 :     return true;
    3987             : }
    3988             : 
    3989             : bool
    3990           0 : SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
    3991             : {
    3992           0 :     AutoAssertNoPendingException aanpe(cx_);
    3993             : 
    3994           0 :     ValOperandId objValId(writer.setInputOperandId(0));
    3995           0 :     ValOperandId rhsValId;
    3996           0 :     if (cacheKind_ == CacheKind::SetProp) {
    3997           0 :         rhsValId = ValOperandId(writer.setInputOperandId(1));
    3998             :     } else {
    3999         361 :         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    4000           0 :         MOZ_ASSERT(setElemKeyValueId().id() == 1);
    4001         361 :         writer.setInputOperandId(1);
    4002           0 :         rhsValId = ValOperandId(writer.setInputOperandId(2));
    4003             :     }
    4004             : 
    4005        3165 :     RootedId id(cx_);
    4006             :     bool nameOrSymbol;
    4007           0 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    4008           0 :         cx_->clearPendingException();
    4009           0 :         return false;
    4010             :     }
    4011             : 
    4012           0 :     if (!lhsVal_.isObject() || !nameOrSymbol)
    4013             :         return false;
    4014             : 
    4015        3132 :     RootedObject obj(cx_, &lhsVal_.toObject());
    4016             : 
    4017        1044 :     PropertyResult prop;
    4018             :     JSObject* holder;
    4019           0 :     if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
    4020             :         return false;
    4021        2076 :     if (obj != holder)
    4022             :         return false;
    4023             : 
    4024           0 :     Shape* propShape = nullptr;
    4025        1007 :     NativeObject* holderOrExpando = nullptr;
    4026             : 
    4027        2014 :     if (obj->isNative()) {
    4028           0 :         propShape = prop.shape();
    4029           0 :         holderOrExpando = &obj->as<NativeObject>();
    4030             :     } else {
    4031           0 :         if (!obj->is<UnboxedPlainObject>())
    4032             :             return false;
    4033          32 :         UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    4034          32 :         if (!expando)
    4035             :             return false;
    4036          32 :         propShape = expando->lookupPure(id);
    4037           0 :         if (!propShape)
    4038             :             return false;
    4039             :         holderOrExpando = expando;
    4040             :     }
    4041             : 
    4042        1007 :     MOZ_ASSERT(propShape);
    4043             : 
    4044             :     // The property must be the last added property of the object.
    4045           0 :     if (holderOrExpando->lastProperty() != propShape)
    4046             :         return false;
    4047             : 
    4048             :     // Object must be extensible, oldShape must be immediate parent of
    4049             :     // current shape.
    4050           0 :     if (!obj->nonProxyIsExtensible() || propShape->previous() != oldShape)
    4051             :         return false;
    4052             : 
    4053             :     // Basic shape checks.
    4054        2624 :     if (propShape->inDictionary() ||
    4055        1741 :         !propShape->isDataProperty() ||
    4056         862 :         !propShape->writable())
    4057             :     {
    4058             :         return false;
    4059             :     }
    4060             : 
    4061             :     // Watch out for resolve hooks.
    4062        4260 :     if (ClassMayResolveId(cx_->names(), obj->getClass(), id, obj)) {
    4063             :         // The JSFunction resolve hook defines a (non-configurable and
    4064             :         // non-enumerable) |prototype| property on certain functions. Scripts
    4065             :         // often assign a custom |prototype| object and we want to optimize
    4066             :         // this |prototype| set and eliminate the default object allocation.
    4067             :         //
    4068             :         // We check group->maybeInterpretedFunction() here and guard on the
    4069             :         // group. The group is unique for a particular function so this ensures
    4070             :         // we don't add the default prototype property to functions that don't
    4071             :         // have it.
    4072          12 :         if (!obj->is<JSFunction>() ||
    4073          18 :             !JSID_IS_ATOM(id, cx_->names().prototype) ||
    4074           0 :             !oldGroup->maybeInterpretedFunction() ||
    4075           0 :             !obj->as<JSFunction>().needsPrototypeProperty())
    4076             :         {
    4077             :             return false;
    4078             :         }
    4079           6 :         MOZ_ASSERT(!propShape->configurable());
    4080           0 :         MOZ_ASSERT(!propShape->enumerable());
    4081             :     }
    4082             : 
    4083             :     // Also watch out for addProperty hooks. Ignore the Array addProperty hook,
    4084             :     // because it doesn't do anything for non-index properties.
    4085         852 :     DebugOnly<uint32_t> index;
    4086        1704 :     MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
    4087           0 :     if (!obj->is<ArrayObject>() && obj->getClass()->getAddProperty())
    4088             :         return false;
    4089             : 
    4090             :     // Walk up the object prototype chain and ensure that all prototypes are
    4091             :     // native, and that all prototypes have no setter defined on the property.
    4092           0 :     for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
    4093           0 :         if (!proto->isNative())
    4094             :             return false;
    4095             : 
    4096             :         // If prototype defines this property in a non-plain way, don't optimize.
    4097        1642 :         Shape* protoShape = proto->as<NativeObject>().lookup(cx_, id);
    4098        1694 :         if (protoShape && !protoShape->hasDefaultSetter())
    4099             :             return false;
    4100             : 
    4101             :         // Otherwise, if there's no such property, watch out for a resolve hook
    4102             :         // that would need to be invoked and thus prevent inlining of property
    4103             :         // addition. Allow the JSFunction resolve hook as it only defines plain
    4104             :         // data properties and we don't need to invoke it for objects on the
    4105             :         // proto chain.
    4106        4926 :         if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
    4107           3 :             !proto->is<JSFunction>())
    4108             :         {
    4109             :             return false;
    4110             :         }
    4111             :     }
    4112             : 
    4113           0 :     ObjOperandId objId = writer.guardIsObject(objValId);
    4114           0 :     maybeEmitIdGuard(id);
    4115             : 
    4116             :     // In addition to guarding for type barrier, we need this group guard (or
    4117             :     // shape guard below) to ensure class is unchanged.
    4118        2508 :     MOZ_ASSERT(!oldGroup->hasUncacheableClass() || obj->is<ShapedObject>());
    4119        1672 :     writer.guardGroupForTypeBarrier(objId, oldGroup);
    4120             : 
    4121             :     // If we are adding a property to an object for which the new script
    4122             :     // properties analysis hasn't been performed yet, make sure the stub fails
    4123             :     // after we run the analysis as a group change may be required here. The
    4124             :     // group change is not required for correctness but improves type
    4125             :     // information elsewhere.
    4126        1672 :     AutoSweepObjectGroup sweep(oldGroup);
    4127           0 :     if (oldGroup->newScript(sweep) && !oldGroup->newScript(sweep)->analyzed()) {
    4128         570 :         writer.guardGroupHasUnanalyzedNewScript(oldGroup);
    4129         285 :         MOZ_ASSERT(IsPreliminaryObject(obj));
    4130         285 :         preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
    4131             :     } else {
    4132           0 :         preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    4133             :     }
    4134             : 
    4135             :     // Shape guard the holder.
    4136           0 :     ObjOperandId holderId = objId;
    4137        1672 :     if (!obj->isNative()) {
    4138           0 :         MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
    4139           0 :         holderId = writer.guardAndLoadUnboxedExpando(objId);
    4140             :     }
    4141         836 :     writer.guardShape(holderId, oldShape);
    4142             : 
    4143         836 :     ShapeGuardProtoChain(writer, obj, objId);
    4144             : 
    4145         836 :     ObjectGroup* newGroup = obj->group();
    4146             : 
    4147             :     // Check if we have to change the object's group. If we're adding an
    4148             :     // unboxed expando property, we pass the expando object to AddAndStore*Slot.
    4149             :     // That's okay because we only have to do a group change if the object is a
    4150             :     // PlainObject.
    4151           0 :     bool changeGroup = oldGroup != newGroup;
    4152           0 :     MOZ_ASSERT_IF(changeGroup, obj->is<PlainObject>());
    4153             : 
    4154        1672 :     if (holderOrExpando->isFixedSlot(propShape->slot())) {
    4155           0 :         size_t offset = NativeObject::getFixedSlotOffset(propShape->slot());
    4156           0 :         writer.addAndStoreFixedSlot(holderId, offset, rhsValId, propShape,
    4157           0 :                                     changeGroup, newGroup);
    4158           0 :         trackAttached("AddSlot");
    4159             :     } else {
    4160           0 :         size_t offset = holderOrExpando->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
    4161           0 :         uint32_t numOldSlots = NativeObject::dynamicSlotsCount(oldShape);
    4162         452 :         uint32_t numNewSlots = NativeObject::dynamicSlotsCount(propShape);
    4163           0 :         if (numOldSlots == numNewSlots) {
    4164           0 :             writer.addAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
    4165           0 :                                           changeGroup, newGroup);
    4166           0 :             trackAttached("AddSlot");
    4167             :         } else {
    4168         118 :             MOZ_ASSERT(numNewSlots > numOldSlots);
    4169           0 :             writer.allocateAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
    4170         118 :                                                changeGroup, newGroup, numNewSlots);
    4171           0 :             trackAttached("AllocateSlot");
    4172             :         }
    4173             :     }
    4174        1672 :     writer.returnFromIC();
    4175             : 
    4176           0 :     typeCheckInfo_.set(oldGroup, id);
    4177             :     return true;
    4178             : }
    4179             : 
    4180           0 : InstanceOfIRGenerator::InstanceOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    4181         587 :                                             ICState::Mode mode, HandleValue lhs, HandleObject rhs)
    4182             :   : IRGenerator(cx, script, pc, CacheKind::InstanceOf, mode),
    4183             :     lhsVal_(lhs),
    4184        1174 :     rhsObj_(rhs)
    4185           0 : { }
    4186             : 
    4187             : bool
    4188         587 : InstanceOfIRGenerator::tryAttachStub()
    4189             : {
    4190           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::InstanceOf);
    4191           0 :     AutoAssertNoPendingException aanpe(cx_);
    4192             : 
    4193             :     // Ensure RHS is a function -- could be a Proxy, which the IC isn't prepared to handle.
    4194           0 :     if (!rhsObj_->is<JSFunction>()) {
    4195           0 :         trackAttached(IRGenerator::NotAttached);
    4196           0 :         return false;
    4197             :     }
    4198             : 
    4199         587 :     HandleFunction fun = rhsObj_.as<JSFunction>();
    4200             : 
    4201        1174 :     if (fun->isBoundFunction()) {
    4202           0 :         trackAttached(IRGenerator::NotAttached);
    4203           0 :         return false;
    4204             :     }
    4205             : 
    4206             :     // If the user has supplied their own @@hasInstance method we shouldn't
    4207             :     // clobber it.
    4208        1761 :     if (!js::FunctionHasDefaultHasInstance(fun, cx_->wellKnownSymbols())) {
    4209           0 :         trackAttached(IRGenerator::NotAttached);
    4210           0 :         return false;
    4211             :     }
    4212             : 
    4213             :     // Refuse to optimize any function whose [[Prototype]] isn't
    4214             :     // Function.prototype.
    4215           0 :     if (!fun->hasStaticPrototype() || fun->hasUncacheableProto()) {
    4216           0 :         trackAttached(IRGenerator::NotAttached);
    4217           0 :         return false;
    4218             :     }
    4219             : 
    4220        1174 :     Value funProto = cx_->global()->getPrototype(JSProto_Function);
    4221        1174 :     if (!funProto.isObject() || fun->staticPrototype() != &funProto.toObject()) {
    4222           0 :         trackAttached(IRGenerator::NotAttached);
    4223           0 :         return false;
    4224             :     }
    4225             : 
    4226             :     // Ensure that the function's prototype slot is the same.
    4227          84 :     Shape* shape = fun->lookupPure(cx_->names().prototype);
    4228           0 :     if (!shape || !shape->isDataProperty()) {
    4229           0 :         trackAttached(IRGenerator::NotAttached);
    4230           0 :         return false;
    4231             :     }
    4232             : 
    4233           1 :     uint32_t slot = shape->slot();
    4234             : 
    4235          42 :     MOZ_ASSERT(fun->numFixedSlots() == 0, "Stub code relies on this");
    4236           0 :     if (!fun->getSlot(slot).isObject()) {
    4237           0 :         trackAttached(IRGenerator::NotAttached);
    4238           0 :         return false;
    4239             :     }
    4240             : 
    4241          21 :     JSObject* prototypeObject = &fun->getSlot(slot).toObject();
    4242             : 
    4243             :     // Abstract Objects
    4244          42 :     ValOperandId lhs(writer.setInputOperandId(0));
    4245          42 :     ValOperandId rhs(writer.setInputOperandId(1));
    4246             : 
    4247          21 :     ObjOperandId rhsId = writer.guardIsObject(rhs);
    4248           0 :     writer.guardShape(rhsId, fun->lastProperty());
    4249             : 
    4250             :     // Load prototypeObject into the cache -- consumed twice in the IC
    4251          21 :     ObjOperandId protoId = writer.loadObject(prototypeObject);
    4252             :     // Ensure that rhs[slot] == prototypeObject.
    4253           0 :     writer.guardFunctionPrototype(rhsId, slot, protoId);
    4254             : 
    4255             :     // Needn't guard LHS is object, because the actual stub can handle that
    4256             :     // and correctly return false.
    4257          21 :     writer.loadInstanceOfObjectResult(lhs, protoId, slot);
    4258          42 :     writer.returnFromIC();
    4259           0 :     trackAttached("InstanceOf");
    4260          21 :     return true;
    4261             : }
    4262             : 
    4263             : void
    4264           1 : InstanceOfIRGenerator::trackAttached(const char* name)
    4265             : {
    4266             : #ifdef JS_CACHEIR_SPEW
    4267           0 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    4268           0 :         sp.valueProperty("lhs", lhsVal_);
    4269           0 :         sp.valueProperty("rhs", ObjectValue(*rhsObj_));
    4270             :     }
    4271             : #endif
    4272           0 : }
    4273             : 
    4274         160 : TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    4275         160 :                                      ICState::Mode mode, HandleValue value)
    4276             :   : IRGenerator(cx, script, pc, CacheKind::TypeOf, mode),
    4277         320 :     val_(value)
    4278           0 : { }
    4279             : 
    4280             : bool
    4281         160 : TypeOfIRGenerator::tryAttachStub()
    4282             : {
    4283         160 :     MOZ_ASSERT(cacheKind_ == CacheKind::TypeOf);
    4284             : 
    4285         480 :     AutoAssertNoPendingException aanpe(cx_);
    4286             : 
    4287           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4288             : 
    4289         160 :     if (tryAttachPrimitive(valId))
    4290             :         return true;
    4291             : 
    4292           0 :     MOZ_ALWAYS_TRUE(tryAttachObject(valId));
    4293             :     return true;
    4294             : }
    4295             : 
    4296             : bool
    4297           0 : TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId)
    4298             : {
    4299         320 :     if (!val_.isPrimitive())
    4300             :         return false;
    4301             : 
    4302           0 :     if (val_.isNumber())
    4303           0 :         writer.guardIsNumber(valId);
    4304             :     else
    4305           0 :         writer.guardType(valId,  val_.extractNonDoubleType());
    4306             : 
    4307         575 :     writer.loadStringResult(TypeName(js::TypeOfValue(val_), cx_->names()));
    4308         230 :     writer.returnFromIC();
    4309             : 
    4310         115 :     return true;
    4311             : }
    4312             : 
    4313             : bool
    4314           0 : TypeOfIRGenerator::tryAttachObject(ValOperandId valId)
    4315             : {
    4316           0 :     if (!val_.isObject())
    4317             :         return false;
    4318             : 
    4319          45 :     ObjOperandId objId = writer.guardIsObject(valId);
    4320          45 :     writer.loadTypeOfObjectResult(objId);
    4321           0 :     writer.returnFromIC();
    4322             : 
    4323          45 :     return true;
    4324             : }
    4325             : 
    4326          83 : GetIteratorIRGenerator::GetIteratorIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    4327          83 :                                                ICState::Mode mode, HandleValue value)
    4328             :   : IRGenerator(cx, script, pc, CacheKind::GetIterator, mode),
    4329         166 :     val_(value)
    4330           0 : { }
    4331             : 
    4332             : bool
    4333          83 : GetIteratorIRGenerator::tryAttachStub()
    4334             : {
    4335          83 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetIterator);
    4336             : 
    4337           0 :     AutoAssertNoPendingException aanpe(cx_);
    4338             : 
    4339          83 :     if (mode_ == ICState::Mode::Megamorphic)
    4340             :         return false;
    4341             : 
    4342         162 :     ValOperandId valId(writer.setInputOperandId(0));
    4343           0 :     if (!val_.isObject())
    4344             :         return false;
    4345             : 
    4346         243 :     RootedObject obj(cx_, &val_.toObject());
    4347             : 
    4348          81 :     ObjOperandId objId = writer.guardIsObject(valId);
    4349          81 :     if (tryAttachNativeIterator(objId, obj))
    4350             :         return true;
    4351             : 
    4352          53 :     return false;
    4353             : }
    4354             : 
    4355             : bool
    4356           0 : GetIteratorIRGenerator::tryAttachNativeIterator(ObjOperandId objId, HandleObject obj)
    4357             : {
    4358          81 :     MOZ_ASSERT(JSOp(*pc_) == JSOP_ITER);
    4359             : 
    4360          81 :     PropertyIteratorObject* iterobj = LookupInIteratorCache(cx_, obj);
    4361          81 :     if (!iterobj)
    4362             :         return false;
    4363             : 
    4364          56 :     MOZ_ASSERT(obj->isNative() || obj->is<UnboxedPlainObject>());
    4365             : 
    4366             :     // Guard on the receiver's shape/group.
    4367           0 :     Maybe<ObjOperandId> expandoId;
    4368           0 :     TestMatchingReceiver(writer, obj, objId, &expandoId);
    4369             : 
    4370             :     // Ensure the receiver or its expando object has no dense elements.
    4371          56 :     if (obj->isNative())
    4372           0 :         writer.guardNoDenseElements(objId);
    4373           0 :     else if (expandoId)
    4374           0 :         writer.guardNoDenseElements(*expandoId);
    4375             : 
    4376             :     // Do the same for the objects on the proto chain.
    4377           0 :     GeneratePrototypeHoleGuards(writer, obj, objId);
    4378             : 
    4379             :     ObjOperandId iterId =
    4380          56 :         writer.guardAndGetIterator(objId, iterobj, &ObjectRealm::get(obj).enumerators);
    4381          28 :     writer.loadObjectResult(iterId);
    4382           0 :     writer.returnFromIC();
    4383             : 
    4384           0 :     return true;
    4385             : }
    4386             : 
    4387        3819 : CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, JSOp op,
    4388             :                                  ICState::Mode mode, uint32_t argc,
    4389        3819 :                                  HandleValue callee, HandleValue thisval, HandleValueArray args)
    4390             :   : IRGenerator(cx, script, pc, CacheKind::Call, mode),
    4391             :     op_(op),
    4392             :     argc_(argc),
    4393             :     callee_(callee),
    4394             :     thisval_(thisval),
    4395             :     args_(args),
    4396             :     typeCheckInfo_(cx, /* needsTypeBarrier = */ true),
    4397       11457 :     cacheIRStubKind_(BaselineCacheIRStubKind::Regular)
    4398        3819 : { }
    4399             : 
    4400             : bool
    4401           3 : CallIRGenerator::tryAttachStringSplit()
    4402             : {
    4403             :     // Only optimize StringSplitString(str, str)
    4404           9 :     if (argc_ != 2 || !args_[0].isString() || !args_[1].isString())
    4405             :         return false;
    4406             : 
    4407             :     // Just for now: if they're both atoms, then do not optimize using
    4408             :     // CacheIR and allow the legacy "ConstStringSplit" BaselineIC optimization
    4409             :     // to proceed.
    4410           0 :     if (args_[0].toString()->isAtom() && args_[1].toString()->isAtom())
    4411             :         return false;
    4412             : 
    4413             :     // Get the object group to use for this location.
    4414           0 :     RootedObjectGroup group(cx_, ObjectGroupRealm::getStringSplitStringGroup(cx_));
    4415           2 :     if (!group)
    4416             :         return false;
    4417             : 
    4418           6 :     AutoAssertNoPendingException aanpe(cx_);
    4419           4 :     Int32OperandId argcId(writer.setInputOperandId(0));
    4420             : 
    4421             :     // Ensure argc == 1.
    4422           2 :     writer.guardSpecificInt32Immediate(argcId, 2);
    4423             : 
    4424             :     // 2 arguments.  Stack-layout here is (bottom to top):
    4425             :     //
    4426             :     //  3: Callee
    4427             :     //  2: ThisValue
    4428             :     //  1: Arg0
    4429             :     //  0: Arg1 <-- Top of stack
    4430             : 
    4431             :     // Ensure callee is the |String_split| native function.
    4432           0 :     ValOperandId calleeValId = writer.loadStackValue(3);
    4433           0 :     ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
    4434           2 :     writer.guardIsNativeFunction(calleeObjId, js::intrinsic_StringSplitString);
    4435             : 
    4436             :     // Ensure arg0 is a string.
    4437           0 :     ValOperandId arg0ValId = writer.loadStackValue(1);
    4438           2 :     StringOperandId arg0StrId = writer.guardIsString(arg0ValId);
    4439             : 
    4440             :     // Ensure arg1 is a string.
    4441           0 :     ValOperandId arg1ValId = writer.loadStackValue(0);
    4442           2 :     StringOperandId arg1StrId = writer.guardIsString(arg1ValId);
    4443             : 
    4444             :     // Call custom string splitter VM-function.
    4445           2 :     writer.callStringSplitResult(arg0StrId, arg1StrId, group);
    4446           0 :     writer.typeMonitorResult();
    4447             : 
    4448           2 :     cacheIRStubKind_ = BaselineCacheIRStubKind::Monitored;
    4449           2 :     trackAttached("StringSplitString");
    4450             : 
    4451           8 :     TypeScript::Monitor(cx_, script_, pc_, TypeSet::ObjectType(group));
    4452             : 
    4453             :     return true;
    4454             : }
    4455             : 
    4456             : bool
    4457          57 : CallIRGenerator::tryAttachArrayPush()
    4458             : {
    4459             :     // Only optimize on obj.push(val);
    4460           0 :     if (argc_ != 1 || !thisval_.isObject())
    4461             :         return false;
    4462             : 
    4463             :     // Where |obj| is a native array.
    4464         171 :     RootedObject thisobj(cx_, &thisval_.toObject());
    4465         114 :     if (!thisobj->is<ArrayObject>())
    4466             :         return false;
    4467             : 
    4468         114 :     RootedArrayObject thisarray(cx_, &thisobj->as<ArrayObject>());
    4469             : 
    4470             :     // And the object group for the array is not collecting preliminary objects.
    4471           0 :     AutoSweepObjectGroup sweep(thisobj->group());
    4472          57 :     if (thisobj->group()->maybePreliminaryObjects(sweep))
    4473             :         return false;
    4474             : 
    4475             :     // Check for other indexed properties or class hooks.
    4476          57 :     if (!CanAttachAddElement(thisarray, /* isInit = */ false))
    4477             :         return false;
    4478             : 
    4479             :     // Can't add new elements to arrays with non-writable length.
    4480         114 :     if (!thisarray->lengthIsWritable())
    4481             :         return false;
    4482             : 
    4483             :     // Check that array is extensible.
    4484           0 :     if (!thisarray->isExtensible())
    4485             :         return false;
    4486             : 
    4487         114 :     MOZ_ASSERT(!thisarray->getElementsHeader()->isFrozen(),
    4488             :                "Extensible arrays should not have frozen elements");
    4489           0 :     MOZ_ASSERT(thisarray->lengthIsWritable());
    4490             : 
    4491             :     // After this point, we can generate code fine.
    4492             : 
    4493             :     // Generate code.
    4494         171 :     AutoAssertNoPendingException aanpe(cx_);
    4495         114 :     Int32OperandId argcId(writer.setInputOperandId(0));
    4496             : 
    4497             :     // Ensure argc == 1.
    4498          57 :     writer.guardSpecificInt32Immediate(argcId, 1);
    4499             : 
    4500             :     // 1 argument only.  Stack-layout here is (bottom to top):
    4501             :     //
    4502             :     //  2: Callee
    4503             :     //  1: ThisValue
    4504             :     //  0: Arg0 <-- Top of stack.
    4505             : 
    4506             :     // Guard callee is the |js::array_push| native function.
    4507           0 :     ValOperandId calleeValId = writer.loadStackValue(2);
    4508           0 :     ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
    4509          57 :     writer.guardIsNativeFunction(calleeObjId, js::array_push);
    4510             : 
    4511             :     // Guard this is an array object.
    4512          57 :     ValOperandId thisValId = writer.loadStackValue(1);
    4513           0 :     ObjOperandId thisObjId = writer.guardIsObject(thisValId);
    4514             : 
    4515             :     // This is a soft assert, documenting the fact that we pass 'true'
    4516             :     // for needsTypeBarrier when constructing typeCheckInfo_ for CallIRGenerator.
    4517             :     // Can be removed safely if the assumption becomes false.
    4518           0 :     MOZ_ASSERT(typeCheckInfo_.needsTypeBarrier());
    4519             : 
    4520             :     // Guard that the group and shape matches.
    4521           0 :     if (typeCheckInfo_.needsTypeBarrier())
    4522          57 :         writer.guardGroupForTypeBarrier(thisObjId, thisobj->group());
    4523         114 :     TestMatchingNativeReceiver(writer, thisarray, thisObjId);
    4524             : 
    4525             :     // Guard proto chain shapes.
    4526          57 :     ShapeGuardProtoChain(writer, thisobj, thisObjId);
    4527             : 
    4528             :     // arr.push(x) is equivalent to arr[arr.length] = x for regular arrays.
    4529          57 :     ValOperandId argId = writer.loadStackValue(0);
    4530           0 :     writer.arrayPush(thisObjId, argId);
    4531             : 
    4532           0 :     writer.returnFromIC();
    4533             : 
    4534             :     // Set the type-check info, and the stub kind to Updated
    4535          57 :     typeCheckInfo_.set(thisobj->group(), JSID_VOID);
    4536             : 
    4537          57 :     cacheIRStubKind_ = BaselineCacheIRStubKind::Updated;
    4538             : 
    4539           0 :     trackAttached("ArrayPush");
    4540             :     return true;
    4541             : }
    4542             : 
    4543             : bool
    4544          16 : CallIRGenerator::tryAttachArrayJoin()
    4545             : {
    4546             :     // Only handle argc <= 1.
    4547          16 :     if (argc_ > 1)
    4548             :         return false;
    4549             : 
    4550             :     // Only optimize on obj.join(...);
    4551           0 :     if (!thisval_.isObject())
    4552             :         return false;
    4553             : 
    4554             :     // Where |obj| is a native array.
    4555          48 :     RootedObject thisobj(cx_, &thisval_.toObject());
    4556          32 :     if (!thisobj->is<ArrayObject>())
    4557             :         return false;
    4558             : 
    4559          32 :     RootedArrayObject thisarray(cx_, &thisobj->as<ArrayObject>());
    4560             : 
    4561             :     // And the array is of length 0 or 1.
    4562          32 :     if (thisarray->length() > 1)
    4563             :         return false;
    4564             : 
    4565             :     // And the array is packed.
    4566          20 :     if (thisarray->getDenseInitializedLength() != thisarray->length())
    4567             :         return false;
    4568             : 
    4569             :     // We don't need to worry about indexed properties because we can perform
    4570             :     // hole check manually.
    4571             : 
    4572             :     // Generate code.
    4573          15 :     AutoAssertNoPendingException aanpe(cx_);
    4574          10 :     Int32OperandId argcId(writer.setInputOperandId(0));
    4575             : 
    4576             :     // if 0 arguments:
    4577             :     //  1: Callee
    4578             :     //  0: ThisValue <-- Top of stack.
    4579             :     //
    4580             :     // if 1 argument:
    4581             :     //  2: Callee
    4582             :     //  1: ThisValue
    4583             :     //  0: Arg0 [optional] <-- Top of stack.
    4584             : 
    4585             :     // Guard callee is the |js::array_join| native function.
    4586           0 :     uint32_t calleeIndex = (argc_ == 0) ? 1 : 2;
    4587           5 :     ValOperandId calleeValId = writer.loadStackValue(calleeIndex);
    4588           0 :     ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
    4589           0 :     writer.guardIsNativeFunction(calleeObjId, js::array_join);
    4590             : 
    4591           5 :     if (argc_ == 1) {
    4592             :         // If argcount is 1, guard that the argument is a string.
    4593           0 :         ValOperandId argValId = writer.loadStackValue(0);
    4594           0 :         writer.guardIsString(argValId);
    4595             :     }
    4596             : 
    4597             :     // Guard this is an array object.
    4598           5 :     uint32_t thisIndex = (argc_ == 0) ? 0 : 1;
    4599           0 :     ValOperandId thisValId = writer.loadStackValue(thisIndex);
    4600           5 :     ObjOperandId thisObjId = writer.guardIsObject(thisValId);
    4601           0 :     writer.guardClass(thisObjId, GuardClassKind::Array);
    4602             : 
    4603             :     // Do the join.
    4604           5 :     writer.arrayJoinResult(thisObjId);
    4605             : 
    4606          10 :     writer.returnFromIC();
    4607             : 
    4608             :     // The result of this stub does not need to be monitored because it will
    4609             :     // always return a string.  We will add String to the stack typeset when
    4610             :     // attaching this stub.
    4611             : 
    4612             :     // Set the stub kind to Regular
    4613           5 :     cacheIRStubKind_ = BaselineCacheIRStubKind::Regular;
    4614             : 
    4615           0 :     trackAttached("ArrayJoin");
    4616             :     return true;
    4617             : }
    4618             : 
    4619             : bool
    4620        3819 : CallIRGenerator::tryAttachStub()
    4621             : {
    4622             :     // Only optimize on JSOP_CALL or JSOP_CALL_IGNORES_RV.  No fancy business for now.
    4623        3819 :     if ((op_ != JSOP_CALL) && (op_ != JSOP_CALL_IGNORES_RV))
    4624             :         return false;
    4625             : 
    4626             :     // Only optimize when the mode is Specialized.
    4627        3005 :     if (mode_ != ICState::Mode::Specialized)
    4628             :         return false;
    4629             : 
    4630             :     // Ensure callee is a function.
    4631       11360 :     if (!callee_.isObject() || !callee_.toObject().is<JSFunction>())
    4632             :         return false;
    4633             : 
    4634           0 :     RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
    4635             : 
    4636             :     // Check for native-function optimizations.
    4637        4906 :     if (calleeFunc->isNative()) {
    4638           0 :         if (calleeFunc->native() == js::intrinsic_StringSplitString) {
    4639           0 :             if (tryAttachStringSplit())
    4640             :                 return true;
    4641             :         }
    4642             : 
    4643           0 :         if (calleeFunc->native() == js::array_push) {
    4644           0 :             if (tryAttachArrayPush())
    4645             :                 return true;
    4646             :         }
    4647             : 
    4648        1021 :         if (calleeFunc->native() == js::array_join) {
    4649          16 :             if (tryAttachArrayJoin())
    4650             :                 return true;
    4651             :         }
    4652             :     }
    4653             : 
    4654             :     return false;
    4655             : }
    4656             : 
    4657             : void
    4658           1 : CallIRGenerator::trackAttached(const char* name)
    4659             : {
    4660             : #ifdef JS_CACHEIR_SPEW
    4661         192 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    4662           0 :         sp.valueProperty("callee", callee_);
    4663           0 :         sp.valueProperty("thisval", thisval_);
    4664           0 :         sp.valueProperty("argc", Int32Value(argc_));
    4665             :     }
    4666             : #endif
    4667          64 : }
    4668             : 
    4669             : // Class which holds a shape pointer for use when caches might reference data in other zones.
    4670             : static const Class shapeContainerClass = {
    4671             :     "ShapeContainer",
    4672             :     JSCLASS_HAS_RESERVED_SLOTS(1)
    4673             : };
    4674             : 
    4675             : static const size_t SHAPE_CONTAINER_SLOT = 0;
    4676             : 
    4677             : JSObject*
    4678           0 : jit::NewWrapperWithObjectShape(JSContext* cx, HandleNativeObject obj)
    4679             : {
    4680           0 :     MOZ_ASSERT(cx->compartment() != obj->compartment());
    4681             : 
    4682           0 :     RootedObject wrapper(cx);
    4683             :     {
    4684           0 :         AutoRealm ar(cx, obj);
    4685           0 :         wrapper = NewObjectWithClassProto(cx, &shapeContainerClass, nullptr);
    4686           0 :         if (!obj)
    4687           0 :             return nullptr;
    4688           0 :         wrapper->as<NativeObject>().setSlot(SHAPE_CONTAINER_SLOT, PrivateGCThingValue(obj->lastProperty()));
    4689             :     }
    4690           0 :     if (!JS_WrapObject(cx, &wrapper))
    4691             :         return nullptr;
    4692           0 :     MOZ_ASSERT(IsWrapper(wrapper));
    4693           0 :     return wrapper;
    4694             : }
    4695             : 
    4696             : void
    4697           0 : jit::LoadShapeWrapperContents(MacroAssembler& masm, Register obj, Register dst, Label* failure)
    4698             : {
    4699           0 :     masm.loadPtr(Address(obj, ProxyObject::offsetOfReservedSlots()), dst);
    4700           0 :     Address privateAddr(dst, detail::ProxyReservedSlots::offsetOfPrivateSlot());
    4701           0 :     masm.branchTestObject(Assembler::NotEqual, privateAddr, failure);
    4702           0 :     masm.unboxObject(privateAddr, dst);
    4703           0 :     masm.unboxNonDouble(Address(dst, NativeObject::getFixedSlotOffset(SHAPE_CONTAINER_SLOT)), dst,
    4704           0 :                         JSVAL_TYPE_PRIVATE_GCTHING);
    4705           0 : }
    4706             : 
    4707           0 : CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    4708             :                                        ICState::Mode mode, JSOp op,
    4709         871 :                                        HandleValue lhsVal, HandleValue rhsVal)
    4710             :   : IRGenerator(cx, script, pc, CacheKind::Compare, mode),
    4711        1742 :     op_(op), lhsVal_(lhsVal), rhsVal_(rhsVal)
    4712           0 : { }
    4713             : 
    4714             : bool
    4715         740 : CompareIRGenerator::tryAttachString(ValOperandId lhsId, ValOperandId rhsId)
    4716             : {
    4717           0 :     MOZ_ASSERT(IsEqualityOp(op_));
    4718             : 
    4719           0 :     if (!lhsVal_.isString() || !rhsVal_.isString())
    4720             :         return false;
    4721             : 
    4722           0 :     StringOperandId lhsStrId = writer.guardIsString(lhsId);
    4723           0 :     StringOperandId rhsStrId = writer.guardIsString(rhsId);
    4724         266 :     writer.compareStringResult(op_, lhsStrId, rhsStrId);
    4725         532 :     writer.returnFromIC();
    4726             : 
    4727           0 :     trackAttached("String");
    4728         266 :     return true;
    4729             : }
    4730             : 
    4731             : bool
    4732         474 : CompareIRGenerator::tryAttachObject(ValOperandId lhsId, ValOperandId rhsId)
    4733             : {
    4734           0 :     MOZ_ASSERT(IsEqualityOp(op_));
    4735             : 
    4736           0 :     if (!lhsVal_.isObject() || !rhsVal_.isObject())
    4737             :         return false;
    4738             : 
    4739           0 :     ObjOperandId lhsObjId = writer.guardIsObject(lhsId);
    4740           0 :     ObjOperandId rhsObjId = writer.guardIsObject(rhsId);
    4741          14 :     writer.compareObjectResult(op_, lhsObjId, rhsObjId);
    4742          28 :     writer.returnFromIC();
    4743             : 
    4744           0 :     trackAttached("Object");
    4745          14 :     return true;
    4746             : }
    4747             : 
    4748             : bool
    4749         460 : CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId)
    4750             : {
    4751           0 :     MOZ_ASSERT(IsEqualityOp(op_));
    4752             : 
    4753           0 :     if (!lhsVal_.isSymbol() || !rhsVal_.isSymbol())
    4754             :         return false;
    4755             : 
    4756           0 :     SymbolOperandId lhsSymId = writer.guardIsSymbol(lhsId);
    4757           0 :     SymbolOperandId rhsSymId = writer.guardIsSymbol(rhsId);
    4758           0 :     writer.compareSymbolResult(op_, lhsSymId, rhsSymId);
    4759           0 :     writer.returnFromIC();
    4760             : 
    4761           0 :     trackAttached("Symbol");
    4762           0 :     return true;
    4763             : }
    4764             : 
    4765             : bool
    4766         460 : CompareIRGenerator::tryAttachStrictDifferentTypes(ValOperandId lhsId, ValOperandId rhsId)
    4767             : {
    4768         920 :     MOZ_ASSERT(IsEqualityOp(op_));
    4769             : 
    4770         460 :     if (op_ != JSOP_STRICTEQ && op_ != JSOP_STRICTNE)
    4771             :         return false;
    4772             : 
    4773             :     // Probably can't hit some of these.
    4774           0 :     if (SameType(lhsVal_, rhsVal_) || (lhsVal_.isNumber() && rhsVal_.isNumber()))
    4775             :         return false;
    4776             : 
    4777             :     // Compare tags
    4778           0 :     ValueTagOperandId lhsTypeId = writer.loadValueTag(lhsId);
    4779           0 :     ValueTagOperandId rhsTypeId = writer.loadValueTag(rhsId);
    4780         149 :     writer.guardTagNotEqual(lhsTypeId, rhsTypeId);
    4781             : 
    4782             :     // Now that we've passed the guard, we know differing types, so return the bool result.
    4783         298 :     writer.loadBooleanResult(op_ == JSOP_STRICTNE ? true : false);
    4784         298 :     writer.returnFromIC();
    4785             : 
    4786           0 :     trackAttached("StrictDifferentTypes");
    4787         149 :     return true;
    4788             : }
    4789             : 
    4790             : bool
    4791         871 : CompareIRGenerator::tryAttachStub()
    4792             : {
    4793           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
    4794        1742 :     MOZ_ASSERT(IsEqualityOp(op_) ||
    4795             :                op_ == JSOP_LE || op_ == JSOP_LT ||
    4796             :                op_ == JSOP_GE || op_ == JSOP_GT);
    4797             : 
    4798           0 :     AutoAssertNoPendingException aanpe(cx_);
    4799             : 
    4800        1742 :     ValOperandId lhsId(writer.setInputOperandId(0));
    4801           0 :     ValOperandId rhsId(writer.setInputOperandId(1));
    4802             : 
    4803           0 :     if (IsEqualityOp(op_)) {
    4804         740 :         if (tryAttachString(lhsId, rhsId))
    4805             :             return true;
    4806         474 :         if (tryAttachObject(lhsId, rhsId))
    4807             :             return true;
    4808           0 :         if (tryAttachSymbol(lhsId, rhsId))
    4809             :             return true;
    4810         460 :         if (tryAttachStrictDifferentTypes(lhsId, rhsId))
    4811             :             return true;
    4812             : 
    4813           0 :         trackAttached(IRGenerator::NotAttached);
    4814         311 :         return false;
    4815             :     }
    4816             : 
    4817           0 :     trackAttached(IRGenerator::NotAttached);
    4818         131 :     return false;
    4819             : }
    4820             : 
    4821             : void
    4822           1 : CompareIRGenerator::trackAttached(const char* name)
    4823             : {
    4824             : #ifdef JS_CACHEIR_SPEW
    4825           0 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    4826           0 :         sp.valueProperty("lhs", lhsVal_);
    4827           0 :         sp.valueProperty("rhs", rhsVal_);
    4828             :     }
    4829             : #endif
    4830           0 : }
    4831             : 
    4832         703 : ToBoolIRGenerator::ToBoolIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
    4833         703 :                                      HandleValue val)
    4834             :   : IRGenerator(cx, script, pc, CacheKind::ToBool, mode),
    4835        1406 :     val_(val)
    4836         703 : {}
    4837             : 
    4838             : void
    4839         703 : ToBoolIRGenerator::trackAttached(const char* name)
    4840             : {
    4841             : #ifdef JS_CACHEIR_SPEW
    4842        2109 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    4843           0 :         sp.valueProperty("val", val_);
    4844             :     }
    4845             : #endif
    4846           0 : }
    4847             : 
    4848             : bool
    4849         703 : ToBoolIRGenerator::tryAttachStub()
    4850             : {
    4851        2109 :     AutoAssertNoPendingException aanpe(cx_);
    4852             : 
    4853         703 :     if (tryAttachInt32())
    4854             :         return true;
    4855         637 :     if (tryAttachDouble())
    4856             :         return true;
    4857         635 :     if (tryAttachString())
    4858             :         return true;
    4859         536 :     if (tryAttachNullOrUndefined())
    4860             :         return true;
    4861           1 :     if (tryAttachObject())
    4862             :         return true;
    4863           0 :     if (tryAttachSymbol())
    4864             :         return true;
    4865             : 
    4866           0 :     trackAttached(IRGenerator::NotAttached);
    4867           0 :     return false;
    4868             : }
    4869             : 
    4870             : bool
    4871           0 : ToBoolIRGenerator::tryAttachInt32()
    4872             : {
    4873           0 :     if (!val_.isInt32())
    4874             :         return false;
    4875             : 
    4876           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4877          66 :     writer.guardType(valId, JSVAL_TYPE_INT32);
    4878          66 :     writer.loadInt32TruthyResult(valId);
    4879         132 :     writer.returnFromIC();
    4880           0 :     trackAttached("ToBoolInt32");
    4881          66 :     return true;
    4882             : }
    4883             : 
    4884             : bool
    4885           0 : ToBoolIRGenerator::tryAttachDouble()
    4886             : {
    4887           0 :     if (!val_.isDouble() || !cx_->runtime()->jitSupportsFloatingPoint)
    4888             :         return false;
    4889             : 
    4890           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4891           2 :     writer.guardType(valId, JSVAL_TYPE_DOUBLE);
    4892           2 :     writer.loadDoubleTruthyResult(valId);
    4893           4 :     writer.returnFromIC();
    4894           0 :     trackAttached("ToBoolDouble");
    4895           2 :     return true;
    4896             : }
    4897             : 
    4898             : bool
    4899           0 : ToBoolIRGenerator::tryAttachSymbol()
    4900             : {
    4901           0 :     if (!val_.isSymbol())
    4902             :         return false;
    4903             : 
    4904           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4905           0 :     writer.guardType(valId, JSVAL_TYPE_SYMBOL);
    4906           0 :     writer.loadBooleanResult(true);
    4907           0 :     writer.returnFromIC();
    4908           0 :     trackAttached("ToBoolSymbol");
    4909           0 :     return true;
    4910             : }
    4911             : 
    4912             : bool
    4913           0 : ToBoolIRGenerator::tryAttachString()
    4914             : {
    4915           0 :     if (!val_.isString())
    4916             :         return false;
    4917             : 
    4918           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4919          99 :     StringOperandId strId = writer.guardIsString(valId);
    4920          99 :     writer.loadStringTruthyResult(strId);
    4921         198 :     writer.returnFromIC();
    4922           0 :     trackAttached("ToBoolString");
    4923          99 :     return true;
    4924             : }
    4925             : 
    4926             : bool
    4927           0 : ToBoolIRGenerator::tryAttachNullOrUndefined()
    4928             : {
    4929           0 :     if (!val_.isNullOrUndefined())
    4930             :         return false;
    4931             : 
    4932           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4933         319 :     writer.guardIsNullOrUndefined(valId);
    4934         638 :     writer.loadBooleanResult(false);
    4935         638 :     writer.returnFromIC();
    4936           0 :     trackAttached("ToBoolNullOrUndefined");
    4937         319 :     return true;
    4938             : }
    4939             : 
    4940             : bool
    4941           0 : ToBoolIRGenerator::tryAttachObject()
    4942             : {
    4943           0 :     if (!val_.isObject())
    4944             :         return false;
    4945             : 
    4946           0 :     ValOperandId valId(writer.setInputOperandId(0));
    4947         217 :     ObjOperandId objId = writer.guardIsObject(valId);
    4948         217 :     writer.loadObjectTruthyResult(objId);
    4949           0 :     writer.returnFromIC();
    4950           0 :     trackAttached("ToBoolObject");
    4951         217 :     return true;
    4952             : }
    4953             : 
    4954         537 : GetIntrinsicIRGenerator::GetIntrinsicIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
    4955         537 :                                                  HandleValue val)
    4956             :   : IRGenerator(cx, script, pc, CacheKind::GetIntrinsic, mode)
    4957        1074 :   , val_(val)
    4958         537 : {}
    4959             : 
    4960             : void
    4961         537 : GetIntrinsicIRGenerator::trackAttached(const char* name)
    4962             : {
    4963             : #ifdef JS_CACHEIR_SPEW
    4964        1611 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    4965           0 :         sp.valueProperty("val", val_);
    4966             :     }
    4967             : #endif
    4968           0 : }
    4969             : 
    4970             : bool
    4971           0 : GetIntrinsicIRGenerator::tryAttachStub()
    4972             : {
    4973           0 :     writer.loadValueResult(val_);
    4974           0 :     writer.returnFromIC();
    4975         537 :     trackAttached("GetIntrinsic");
    4976         537 :     return true;
    4977             : }
    4978           0 : UnaryArithIRGenerator::UnaryArithIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, ICState::Mode mode,
    4979           0 :                                              JSOp op, HandleValue val, HandleValue res)
    4980             :   : IRGenerator(cx, script, pc, CacheKind::UnaryArith, mode),
    4981             :     op_(op),
    4982             :     val_(val),
    4983           2 :     res_(res)
    4984           1 : { }
    4985             : 
    4986             : void
    4987           1 : UnaryArithIRGenerator::trackAttached(const char* name)
    4988             : {
    4989             : #ifdef JS_CACHEIR_SPEW
    4990           3 :     if (const CacheIRSpewer::Guard& sp = CacheIRSpewer::Guard(*this, name)) {
    4991           0 :         sp.valueProperty("val", val_);
    4992             :     }
    4993             : #endif
    4994           0 : }
    4995             : 
    4996             : bool
    4997           1 : UnaryArithIRGenerator::tryAttachStub()
    4998             : {
    4999           0 :     if (tryAttachInt32())
    5000             :         return true;
    5001           1 :     if (tryAttachNumber())
    5002             :         return true;
    5003             : 
    5004           0 :     trackAttached(IRGenerator::NotAttached);
    5005           0 :     return false;
    5006             : }
    5007             : 
    5008             : bool
    5009           0 : UnaryArithIRGenerator::tryAttachInt32()
    5010             : {
    5011           0 :     if (!val_.isInt32() || !res_.isInt32())
    5012             :         return false;
    5013             : 
    5014           0 :     ValOperandId valId(writer.setInputOperandId(0));
    5015             : 
    5016           0 :     Int32OperandId intId = writer.guardIsInt32(valId);
    5017           0 :     switch (op_) {
    5018             :       case JSOP_BITNOT:
    5019           0 :         writer.int32NotResult(intId);
    5020           0 :         trackAttached("UnaryArith.Int32Not");
    5021           0 :         break;
    5022             :       case JSOP_NEG:
    5023           0 :         writer.int32NegationResult(intId);
    5024           0 :         trackAttached("UnaryArith.Int32Neg");
    5025           0 :         break;
    5026             :       default:
    5027           0 :         MOZ_CRASH("Unexected OP");
    5028             :     }
    5029             : 
    5030           0 :     writer.returnFromIC();
    5031           0 :     return true;
    5032             : }
    5033             : 
    5034             : bool
    5035           0 : UnaryArithIRGenerator::tryAttachNumber()
    5036             : {
    5037           0 :     if (!val_.isNumber() || !res_.isNumber() || !cx_->runtime()->jitSupportsFloatingPoint)
    5038             :         return false;
    5039             : 
    5040           0 :     ValOperandId valId(writer.setInputOperandId(0));
    5041           0 :     writer.guardType(valId, JSVAL_TYPE_DOUBLE);
    5042           0 :     Int32OperandId truncatedId;
    5043           0 :     switch (op_) {
    5044             :       case JSOP_BITNOT:
    5045           0 :         truncatedId = writer.truncateDoubleToUInt32(valId);
    5046           0 :         writer.int32NotResult(truncatedId);
    5047           0 :         trackAttached("UnaryArith.DoubleNot");
    5048           0 :         break;
    5049             :       case JSOP_NEG:
    5050           1 :         writer.doubleNegationResult(valId);
    5051           1 :         trackAttached("UnaryArith.DoubleNeg");
    5052           0 :         break;
    5053             :       default:
    5054             :         MOZ_CRASH("Unexpected OP");
    5055             :     }
    5056             : 
    5057             :     writer.returnFromIC();
    5058             :     return true;
    5059             : }

Generated by: LCOV version 1.13-14-ga5dd952