LCOV - code coverage report
Current view: top level - js/src/jit - CacheIR.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 1295 2048 63.2 %
Date: 2017-07-14 16:53:18 Functions: 108 124 87.1 %
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/BaselineIC.h"
      13             : #include "jit/CacheIRSpewer.h"
      14             : #include "jit/IonCaches.h"
      15             : 
      16             : #include "vm/SelfHosting.h"
      17             : #include "jsobjinlines.h"
      18             : 
      19             : #include "vm/EnvironmentObject-inl.h"
      20             : #include "vm/UnboxedObject-inl.h"
      21             : 
      22             : using namespace js;
      23             : using namespace js::jit;
      24             : 
      25             : using mozilla::DebugOnly;
      26             : using mozilla::Maybe;
      27             : 
      28             : const char* js::jit::CacheKindNames[] = {
      29             : #define DEFINE_KIND(kind) #kind,
      30             :     CACHE_IR_KINDS(DEFINE_KIND)
      31             : #undef DEFINE_KIND
      32             : };
      33             : 
      34             : 
      35       12114 : IRGenerator::IRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc, CacheKind cacheKind,
      36       12114 :                          ICState::Mode mode)
      37             :   : writer(cx),
      38             :     cx_(cx),
      39             :     script_(script),
      40             :     pc_(pc),
      41             :     cacheKind_(cacheKind),
      42       12114 :     mode_(mode)
      43       12114 : {}
      44             : 
      45        4189 : GetPropIRGenerator::GetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
      46             :                                        CacheKind cacheKind, ICState::Mode mode,
      47             :                                        bool* isTemporarilyUnoptimizable, HandleValue val,
      48             :                                        HandleValue idVal, HandleValue receiver,
      49        4189 :                                        CanAttachGetter canAttachGetter)
      50             :   : IRGenerator(cx, script, pc, cacheKind, mode),
      51             :     val_(val),
      52             :     idVal_(idVal),
      53             :     receiver_(receiver),
      54             :     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
      55             :     canAttachGetter_(canAttachGetter),
      56        4189 :     preliminaryObjectAction_(PreliminaryObjectAction::None)
      57        4189 : {}
      58             : 
      59             : static void
      60        3671 : EmitLoadSlotResult(CacheIRWriter& writer, ObjOperandId holderOp, NativeObject* holder,
      61             :                    Shape* shape)
      62             : {
      63        3671 :     if (holder->isFixedSlot(shape->slot())) {
      64        1034 :         writer.loadFixedSlotResult(holderOp, NativeObject::getFixedSlotOffset(shape->slot()));
      65             :     } else {
      66        2637 :         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
      67        2637 :         writer.loadDynamicSlotResult(holderOp, dynamicSlotOffset);
      68             :     }
      69        3671 : }
      70             : 
      71             : // DOM proxies
      72             : // -----------
      73             : //
      74             : // DOM proxies are proxies that are used to implement various DOM objects like
      75             : // HTMLDocument and NodeList. DOM proxies may have an expando object - a native
      76             : // object that stores extra properties added to the object. The following
      77             : // CacheIR instructions are only used with DOM proxies:
      78             : //
      79             : // * LoadDOMExpandoValue: returns the Value in the proxy's expando slot. This
      80             : //   returns either an UndefinedValue (no expando), ObjectValue (the expando
      81             : //   object), or PrivateValue(ExpandoAndGeneration*).
      82             : //
      83             : // * LoadDOMExpandoValueGuardGeneration: guards the Value in the proxy's expando
      84             : //   slot is the same PrivateValue(ExpandoAndGeneration*), then guards on its
      85             : //   generation, then returns expandoAndGeneration->expando. This Value is
      86             : //   either an UndefinedValue or ObjectValue.
      87             : //
      88             : // * LoadDOMExpandoValueIgnoreGeneration: assumes the Value in the proxy's
      89             : //   expando slot is a PrivateValue(ExpandoAndGeneration*), unboxes it, and
      90             : //   returns the expandoAndGeneration->expando Value.
      91             : //
      92             : // * GuardDOMExpandoMissingOrGuardShape: takes an expando Value as input, then
      93             : //   guards it's either UndefinedValue or an object with the expected shape.
      94             : 
      95             : enum class ProxyStubType {
      96             :     None,
      97             :     DOMExpando,
      98             :     DOMShadowed,
      99             :     DOMUnshadowed,
     100             :     Generic
     101             : };
     102             : 
     103             : static ProxyStubType
     104         943 : GetProxyStubType(JSContext* cx, HandleObject obj, HandleId id)
     105             : {
     106         943 :     if (!obj->is<ProxyObject>())
     107         810 :         return ProxyStubType::None;
     108             : 
     109         133 :     if (!IsCacheableDOMProxy(obj))
     110         127 :         return ProxyStubType::Generic;
     111             : 
     112           6 :     DOMProxyShadowsResult shadows = GetDOMProxyShadowsCheck()(cx, obj, id);
     113           6 :     if (shadows == ShadowCheckFailed) {
     114           0 :         cx->clearPendingException();
     115           0 :         return ProxyStubType::None;
     116             :     }
     117             : 
     118           6 :     if (DOMProxyIsShadowing(shadows)) {
     119           0 :         if (shadows == ShadowsViaDirectExpando || shadows == ShadowsViaIndirectExpando)
     120           0 :             return ProxyStubType::DOMExpando;
     121           0 :         return ProxyStubType::DOMShadowed;
     122             :     }
     123             : 
     124           6 :     MOZ_ASSERT(shadows == DoesntShadow || shadows == DoesntShadowUnique);
     125           6 :     return ProxyStubType::DOMUnshadowed;
     126             : }
     127             : 
     128             : bool
     129        4189 : GetPropIRGenerator::tryAttachStub()
     130             : {
     131             :     // Idempotent ICs should call tryAttachIdempotentStub instead.
     132        4189 :     MOZ_ASSERT(!idempotent());
     133             : 
     134        8378 :     AutoAssertNoPendingException aanpe(cx_);
     135             : 
     136             :     // Non-object receivers are a degenerate case, so don't try to attach
     137             :     // stubs. The stubs we do emit will still perform runtime checks and
     138             :     // fallback as needed.
     139        4189 :     if (isSuper() && !receiver_.isObject())
     140           0 :         return false;
     141             : 
     142        4189 :     ValOperandId valId(writer.setInputOperandId(0));
     143        4189 :     if (cacheKind_ != CacheKind::GetProp) {
     144         801 :         MOZ_ASSERT_IF(cacheKind_ == CacheKind::GetPropSuper, getSuperReceiverValueId().id() == 1);
     145         801 :         MOZ_ASSERT_IF(cacheKind_ != CacheKind::GetPropSuper, getElemKeyValueId().id() == 1);
     146         801 :         writer.setInputOperandId(1);
     147             :     }
     148        4189 :     if (cacheKind_ == CacheKind::GetElemSuper) {
     149           0 :         MOZ_ASSERT(getSuperReceiverValueId().id() == 2);
     150           0 :         writer.setInputOperandId(2);
     151             :     }
     152             : 
     153        8378 :     RootedId id(cx_);
     154             :     bool nameOrSymbol;
     155        4189 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
     156           0 :         cx_->clearPendingException();
     157           0 :         return false;
     158             :     }
     159             : 
     160        4189 :     if (val_.isObject()) {
     161        8158 :         RootedObject obj(cx_, &val_.toObject());
     162        4079 :         ObjOperandId objId = writer.guardIsObject(valId);
     163        4079 :         if (nameOrSymbol) {
     164        3898 :             if (tryAttachObjectLength(obj, objId, id))
     165         100 :                 return true;
     166        3798 :             if (tryAttachNative(obj, objId, id))
     167        2798 :                 return true;
     168        1000 :             if (tryAttachUnboxed(obj, objId, id))
     169           8 :                 return true;
     170         992 :             if (tryAttachUnboxedExpando(obj, objId, id))
     171           0 :                 return true;
     172         992 :             if (tryAttachTypedObject(obj, objId, id))
     173           0 :                 return true;
     174         992 :             if (tryAttachModuleNamespace(obj, objId, id))
     175           0 :                 return true;
     176         992 :             if (tryAttachWindowProxy(obj, objId, id))
     177           0 :                 return true;
     178         992 :             if (tryAttachCrossCompartmentWrapper(obj, objId, id))
     179         544 :                 return true;
     180         448 :             if (tryAttachFunction(obj, objId, id))
     181           0 :                 return true;
     182         448 :             if (tryAttachProxy(obj, objId, id))
     183         117 :                 return true;
     184             : 
     185         331 :             trackNotAttached();
     186         331 :             return false;
     187             :         }
     188             : 
     189         181 :         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
     190             : 
     191         181 :         if (tryAttachProxyElement(obj, objId))
     192          12 :             return true;
     193             : 
     194             :         uint32_t index;
     195         169 :         Int32OperandId indexId;
     196         169 :         if (maybeGuardInt32Index(idVal_, getElemKeyValueId(), &index, &indexId)) {
     197         147 :             if (tryAttachTypedElement(obj, objId, index, indexId))
     198           0 :                 return true;
     199         147 :             if (tryAttachDenseElement(obj, objId, index, indexId))
     200         145 :                 return true;
     201           2 :             if (tryAttachDenseElementHole(obj, objId, index, indexId))
     202           2 :                 return true;
     203           0 :             if (tryAttachUnboxedArrayElement(obj, objId, index, indexId))
     204           0 :                 return true;
     205           0 :             if (tryAttachArgumentsObjectArg(obj, objId, index, indexId))
     206           0 :                 return true;
     207             : 
     208           0 :             trackNotAttached();
     209           0 :             return false;
     210             :         }
     211             : 
     212          22 :         trackNotAttached();
     213          22 :         return false;
     214             :     }
     215             : 
     216         110 :     if (nameOrSymbol) {
     217          89 :         if (tryAttachPrimitive(valId, id))
     218          37 :             return true;
     219          52 :         if (tryAttachStringLength(valId, id))
     220          13 :             return true;
     221          39 :         if (tryAttachMagicArgumentsName(valId, id))
     222          38 :             return true;
     223             : 
     224           1 :         trackNotAttached();
     225           1 :         return false;
     226             :     }
     227             : 
     228          21 :     if (idVal_.isInt32()) {
     229          21 :         ValOperandId indexId = getElemKeyValueId();
     230          21 :         if (tryAttachStringChar(valId, indexId))
     231           3 :             return true;
     232          18 :         if (tryAttachMagicArgument(valId, indexId))
     233          16 :             return true;
     234             : 
     235           2 :         trackNotAttached();
     236           2 :         return false;
     237             :     }
     238             : 
     239           0 :     trackNotAttached();
     240           0 :     return false;
     241             : }
     242             : 
     243             : bool
     244           0 : GetPropIRGenerator::tryAttachIdempotentStub()
     245             : {
     246             :     // For idempotent ICs, only attach stubs for plain data properties.
     247             :     // This ensures (1) the lookup has no side-effects and (2) Ion has complete
     248             :     // static type information and we don't have to monitor the result. Because
     249             :     // of (2), we don't support for instance missing properties or array
     250             :     // lengths, as TI does not account for these cases.
     251             : 
     252           0 :     MOZ_ASSERT(idempotent());
     253             : 
     254           0 :     RootedObject obj(cx_, &val_.toObject());
     255           0 :     RootedId id(cx_, NameToId(idVal_.toString()->asAtom().asPropertyName()));
     256             : 
     257           0 :     ValOperandId valId(writer.setInputOperandId(0));
     258           0 :     ObjOperandId objId = writer.guardIsObject(valId);
     259           0 :     if (tryAttachNative(obj, objId, id))
     260           0 :         return true;
     261             : 
     262             :     // Also support native data properties on DOMProxy prototypes.
     263           0 :     if (GetProxyStubType(cx_, obj, id) == ProxyStubType::DOMUnshadowed)
     264           0 :         return tryAttachDOMProxyUnshadowed(obj, objId, id);
     265             : 
     266           0 :     return false;
     267             : }
     268             : 
     269             : static bool
     270         431 : IsCacheableNoProperty(JSContext* cx, JSObject* obj, JSObject* holder, Shape* shape, jsid id,
     271             :                       jsbytecode* pc)
     272             : {
     273         431 :     if (shape)
     274         230 :         return false;
     275             : 
     276         201 :     MOZ_ASSERT(!holder);
     277             : 
     278         201 :     if (!pc) {
     279             :         // This is an idempotent IC, don't attach a missing-property stub.
     280             :         // See tryAttachStub.
     281           0 :         return false;
     282             :     }
     283             : 
     284             :     // If we're doing a name lookup, we have to throw a ReferenceError. If
     285             :     // extra warnings are enabled, we may have to report a warning.
     286         201 :     if (*pc == JSOP_GETBOUNDNAME || cx->compartment()->behaviors().extraWarnings(cx))
     287           0 :         return false;
     288             : 
     289         201 :     return CheckHasNoSuchProperty(cx, obj, id);
     290             : }
     291             : 
     292             : enum NativeGetPropCacheability {
     293             :     CanAttachNone,
     294             :     CanAttachReadSlot,
     295             :     CanAttachCallGetter,
     296             : };
     297             : 
     298             : static NativeGetPropCacheability
     299        4396 : CanAttachNativeGetProp(JSContext* cx, HandleObject obj, HandleId id,
     300             :                        MutableHandleNativeObject holder, MutableHandleShape shape,
     301             :                        jsbytecode* pc, CanAttachGetter canAttachGetter,
     302             :                        bool* isTemporarilyUnoptimizable)
     303             : {
     304        4396 :     MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_SYMBOL(id));
     305             : 
     306             :     // The lookup needs to be universally pure, otherwise we risk calling hooks out
     307             :     // of turn. We don't mind doing this even when purity isn't required, because we
     308             :     // only miss out on shape hashification, which is only a temporary perf cost.
     309             :     // The limits were arbitrarily set, anyways.
     310        4396 :     JSObject* baseHolder = nullptr;
     311        4396 :     PropertyResult prop;
     312        4396 :     if (!LookupPropertyPure(cx, obj, id, &baseHolder, &prop))
     313         947 :         return CanAttachNone;
     314             : 
     315        3449 :     MOZ_ASSERT(!holder);
     316        3449 :     if (baseHolder) {
     317        3248 :         if (!baseHolder->isNative())
     318           8 :             return CanAttachNone;
     319        3240 :         holder.set(&baseHolder->as<NativeObject>());
     320             :     }
     321        3441 :     shape.set(prop.maybeShape());
     322             : 
     323        3441 :     if (IsCacheableGetPropReadSlotForIonOrCacheIR(obj, holder, prop))
     324        3010 :         return CanAttachReadSlot;
     325             : 
     326             :     // Idempotent ICs only support plain data properties, see
     327             :     // tryAttachIdempotentStub.
     328         431 :     if (!pc)
     329           0 :         return CanAttachNone;
     330             : 
     331         431 :     if (IsCacheableNoProperty(cx, obj, holder, shape, id, pc))
     332         201 :         return CanAttachReadSlot;
     333             : 
     334         230 :     if (canAttachGetter == CanAttachGetter::No)
     335           0 :         return CanAttachNone;
     336             : 
     337         230 :     if (IsCacheableGetPropCallScripted(obj, holder, shape, isTemporarilyUnoptimizable))
     338          66 :         return CanAttachCallGetter;
     339             : 
     340         164 :     if (IsCacheableGetPropCallNative(obj, holder, shape))
     341         110 :         return CanAttachCallGetter;
     342             : 
     343          54 :     return CanAttachNone;
     344             : }
     345             : 
     346             : static void
     347         994 : GeneratePrototypeGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder, ObjOperandId objId)
     348             : {
     349             :     // The guards here protect against the effects of JSObject::swap(). If the
     350             :     // prototype chain is directly altered, then TI will toss the jitcode, so we
     351             :     // don't have to worry about it, and any other change to the holder, or
     352             :     // adding a shadowing property will result in reshaping the holder, and thus
     353             :     // the failure of the shape guard.
     354         994 :     MOZ_ASSERT(obj != holder);
     355             : 
     356         994 :     if (obj->hasUncacheableProto()) {
     357             :         // If the shape does not imply the proto, emit an explicit proto guard.
     358          88 :         writer.guardProto(objId, obj->staticPrototype());
     359             :     }
     360             : 
     361         994 :     JSObject* pobj = obj->staticPrototype();
     362         994 :     if (!pobj)
     363           4 :         return;
     364             : 
     365         600 :     while (pobj != holder) {
     366         600 :         if (pobj->hasUncacheableProto()) {
     367           0 :             ObjOperandId protoId = writer.loadObject(pobj);
     368           0 :             if (pobj->isSingleton()) {
     369             :                 // Singletons can have their group's |proto| mutated directly.
     370           0 :                 writer.guardProto(protoId, pobj->staticPrototype());
     371             :             } else {
     372           0 :                 writer.guardGroup(protoId, pobj->group());
     373             :             }
     374             :         }
     375         600 :         pobj = pobj->staticPrototype();
     376             :     }
     377             : }
     378             : 
     379             : static void
     380           2 : GeneratePrototypeHoleGuards(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
     381             : {
     382           2 :     if (obj->hasUncacheableProto()) {
     383             :         // If the shape does not imply the proto, emit an explicit proto guard.
     384           0 :         writer.guardProto(objId, obj->staticPrototype());
     385             :     }
     386             : 
     387           2 :     JSObject* pobj = obj->staticPrototype();
     388           6 :     while (pobj) {
     389           2 :         ObjOperandId protoId = writer.loadObject(pobj);
     390             : 
     391             :         // Non-singletons with uncacheable protos can change their proto
     392             :         // without a shape change, so also guard on the group (which determines
     393             :         // the proto) in this case.
     394           2 :         if (pobj->hasUncacheableProto() && !pobj->isSingleton())
     395           0 :             writer.guardGroup(protoId, pobj->group());
     396             : 
     397             :         // Make sure the shape matches, to avoid non-dense elements or anything
     398             :         // else that is being checked by CanAttachDenseElementHole.
     399           2 :         writer.guardShape(protoId, pobj->as<NativeObject>().lastProperty());
     400             : 
     401             :         // Also make sure there are no dense elements.
     402           2 :         writer.guardNoDenseElements(protoId);
     403             : 
     404           2 :         pobj = pobj->staticPrototype();
     405             :     }
     406           2 : }
     407             : 
     408             : static void
     409        3434 : TestMatchingReceiver(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId,
     410             :                      Maybe<ObjOperandId>* expandoId)
     411             : {
     412        3434 :     if (obj->is<UnboxedPlainObject>()) {
     413           0 :         writer.guardGroup(objId, obj->group());
     414             : 
     415           0 :         if (UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando()) {
     416           0 :             expandoId->emplace(writer.guardAndLoadUnboxedExpando(objId));
     417           0 :             writer.guardShape(expandoId->ref(), expando->lastProperty());
     418             :         } else {
     419           0 :             writer.guardNoUnboxedExpando(objId);
     420             :         }
     421        3434 :     } else if (obj->is<UnboxedArrayObject>() || obj->is<TypedObject>()) {
     422           0 :         writer.guardGroup(objId, obj->group());
     423             :     } else {
     424        3434 :         Shape* shape = obj->maybeShape();
     425        3434 :         MOZ_ASSERT(shape);
     426        3434 :         writer.guardShape(objId, shape);
     427             :     }
     428        3434 : }
     429             : 
     430             : static void
     431        3273 : EmitReadSlotGuard(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     432             :                   ObjOperandId objId, Maybe<ObjOperandId>* holderId)
     433             : {
     434        6546 :     Maybe<ObjOperandId> expandoId;
     435        3273 :     TestMatchingReceiver(writer, obj, objId, &expandoId);
     436             : 
     437        3273 :     if (obj != holder) {
     438         897 :         GeneratePrototypeGuards(writer, obj, holder, objId);
     439             : 
     440         897 :         if (holder) {
     441             :             // Guard on the holder's shape.
     442         652 :             holderId->emplace(writer.loadObject(holder));
     443         652 :             writer.guardShape(holderId->ref(), holder->as<NativeObject>().lastProperty());
     444             :         } else {
     445             :             // The property does not exist. Guard on everything in the prototype
     446             :             // chain. This is guaranteed to see only Native objects because of
     447             :             // CanAttachNativeGetProp().
     448         245 :             JSObject* proto = obj->taggedProto().toObjectOrNull();
     449         245 :             ObjOperandId lastObjId = objId;
     450         927 :             while (proto) {
     451         341 :                 ObjOperandId protoId = writer.loadProto(lastObjId);
     452         341 :                 writer.guardShape(protoId, proto->as<NativeObject>().lastProperty());
     453         341 :                 proto = proto->staticPrototype();
     454         341 :                 lastObjId = protoId;
     455             :             }
     456             :         }
     457        2376 :     } else if (obj->is<UnboxedPlainObject>() && expandoId.isSome()) {
     458           0 :         holderId->emplace(*expandoId);
     459             :     } else {
     460        2376 :         holderId->emplace(objId);
     461             :     }
     462        3273 : }
     463             : 
     464             : static void
     465        3150 : EmitReadSlotResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     466             :                    Shape* shape, ObjOperandId objId)
     467             : {
     468        6300 :     Maybe<ObjOperandId> holderId;
     469        3150 :     EmitReadSlotGuard(writer, obj, holder, objId, &holderId);
     470             : 
     471        3150 :     if (obj == holder && obj->is<UnboxedPlainObject>())
     472           0 :         holder = obj->as<UnboxedPlainObject>().maybeExpando();
     473             : 
     474             :     // Slot access.
     475        3150 :     if (holder) {
     476        2957 :         MOZ_ASSERT(holderId->valid());
     477        2957 :         EmitLoadSlotResult(writer, *holderId, &holder->as<NativeObject>(), shape);
     478             :     } else {
     479         193 :         MOZ_ASSERT(holderId.isNothing());
     480         193 :         writer.loadUndefinedResult();
     481             :     }
     482        3150 : }
     483             : 
     484             : static void
     485        3150 : EmitReadSlotReturn(CacheIRWriter& writer, JSObject*, JSObject* holder, Shape* shape,
     486             :                    bool wrapResult = false)
     487             : {
     488             :     // Slot access.
     489        3150 :     if (holder) {
     490        2957 :         MOZ_ASSERT(shape);
     491        2957 :         if (wrapResult)
     492         477 :             writer.wrapResult();
     493        2957 :         writer.typeMonitorResult();
     494             :     } else {
     495             :         // Normally for this op, the result would have to be monitored by TI.
     496             :         // However, since this stub ALWAYS returns UndefinedValue(), and we can be sure
     497             :         // that undefined is already registered with the type-set, this can be avoided.
     498         193 :         writer.returnFromIC();
     499             :     }
     500        3150 : }
     501             : 
     502             : static void
     503         178 : EmitCallGetterResultNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     504             :                              Shape* shape, ObjOperandId receiverId)
     505             : {
     506         178 :     if (IsCacheableGetPropCallNative(obj, holder, shape)) {
     507         112 :         JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
     508         112 :         MOZ_ASSERT(target->isNative());
     509         112 :         writer.callNativeGetterResult(receiverId, target);
     510         112 :         writer.typeMonitorResult();
     511         112 :         return;
     512             :     }
     513             : 
     514          66 :     MOZ_ASSERT(IsCacheableGetPropCallScripted(obj, holder, shape));
     515             : 
     516          66 :     JSFunction* target = &shape->getterValue().toObject().as<JSFunction>();
     517          66 :     MOZ_ASSERT(target->hasJITCode());
     518          66 :     writer.callScriptedGetterResult(receiverId, target);
     519          66 :     writer.typeMonitorResult();
     520             : }
     521             : 
     522             : static void
     523         168 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder, Shape* shape,
     524             :                      ObjOperandId objId, ObjOperandId receiverId, ICState::Mode mode)
     525             : {
     526             :     // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
     527             :     // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
     528             :     // require outerizing).
     529         168 :     if (mode == ICState::Mode::Specialized || IsWindow(obj)) {
     530         304 :         Maybe<ObjOperandId> expandoId;
     531         152 :         TestMatchingReceiver(writer, obj, objId, &expandoId);
     532             : 
     533         152 :         if (obj != holder) {
     534          82 :             GeneratePrototypeGuards(writer, obj, holder, objId);
     535             : 
     536             :             // Guard on the holder's shape.
     537          82 :             ObjOperandId holderId = writer.loadObject(holder);
     538          82 :             writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
     539             :         }
     540             :     } else {
     541          16 :         writer.guardHasGetterSetter(objId, shape);
     542             :     }
     543             : 
     544         168 :     EmitCallGetterResultNoGuards(writer, obj, holder, shape, receiverId);
     545         168 : }
     546             : 
     547             : static void
     548           0 : EmitCallGetterResult(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
     549             :                      Shape* shape, ObjOperandId objId, ICState::Mode mode)
     550             : {
     551           0 :     EmitCallGetterResult(writer, obj, holder, shape, objId, objId, mode);
     552           0 : }
     553             : 
     554             : void
     555          61 : GetPropIRGenerator::attachMegamorphicNativeSlot(ObjOperandId objId, jsid id, bool handleMissing)
     556             : {
     557          61 :     MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
     558             : 
     559             :     // The stub handles the missing-properties case only if we're seeing one
     560             :     // now, to make sure Ion ICs correctly monitor the undefined type.
     561             : 
     562          61 :     if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
     563          28 :         writer.megamorphicLoadSlotResult(objId, JSID_TO_ATOM(id)->asPropertyName(),
     564          28 :                                          handleMissing);
     565             :     } else {
     566          33 :         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
     567          33 :         writer.megamorphicLoadSlotByValueResult(objId, getElemKeyValueId(), handleMissing);
     568             :     }
     569          61 :     writer.typeMonitorResult();
     570             : 
     571          61 :     trackAttached(handleMissing ? "MegamorphicMissingNativeSlot" : "MegamorphicNativeSlot");
     572          61 : }
     573             : 
     574             : bool
     575        3798 : GetPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId, HandleId id)
     576             : {
     577        7596 :     RootedShape shape(cx_);
     578        7596 :     RootedNativeObject holder(cx_);
     579             : 
     580        7596 :     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, obj, id, &holder, &shape, pc_,
     581             :                                                             canAttachGetter_,
     582        3798 :                                                             isTemporarilyUnoptimizable_);
     583        3798 :     MOZ_ASSERT_IF(idempotent(),
     584             :                   type == CanAttachNone || (type == CanAttachReadSlot && holder));
     585        3798 :     switch (type) {
     586             :       case CanAttachNone:
     587        1000 :         return false;
     588             :       case CanAttachReadSlot:
     589        2630 :         if (mode_ == ICState::Mode::Megamorphic) {
     590          61 :             attachMegamorphicNativeSlot(objId, id, holder == nullptr);
     591          61 :             return true;
     592             :         }
     593             : 
     594        2569 :         maybeEmitIdGuard(id);
     595        2569 :         if (holder) {
     596        2443 :             EnsureTrackPropertyTypes(cx_, holder, id);
     597        2443 :             if (obj == holder) {
     598             :                 // See the comment in StripPreliminaryObjectStubs.
     599        1859 :                 if (IsPreliminaryObject(obj))
     600         372 :                     preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
     601             :                 else
     602        1487 :                     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
     603             :             }
     604             :         }
     605        2569 :         EmitReadSlotResult(writer, obj, holder, shape, objId);
     606        2569 :         EmitReadSlotReturn(writer, obj, holder, shape);
     607             : 
     608        2569 :         trackAttached("NativeSlot");
     609        2569 :         return true;
     610             :       case CanAttachCallGetter: {
     611             :         // |super.prop| accesses use a |this| value that differs from lookup object
     612         168 :         ObjOperandId receiverId = isSuper() ? writer.guardIsObject(getSuperReceiverValueId())
     613         168 :                                             : objId;
     614         168 :         maybeEmitIdGuard(id);
     615         168 :         EmitCallGetterResult(writer, obj, holder, shape, objId, receiverId, mode_);
     616             : 
     617         168 :         trackAttached("NativeGetter");
     618         168 :         return true;
     619             :       }
     620             :     }
     621             : 
     622           0 :     MOZ_CRASH("Bad NativeGetPropCacheability");
     623             : }
     624             : 
     625             : bool
     626         992 : GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id)
     627             : {
     628             :     // Attach a stub when the receiver is a WindowProxy and we can do the lookup
     629             :     // on the Window (the global object).
     630             : 
     631         992 :     if (!IsWindowProxy(obj))
     632         992 :         return false;
     633             : 
     634             :     // If we're megamorphic prefer a generic proxy stub that handles a lot more
     635             :     // cases.
     636           0 :     if (mode_ == ICState::Mode::Megamorphic)
     637           0 :         return false;
     638             : 
     639             :     // This must be a WindowProxy for the current Window/global. Else it would
     640             :     // be a cross-compartment wrapper and IsWindowProxy returns false for
     641             :     // those.
     642           0 :     MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
     643           0 :     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
     644             : 
     645             :     // Now try to do the lookup on the Window (the current global).
     646           0 :     HandleObject windowObj = cx_->global();
     647           0 :     RootedShape shape(cx_);
     648           0 :     RootedNativeObject holder(cx_);
     649           0 :     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
     650             :                                                             canAttachGetter_,
     651           0 :                                                             isTemporarilyUnoptimizable_);
     652           0 :     switch (type) {
     653             :       case CanAttachNone:
     654           0 :         return false;
     655             : 
     656             :       case CanAttachReadSlot: {
     657           0 :         maybeEmitIdGuard(id);
     658           0 :         writer.guardClass(objId, GuardClassKind::WindowProxy);
     659             : 
     660           0 :         ObjOperandId windowObjId = writer.loadObject(windowObj);
     661           0 :         EmitReadSlotResult(writer, windowObj, holder, shape, windowObjId);
     662           0 :         EmitReadSlotReturn(writer, windowObj, holder, shape);
     663             : 
     664           0 :         trackAttached("WindowProxySlot");
     665           0 :         return true;
     666             :       }
     667             : 
     668             :       case CanAttachCallGetter: {
     669           0 :         if (!IsCacheableGetPropCallNative(windowObj, holder, shape))
     670           0 :             return false;
     671             : 
     672             :         // Make sure the native getter is okay with the IC passing the Window
     673             :         // instead of the WindowProxy as |this| value.
     674           0 :         JSFunction* callee = &shape->getterObject()->as<JSFunction>();
     675           0 :         MOZ_ASSERT(callee->isNative());
     676           0 :         if (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())
     677           0 :             return false;
     678             : 
     679             :         // If a |super| access, it is not worth the complexity to attach an IC.
     680           0 :         if (isSuper())
     681           0 :             return false;
     682             : 
     683             :         // Guard the incoming object is a WindowProxy and inline a getter call based
     684             :         // on the Window object.
     685           0 :         maybeEmitIdGuard(id);
     686           0 :         writer.guardClass(objId, GuardClassKind::WindowProxy);
     687           0 :         ObjOperandId windowObjId = writer.loadObject(windowObj);
     688           0 :         EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId, mode_);
     689             : 
     690           0 :         trackAttached("WindowProxyGetter");
     691           0 :         return true;
     692             :       }
     693             :     }
     694             : 
     695           0 :     MOZ_CRASH("Unreachable");
     696             : }
     697             : 
     698             : bool
     699         992 : GetPropIRGenerator::tryAttachCrossCompartmentWrapper(HandleObject obj, ObjOperandId objId,
     700             :                                                      HandleId id)
     701             : {
     702             :     // We can only optimize this very wrapper-handler, because others might
     703             :     // have a security policy.
     704         992 :     if (!IsWrapper(obj) || Wrapper::wrapperHandler(obj) != &CrossCompartmentWrapper::singleton)
     705         339 :         return false;
     706             : 
     707             :     // If we're megamorphic prefer a generic proxy stub that handles a lot more
     708             :     // cases.
     709         653 :     if (mode_ == ICState::Mode::Megamorphic)
     710          31 :         return false;
     711             : 
     712        1244 :     RootedObject unwrapped(cx_, Wrapper::wrappedObject(obj));
     713         622 :     MOZ_ASSERT(unwrapped == UnwrapOneChecked(obj));
     714             : 
     715             :     // If we allowed different zones we would have to wrap strings.
     716         622 :     if (unwrapped->compartment()->zone() != cx_->compartment()->zone())
     717          66 :         return false;
     718             : 
     719        1112 :     RootedObject wrappedGlobal(cx_, &obj->global());
     720         556 :     if (!cx_->compartment()->wrap(cx_, &wrappedGlobal))
     721           0 :         return false;
     722             : 
     723        1112 :     AutoCompartment ac(cx_, unwrapped);
     724             : 
     725             :     // The first CCW for iframes is almost always wrapping another WindowProxy
     726             :     // so we optimize for that case as well.
     727         556 :     bool isWindowProxy = IsWindowProxy(unwrapped);
     728         556 :     if (isWindowProxy) {
     729           0 :         MOZ_ASSERT(ToWindowIfWindowProxy(unwrapped) == unwrapped->compartment()->maybeGlobal());
     730           0 :         unwrapped = cx_->global();
     731           0 :         MOZ_ASSERT(unwrapped);
     732             :     }
     733             : 
     734        1112 :     RootedShape shape(cx_);
     735        1112 :     RootedNativeObject holder(cx_);
     736             :     NativeGetPropCacheability canCache =
     737        1112 :         CanAttachNativeGetProp(cx_, unwrapped, id, &holder, &shape, pc_, canAttachGetter_,
     738         556 :                                isTemporarilyUnoptimizable_);
     739         556 :     if (canCache != CanAttachReadSlot)
     740          12 :         return false;
     741             : 
     742         544 :     if (holder) {
     743         477 :         EnsureTrackPropertyTypes(cx_, holder, id);
     744         477 :         if (unwrapped == holder) {
     745             :             // See the comment in StripPreliminaryObjectStubs.
     746         410 :             if (IsPreliminaryObject(unwrapped))
     747          64 :                 preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
     748             :             else
     749         346 :                 preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
     750             :         }
     751             :     }
     752             : 
     753         544 :     maybeEmitIdGuard(id);
     754         544 :     writer.guardIsProxy(objId);
     755         544 :     writer.guardIsCrossCompartmentWrapper(objId);
     756             : 
     757             :     // Load the object wrapped by the CCW
     758         544 :     ObjOperandId wrapperTargetId = writer.loadWrapperTarget(objId);
     759             : 
     760             :     // If the compartment of the wrapped object is different we should fail.
     761         544 :     writer.guardCompartment(wrapperTargetId, wrappedGlobal, unwrapped->compartment());
     762             : 
     763         544 :     ObjOperandId unwrappedId = wrapperTargetId;
     764         544 :     if (isWindowProxy) {
     765             :         // For the WindowProxy case also unwrap the inner window.
     766             :         // We avoid loadObject, because storing cross compartment objects in
     767             :         // stubs / JIT code is tricky.
     768           0 :         writer.guardClass(wrapperTargetId, GuardClassKind::WindowProxy);
     769           0 :         unwrappedId = writer.loadWrapperTarget(wrapperTargetId);
     770             :     }
     771             : 
     772         544 :     EmitReadSlotResult(writer, unwrapped, holder, shape, unwrappedId);
     773         544 :     EmitReadSlotReturn(writer, unwrapped, holder, shape, /* wrapResult = */ true);
     774             : 
     775         544 :     trackAttached("CCWSlot");
     776         544 :     return true;
     777             : }
     778             : 
     779             : bool
     780         112 : GetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
     781             :                                           bool handleDOMProxies)
     782             : {
     783         112 :     MOZ_ASSERT(obj->is<ProxyObject>());
     784             : 
     785         112 :     writer.guardIsProxy(objId);
     786             : 
     787         112 :     if (!handleDOMProxies) {
     788             :         // Ensure that the incoming object is not a DOM proxy, so that we can get to
     789             :         // the specialized stubs
     790          81 :         writer.guardNotDOMProxy(objId);
     791             :     }
     792             : 
     793         112 :     if (cacheKind_ == CacheKind::GetProp || mode_ == ICState::Mode::Specialized) {
     794         104 :         MOZ_ASSERT(!isSuper());
     795         104 :         maybeEmitIdGuard(id);
     796         104 :         writer.callProxyGetResult(objId, id);
     797             :     } else {
     798             :         // Attach a stub that handles every id.
     799           8 :         MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
     800           8 :         MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
     801           8 :         MOZ_ASSERT(!isSuper());
     802           8 :         writer.callProxyGetByValueResult(objId, getElemKeyValueId());
     803             :     }
     804             : 
     805         112 :     writer.typeMonitorResult();
     806             : 
     807         112 :     trackAttached("GenericProxy");
     808         112 :     return true;
     809             : }
     810             : 
     811             : ObjOperandId
     812           0 : IRGenerator::guardDOMProxyExpandoObjectAndShape(JSObject* obj, ObjOperandId objId,
     813             :                                                 const Value& expandoVal, JSObject* expandoObj)
     814             : {
     815           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     816             : 
     817           0 :     writer.guardShape(objId, obj->maybeShape());
     818             : 
     819             :     // Shape determines Class, so now it must be a DOM proxy.
     820           0 :     ValOperandId expandoValId;
     821           0 :     if (expandoVal.isObject())
     822           0 :         expandoValId = writer.loadDOMExpandoValue(objId);
     823             :     else
     824           0 :         expandoValId = writer.loadDOMExpandoValueIgnoreGeneration(objId);
     825             : 
     826             :     // Guard the expando is an object and shape guard.
     827           0 :     ObjOperandId expandoObjId = writer.guardIsObject(expandoValId);
     828           0 :     writer.guardShape(expandoObjId, expandoObj->as<NativeObject>().shape());
     829           0 :     return expandoObjId;
     830             : }
     831             : 
     832             : bool
     833           0 : GetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id)
     834             : {
     835           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     836             : 
     837           0 :     RootedValue expandoVal(cx_, GetProxyPrivate(obj));
     838           0 :     RootedObject expandoObj(cx_);
     839           0 :     if (expandoVal.isObject()) {
     840           0 :         expandoObj = &expandoVal.toObject();
     841             :     } else {
     842           0 :         MOZ_ASSERT(!expandoVal.isUndefined(),
     843             :                    "How did a missing expando manage to shadow things?");
     844           0 :         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
     845           0 :         MOZ_ASSERT(expandoAndGeneration);
     846           0 :         expandoObj = &expandoAndGeneration->expando.toObject();
     847             :     }
     848             : 
     849             :     // Try to do the lookup on the expando object.
     850           0 :     RootedNativeObject holder(cx_);
     851           0 :     RootedShape propShape(cx_);
     852             :     NativeGetPropCacheability canCache =
     853           0 :         CanAttachNativeGetProp(cx_, expandoObj, id, &holder, &propShape, pc_,
     854           0 :                                canAttachGetter_, isTemporarilyUnoptimizable_);
     855           0 :     if (canCache != CanAttachReadSlot && canCache != CanAttachCallGetter)
     856           0 :         return false;
     857           0 :     if (!holder)
     858           0 :         return false;
     859             : 
     860           0 :     MOZ_ASSERT(holder == expandoObj);
     861             : 
     862           0 :     maybeEmitIdGuard(id);
     863             :     ObjOperandId expandoObjId =
     864           0 :         guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
     865             : 
     866           0 :     if (canCache == CanAttachReadSlot) {
     867             :         // Load from the expando's slots.
     868           0 :         EmitLoadSlotResult(writer, expandoObjId, &expandoObj->as<NativeObject>(), propShape);
     869           0 :         writer.typeMonitorResult();
     870             :     } else {
     871             :         // Call the getter. Note that we pass objId, the DOM proxy, as |this|
     872             :         // and not the expando object.
     873           0 :         MOZ_ASSERT(canCache == CanAttachCallGetter);
     874           0 :         EmitCallGetterResultNoGuards(writer, expandoObj, expandoObj, propShape, objId);
     875             :     }
     876             : 
     877           0 :     trackAttached("DOMProxyExpando");
     878           0 :     return true;
     879             : }
     880             : 
     881             : bool
     882           0 : GetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id)
     883             : {
     884           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     885             : 
     886           0 :     maybeEmitIdGuard(id);
     887           0 :     writer.guardShape(objId, obj->maybeShape());
     888             : 
     889             :     // No need for more guards: we know this is a DOM proxy, since the shape
     890             :     // guard enforces a given JSClass, so just go ahead and emit the call to
     891             :     // ProxyGet.
     892           0 :     MOZ_ASSERT(!isSuper());
     893           0 :     writer.callProxyGetResult(objId, id);
     894           0 :     writer.typeMonitorResult();
     895             : 
     896           0 :     trackAttached("DOMProxyShadowed");
     897           0 :     return true;
     898             : }
     899             : 
     900             : // Callers are expected to have already guarded on the shape of the
     901             : // object, which guarantees the object is a DOM proxy.
     902             : static void
     903           6 : CheckDOMProxyExpandoDoesNotShadow(CacheIRWriter& writer, JSObject* obj, jsid id,
     904             :                                   ObjOperandId objId)
     905             : {
     906           6 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     907             : 
     908           6 :     Value expandoVal = GetProxyPrivate(obj);
     909             : 
     910           6 :     ValOperandId expandoId;
     911           6 :     if (!expandoVal.isObject() && !expandoVal.isUndefined()) {
     912           0 :         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
     913           0 :         expandoId = writer.loadDOMExpandoValueGuardGeneration(objId, expandoAndGeneration);
     914           0 :         expandoVal = expandoAndGeneration->expando;
     915             :     } else {
     916           6 :         expandoId = writer.loadDOMExpandoValue(objId);
     917             :     }
     918             : 
     919           6 :     if (expandoVal.isUndefined()) {
     920             :         // Guard there's no expando object.
     921           6 :         writer.guardType(expandoId, JSVAL_TYPE_UNDEFINED);
     922           0 :     } else if (expandoVal.isObject()) {
     923             :         // Guard the proxy either has no expando object or, if it has one, that
     924             :         // the shape matches the current expando object.
     925           0 :         NativeObject& expandoObj = expandoVal.toObject().as<NativeObject>();
     926           0 :         MOZ_ASSERT(!expandoObj.containsPure(id));
     927           0 :         writer.guardDOMExpandoMissingOrGuardShape(expandoId, expandoObj.lastProperty());
     928             :     } else {
     929           0 :         MOZ_CRASH("Invalid expando value");
     930             :     }
     931           6 : }
     932             : 
     933             : bool
     934           5 : GetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id)
     935             : {
     936           5 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
     937             : 
     938          10 :     RootedObject checkObj(cx_, obj->staticPrototype());
     939           5 :     if (!checkObj)
     940           0 :         return false;
     941             : 
     942          10 :     RootedNativeObject holder(cx_);
     943          10 :     RootedShape shape(cx_);
     944          10 :     NativeGetPropCacheability canCache = CanAttachNativeGetProp(cx_, checkObj, id, &holder, &shape,
     945             :                                                                 pc_, canAttachGetter_,
     946           5 :                                                                 isTemporarilyUnoptimizable_);
     947           5 :     MOZ_ASSERT_IF(idempotent(),
     948             :                   canCache == CanAttachNone || (canCache == CanAttachReadSlot && holder));
     949           5 :     if (canCache == CanAttachNone)
     950           0 :         return false;
     951             : 
     952           5 :     maybeEmitIdGuard(id);
     953           5 :     writer.guardShape(objId, obj->maybeShape());
     954             : 
     955             :     // Guard that our expando object hasn't started shadowing this property.
     956           5 :     CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
     957             : 
     958           5 :     if (holder) {
     959             :         // Found the property on the prototype chain. Treat it like a native
     960             :         // getprop.
     961           5 :         GeneratePrototypeGuards(writer, obj, holder, objId);
     962             : 
     963             :         // Guard on the holder of the property.
     964           5 :         ObjOperandId holderId = writer.loadObject(holder);
     965           5 :         writer.guardShape(holderId, holder->lastProperty());
     966             : 
     967           5 :         if (canCache == CanAttachReadSlot) {
     968           0 :             EmitLoadSlotResult(writer, holderId, holder, shape);
     969           0 :             writer.typeMonitorResult();
     970             :         } else {
     971             :             // EmitCallGetterResultNoGuards expects |obj| to be the object the
     972             :             // property is on to do some checks. Since we actually looked at
     973             :             // checkObj, and no extra guards will be generated, we can just
     974             :             // pass that instead.
     975           5 :             MOZ_ASSERT(canCache == CanAttachCallGetter);
     976           5 :             MOZ_ASSERT(!isSuper());
     977           5 :             EmitCallGetterResultNoGuards(writer, checkObj, holder, shape, objId);
     978             :         }
     979             :     } else {
     980             :         // Property was not found on the prototype chain. Deoptimize down to
     981             :         // proxy get call.
     982           0 :         MOZ_ASSERT(!isSuper());
     983           0 :         writer.callProxyGetResult(objId, id);
     984           0 :         writer.typeMonitorResult();
     985             :     }
     986             : 
     987           5 :     trackAttached("DOMProxyUnshadowed");
     988           5 :     return true;
     989             : }
     990             : 
     991             : bool
     992         448 : GetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id)
     993             : {
     994         448 :     ProxyStubType type = GetProxyStubType(cx_, obj, id);
     995         448 :     if (type == ProxyStubType::None)
     996         331 :         return false;
     997             : 
     998             :     // The proxy stubs don't currently support |super| access.
     999         117 :     if (isSuper())
    1000           0 :         return false;
    1001             : 
    1002         117 :     if (mode_ == ICState::Mode::Megamorphic)
    1003          31 :         return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
    1004             : 
    1005          86 :     switch (type) {
    1006             :       case ProxyStubType::None:
    1007           0 :         break;
    1008             :       case ProxyStubType::DOMExpando:
    1009           0 :         if (tryAttachDOMProxyExpando(obj, objId, id))
    1010           0 :             return true;
    1011           0 :         if (*isTemporarilyUnoptimizable_) {
    1012             :             // Scripted getter without JIT code. Just wait.
    1013           0 :             return false;
    1014             :         }
    1015             :         MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
    1016             :       case ProxyStubType::DOMShadowed:
    1017           0 :         return tryAttachDOMProxyShadowed(obj, objId, id);
    1018             :       case ProxyStubType::DOMUnshadowed:
    1019           5 :         if (tryAttachDOMProxyUnshadowed(obj, objId, id))
    1020           5 :             return true;
    1021           0 :         if (*isTemporarilyUnoptimizable_) {
    1022             :             // Scripted getter without JIT code. Just wait.
    1023           0 :             return false;
    1024             :         }
    1025           0 :         return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ true);
    1026             :       case ProxyStubType::Generic:
    1027          81 :         return tryAttachGenericProxy(obj, objId, id, /* handleDOMProxies = */ false);
    1028             :     }
    1029             : 
    1030           0 :     MOZ_CRASH("Unexpected ProxyStubType");
    1031             : }
    1032             : 
    1033             : bool
    1034        1000 : GetPropIRGenerator::tryAttachUnboxed(HandleObject obj, ObjOperandId objId, HandleId id)
    1035             : {
    1036        1000 :     if (!obj->is<UnboxedPlainObject>())
    1037         992 :         return false;
    1038             : 
    1039           8 :     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
    1040           8 :     if (!property)
    1041           0 :         return false;
    1042             : 
    1043           8 :     if (!cx_->runtime()->jitSupportsFloatingPoint)
    1044           0 :         return false;
    1045             : 
    1046           8 :     maybeEmitIdGuard(id);
    1047           8 :     writer.guardGroup(objId, obj->group());
    1048           8 :     writer.loadUnboxedPropertyResult(objId, property->type,
    1049          16 :                                      UnboxedPlainObject::offsetOfData() + property->offset);
    1050           8 :     if (property->type == JSVAL_TYPE_OBJECT)
    1051           3 :         writer.typeMonitorResult();
    1052             :     else
    1053           5 :         writer.returnFromIC();
    1054             : 
    1055           8 :     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    1056             : 
    1057           8 :     trackAttached("Unboxed");
    1058           8 :     return true;
    1059             : }
    1060             : 
    1061             : bool
    1062         992 : GetPropIRGenerator::tryAttachUnboxedExpando(HandleObject obj, ObjOperandId objId, HandleId id)
    1063             : {
    1064         992 :     if (!obj->is<UnboxedPlainObject>())
    1065         992 :         return false;
    1066             : 
    1067           0 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    1068           0 :     if (!expando)
    1069           0 :         return false;
    1070             : 
    1071           0 :     Shape* shape = expando->lookup(cx_, id);
    1072           0 :     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
    1073           0 :         return false;
    1074             : 
    1075           0 :     maybeEmitIdGuard(id);
    1076           0 :     EmitReadSlotResult(writer, obj, obj, shape, objId);
    1077           0 :     EmitReadSlotReturn(writer, obj, obj, shape);
    1078             : 
    1079           0 :     trackAttached("UnboxedExpando");
    1080           0 :     return true;
    1081             : }
    1082             : 
    1083             : bool
    1084         992 : GetPropIRGenerator::tryAttachTypedObject(HandleObject obj, ObjOperandId objId, HandleId id)
    1085             : {
    1086        1984 :     if (!obj->is<TypedObject>() ||
    1087         992 :         !cx_->runtime()->jitSupportsFloatingPoint ||
    1088           0 :         cx_->compartment()->detachedTypedObjects)
    1089             :     {
    1090         992 :         return false;
    1091             :     }
    1092             : 
    1093           0 :     TypedObject* typedObj = &obj->as<TypedObject>();
    1094           0 :     if (!typedObj->typeDescr().is<StructTypeDescr>())
    1095           0 :         return false;
    1096             : 
    1097           0 :     StructTypeDescr* structDescr = &typedObj->typeDescr().as<StructTypeDescr>();
    1098             :     size_t fieldIndex;
    1099           0 :     if (!structDescr->fieldIndex(id, &fieldIndex))
    1100           0 :         return false;
    1101             : 
    1102           0 :     TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
    1103           0 :     if (!fieldDescr->is<SimpleTypeDescr>())
    1104           0 :         return false;
    1105             : 
    1106           0 :     Shape* shape = typedObj->maybeShape();
    1107           0 :     TypedThingLayout layout = GetTypedThingLayout(shape->getObjectClass());
    1108             : 
    1109           0 :     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
    1110           0 :     uint32_t typeDescr = SimpleTypeDescrKey(&fieldDescr->as<SimpleTypeDescr>());
    1111             : 
    1112           0 :     maybeEmitIdGuard(id);
    1113           0 :     writer.guardNoDetachedTypedObjects();
    1114           0 :     writer.guardShape(objId, shape);
    1115           0 :     writer.loadTypedObjectResult(objId, fieldOffset, layout, typeDescr);
    1116             : 
    1117             :     // Only monitor the result if the type produced by this stub might vary.
    1118           0 :     bool monitorLoad = false;
    1119           0 :     if (SimpleTypeDescrKeyIsScalar(typeDescr)) {
    1120           0 :         Scalar::Type type = ScalarTypeFromSimpleTypeDescrKey(typeDescr);
    1121           0 :         monitorLoad = type == Scalar::Uint32;
    1122             :     } else {
    1123           0 :         ReferenceTypeDescr::Type type = ReferenceTypeFromSimpleTypeDescrKey(typeDescr);
    1124           0 :         monitorLoad = type != ReferenceTypeDescr::TYPE_STRING;
    1125             :     }
    1126             : 
    1127           0 :     if (monitorLoad)
    1128           0 :         writer.typeMonitorResult();
    1129             :     else
    1130           0 :         writer.returnFromIC();
    1131             : 
    1132           0 :     trackAttached("TypedObject");
    1133           0 :     return true;
    1134             : }
    1135             : 
    1136             : bool
    1137        3898 : GetPropIRGenerator::tryAttachObjectLength(HandleObject obj, ObjOperandId objId, HandleId id)
    1138             : {
    1139        3898 :     if (!JSID_IS_ATOM(id, cx_->names().length))
    1140        3786 :         return false;
    1141             : 
    1142         112 :     if (obj->is<ArrayObject>()) {
    1143             :         // Make sure int32 is added to the TypeSet before we attach a stub, so
    1144             :         // the stub can return int32 values without monitoring the result.
    1145          99 :         if (obj->as<ArrayObject>().length() > INT32_MAX)
    1146           0 :             return false;
    1147             : 
    1148          99 :         maybeEmitIdGuard(id);
    1149          99 :         writer.guardClass(objId, GuardClassKind::Array);
    1150          99 :         writer.loadInt32ArrayLengthResult(objId);
    1151          99 :         writer.returnFromIC();
    1152             : 
    1153          99 :         trackAttached("ArrayLength");
    1154          99 :         return true;
    1155             :     }
    1156             : 
    1157          13 :     if (obj->is<UnboxedArrayObject>()) {
    1158           0 :         maybeEmitIdGuard(id);
    1159           0 :         writer.guardClass(objId, GuardClassKind::UnboxedArray);
    1160           0 :         writer.loadUnboxedArrayLengthResult(objId);
    1161           0 :         writer.returnFromIC();
    1162             : 
    1163           0 :         trackAttached("UnboxedArrayLength");
    1164           0 :         return true;
    1165             :     }
    1166             : 
    1167          13 :     if (obj->is<ArgumentsObject>() && !obj->as<ArgumentsObject>().hasOverriddenLength()) {
    1168           1 :         maybeEmitIdGuard(id);
    1169           1 :         if (obj->is<MappedArgumentsObject>()) {
    1170           0 :             writer.guardClass(objId, GuardClassKind::MappedArguments);
    1171             :         } else {
    1172           1 :             MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
    1173           1 :             writer.guardClass(objId, GuardClassKind::UnmappedArguments);
    1174             :         }
    1175           1 :         writer.loadArgumentsObjectLengthResult(objId);
    1176           1 :         writer.returnFromIC();
    1177             : 
    1178           1 :         trackAttached("ArgumentsObjectLength");
    1179           1 :         return true;
    1180             :     }
    1181             : 
    1182          12 :     return false;
    1183             : }
    1184             : 
    1185             : bool
    1186         448 : GetPropIRGenerator::tryAttachFunction(HandleObject obj, ObjOperandId objId, HandleId id)
    1187             : {
    1188             :     // Function properties are lazily resolved so they might not be defined yet.
    1189             :     // And we might end up in a situation where we always have a fresh function
    1190             :     // object during the IC generation.
    1191         448 :     if (!obj->is<JSFunction>())
    1192         440 :         return false;
    1193             : 
    1194           8 :     JSObject* holder = nullptr;
    1195           8 :     PropertyResult prop;
    1196             :     // This property exists already, don't attach the stub.
    1197           8 :     if (LookupPropertyPure(cx_, obj, id, &holder, &prop))
    1198           7 :         return false;
    1199             : 
    1200           1 :     JSFunction* fun = &obj->as<JSFunction>();
    1201             : 
    1202           1 :     if (JSID_IS_ATOM(id, cx_->names().length)) {
    1203             :         // length was probably deleted from the function.
    1204           0 :         if (fun->hasResolvedLength())
    1205           0 :             return false;
    1206             : 
    1207             :         // Lazy functions don't store the length.
    1208           0 :         if (fun->isInterpretedLazy())
    1209           0 :             return false;
    1210             : 
    1211           0 :         maybeEmitIdGuard(id);
    1212           0 :         writer.guardClass(objId, GuardClassKind::JSFunction);
    1213           0 :         writer.loadFunctionLengthResult(objId);
    1214           0 :         writer.returnFromIC();
    1215             : 
    1216           0 :         trackAttached("FunctionLength");
    1217           0 :         return true;
    1218             :     }
    1219             : 
    1220           1 :     return false;
    1221             : }
    1222             : 
    1223             : bool
    1224         992 : GetPropIRGenerator::tryAttachModuleNamespace(HandleObject obj, ObjOperandId objId, HandleId id)
    1225             : {
    1226         992 :     if (!obj->is<ModuleNamespaceObject>())
    1227         992 :         return false;
    1228             : 
    1229           0 :     Rooted<ModuleNamespaceObject*> ns(cx_, &obj->as<ModuleNamespaceObject>());
    1230           0 :     RootedModuleEnvironmentObject env(cx_);
    1231           0 :     RootedShape shape(cx_);
    1232           0 :     if (!ns->bindings().lookup(id, env.address(), shape.address()))
    1233           0 :         return false;
    1234             : 
    1235             :     // Don't emit a stub until the target binding has been initialized.
    1236           0 :     if (env->getSlot(shape->slot()).isMagic(JS_UNINITIALIZED_LEXICAL))
    1237           0 :         return false;
    1238             : 
    1239           0 :     if (IsIonEnabled(cx_))
    1240           0 :         EnsureTrackPropertyTypes(cx_, env, shape->propid());
    1241             : 
    1242             :     // Check for the specific namespace object.
    1243           0 :     maybeEmitIdGuard(id);
    1244           0 :     writer.guardSpecificObject(objId, ns);
    1245             : 
    1246           0 :     ObjOperandId envId = writer.loadObject(env);
    1247           0 :     EmitLoadSlotResult(writer, envId, env, shape);
    1248           0 :     writer.typeMonitorResult();
    1249             : 
    1250           0 :     trackAttached("ModuleNamespace");
    1251           0 :     return true;
    1252             : }
    1253             : 
    1254             : bool
    1255          89 : GetPropIRGenerator::tryAttachPrimitive(ValOperandId valId, HandleId id)
    1256             : {
    1257             :     JSValueType primitiveType;
    1258         178 :     RootedNativeObject proto(cx_);
    1259          89 :     if (val_.isString()) {
    1260          51 :         if (JSID_IS_ATOM(id, cx_->names().length)) {
    1261             :             // String length is special-cased, see js::GetProperty.
    1262          13 :             return false;
    1263             :         }
    1264          38 :         primitiveType = JSVAL_TYPE_STRING;
    1265          38 :         proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_String));
    1266          38 :     } else if (val_.isNumber()) {
    1267           0 :         primitiveType = JSVAL_TYPE_DOUBLE;
    1268           0 :         proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Number));
    1269          38 :     } else if (val_.isBoolean()) {
    1270           0 :         primitiveType = JSVAL_TYPE_BOOLEAN;
    1271           0 :         proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Boolean));
    1272          38 :     } else if (val_.isSymbol()) {
    1273           0 :         primitiveType = JSVAL_TYPE_SYMBOL;
    1274           0 :         proto = MaybeNativeObject(GetBuiltinPrototypePure(cx_->global(), JSProto_Symbol));
    1275             :     } else {
    1276          38 :         MOZ_ASSERT(val_.isNullOrUndefined() || val_.isMagic());
    1277          38 :         return false;
    1278             :     }
    1279          38 :     if (!proto)
    1280           1 :         return false;
    1281             : 
    1282          74 :     RootedShape shape(cx_);
    1283          74 :     RootedNativeObject holder(cx_);
    1284          74 :     NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, proto, id, &holder, &shape, pc_,
    1285             :                                                             canAttachGetter_,
    1286          37 :                                                             isTemporarilyUnoptimizable_);
    1287          37 :     if (type != CanAttachReadSlot)
    1288           0 :         return false;
    1289             : 
    1290          37 :     if (holder) {
    1291             :         // Instantiate this property, for use during Ion compilation.
    1292          37 :         if (IsIonEnabled(cx_))
    1293          37 :             EnsureTrackPropertyTypes(cx_, holder, id);
    1294             :     }
    1295             : 
    1296          37 :     writer.guardType(valId, primitiveType);
    1297          37 :     maybeEmitIdGuard(id);
    1298             : 
    1299          37 :     ObjOperandId protoId = writer.loadObject(proto);
    1300          37 :     EmitReadSlotResult(writer, proto, holder, shape, protoId);
    1301          37 :     EmitReadSlotReturn(writer, proto, holder, shape);
    1302             : 
    1303          37 :     trackAttached("Primitive");
    1304          37 :     return true;
    1305             : }
    1306             : 
    1307             : bool
    1308          52 : GetPropIRGenerator::tryAttachStringLength(ValOperandId valId, HandleId id)
    1309             : {
    1310          52 :     if (!val_.isString() || !JSID_IS_ATOM(id, cx_->names().length))
    1311          39 :         return false;
    1312             : 
    1313          13 :     StringOperandId strId = writer.guardIsString(valId);
    1314          13 :     maybeEmitIdGuard(id);
    1315          13 :     writer.loadStringLengthResult(strId);
    1316          13 :     writer.returnFromIC();
    1317             : 
    1318          13 :     trackAttached("StringLength");
    1319          13 :     return true;
    1320             : }
    1321             : 
    1322             : bool
    1323          21 : GetPropIRGenerator::tryAttachStringChar(ValOperandId valId, ValOperandId indexId)
    1324             : {
    1325          21 :     MOZ_ASSERT(idVal_.isInt32());
    1326             : 
    1327          21 :     if (!val_.isString())
    1328          16 :         return false;
    1329             : 
    1330           5 :     int32_t index = idVal_.toInt32();
    1331           5 :     if (index < 0)
    1332           0 :         return false;
    1333             : 
    1334           5 :     JSString* str = val_.toString();
    1335           5 :     if (size_t(index) >= str->length())
    1336           0 :         return false;
    1337             : 
    1338             :     // This follows JSString::getChar, otherwise we fail to attach getChar in a lot of cases.
    1339           5 :     if (str->isRope()) {
    1340           0 :         JSRope* rope = &str->asRope();
    1341             : 
    1342             :         // Make sure the left side contains the index.
    1343           0 :         if (size_t(index) >= rope->leftChild()->length())
    1344           0 :             return false;
    1345             : 
    1346           0 :         str = rope->leftChild();
    1347             :     }
    1348             : 
    1349          10 :     if (!str->isLinear() ||
    1350           5 :         str->asLinear().latin1OrTwoByteChar(index) >= StaticStrings::UNIT_STATIC_LIMIT)
    1351             :     {
    1352           2 :         return false;
    1353             :     }
    1354             : 
    1355           3 :     StringOperandId strId = writer.guardIsString(valId);
    1356           3 :     Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
    1357           3 :     writer.loadStringCharResult(strId, int32IndexId);
    1358           3 :     writer.returnFromIC();
    1359             : 
    1360           3 :     trackAttached("StringChar");
    1361           3 :     return true;
    1362             : }
    1363             : 
    1364             : bool
    1365          39 : GetPropIRGenerator::tryAttachMagicArgumentsName(ValOperandId valId, HandleId id)
    1366             : {
    1367          39 :     if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
    1368           1 :         return false;
    1369             : 
    1370          38 :     if (!JSID_IS_ATOM(id, cx_->names().length) && !JSID_IS_ATOM(id, cx_->names().callee))
    1371           0 :         return false;
    1372             : 
    1373          38 :     maybeEmitIdGuard(id);
    1374          38 :     writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
    1375          38 :     writer.guardFrameHasNoArgumentsObject();
    1376             : 
    1377          38 :     if (JSID_IS_ATOM(id, cx_->names().length)) {
    1378          38 :         writer.loadFrameNumActualArgsResult();
    1379          38 :         writer.returnFromIC();
    1380             :     } else {
    1381           0 :         MOZ_ASSERT(JSID_IS_ATOM(id, cx_->names().callee));
    1382           0 :         writer.loadFrameCalleeResult();
    1383           0 :         writer.typeMonitorResult();
    1384             :     }
    1385             : 
    1386          38 :     trackAttached("MagicArgumentsName");
    1387          38 :     return true;
    1388             : }
    1389             : 
    1390             : bool
    1391          18 : GetPropIRGenerator::tryAttachMagicArgument(ValOperandId valId, ValOperandId indexId)
    1392             : {
    1393          18 :     MOZ_ASSERT(idVal_.isInt32());
    1394             : 
    1395          18 :     if (!val_.isMagic(JS_OPTIMIZED_ARGUMENTS))
    1396           2 :         return false;
    1397             : 
    1398          16 :     writer.guardMagicValue(valId, JS_OPTIMIZED_ARGUMENTS);
    1399          16 :     writer.guardFrameHasNoArgumentsObject();
    1400             : 
    1401          16 :     Int32OperandId int32IndexId = writer.guardIsInt32Index(indexId);
    1402          16 :     writer.loadFrameArgumentResult(int32IndexId);
    1403          16 :     writer.typeMonitorResult();
    1404             : 
    1405          16 :     trackAttached("MagicArgument");
    1406          16 :     return true;
    1407             : }
    1408             : 
    1409             : bool
    1410           0 : GetPropIRGenerator::tryAttachArgumentsObjectArg(HandleObject obj, ObjOperandId objId,
    1411             :                                                 uint32_t index, Int32OperandId indexId)
    1412             : {
    1413           0 :     if (!obj->is<ArgumentsObject>() || obj->as<ArgumentsObject>().hasOverriddenElement())
    1414           0 :         return false;
    1415             : 
    1416           0 :     if (obj->is<MappedArgumentsObject>()) {
    1417           0 :         writer.guardClass(objId, GuardClassKind::MappedArguments);
    1418             :     } else {
    1419           0 :         MOZ_ASSERT(obj->is<UnmappedArgumentsObject>());
    1420           0 :         writer.guardClass(objId, GuardClassKind::UnmappedArguments);
    1421             :     }
    1422             : 
    1423           0 :     writer.loadArgumentsObjectArgResult(objId, indexId);
    1424           0 :     writer.typeMonitorResult();
    1425             : 
    1426           0 :     trackAttached("ArgumentsObjectArg");
    1427           0 :     return true;
    1428             : }
    1429             : 
    1430             : bool
    1431         147 : GetPropIRGenerator::tryAttachDenseElement(HandleObject obj, ObjOperandId objId,
    1432             :                                           uint32_t index, Int32OperandId indexId)
    1433             : {
    1434         147 :     if (!obj->isNative())
    1435           0 :         return false;
    1436             : 
    1437         147 :     if (!obj->as<NativeObject>().containsDenseElement(index))
    1438           2 :         return false;
    1439             : 
    1440         145 :     writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
    1441         145 :     writer.loadDenseElementResult(objId, indexId);
    1442         145 :     writer.typeMonitorResult();
    1443             : 
    1444         145 :     trackAttached("DenseElement");
    1445         145 :     return true;
    1446             : }
    1447             : 
    1448             : static bool
    1449           4 : CanAttachDenseElementHole(JSObject* obj, bool ownProp)
    1450             : {
    1451             :     // Make sure the objects on the prototype don't have any indexed properties
    1452             :     // or that such properties can't appear without a shape change.
    1453             :     // Otherwise returning undefined for holes would obviously be incorrect,
    1454             :     // because we would have to lookup a property on the prototype instead.
    1455             :     do {
    1456             :         // The first two checks are also relevant to the receiver object.
    1457           4 :         if (obj->isIndexed())
    1458           0 :             return false;
    1459             : 
    1460           4 :         if (ClassCanHaveExtraProperties(obj->getClass()))
    1461           0 :             return false;
    1462             : 
    1463             :         // Don't need to check prototype for OwnProperty checks
    1464           4 :         if (ownProp)
    1465           0 :             return true;
    1466             : 
    1467           4 :         JSObject* proto = obj->staticPrototype();
    1468           4 :         if (!proto)
    1469           2 :             break;
    1470             : 
    1471           2 :         if (!proto->isNative())
    1472           0 :             return false;
    1473             : 
    1474             :         // Make sure objects on the prototype don't have dense elements.
    1475           2 :         if (proto->as<NativeObject>().getDenseInitializedLength() != 0)
    1476           0 :             return false;
    1477             : 
    1478           2 :         obj = proto;
    1479             :     } while (true);
    1480             : 
    1481           2 :     return true;
    1482             : }
    1483             : 
    1484             : bool
    1485           2 : GetPropIRGenerator::tryAttachDenseElementHole(HandleObject obj, ObjOperandId objId,
    1486             :                                               uint32_t index, Int32OperandId indexId)
    1487             : {
    1488           2 :     if (!obj->isNative())
    1489           0 :         return false;
    1490             : 
    1491           2 :     if (obj->as<NativeObject>().containsDenseElement(index))
    1492           0 :         return false;
    1493             : 
    1494           2 :     if (!CanAttachDenseElementHole(obj, false))
    1495           0 :         return false;
    1496             : 
    1497             :     // Guard on the shape, to prevent non-dense elements from appearing.
    1498           2 :     writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
    1499             : 
    1500           2 :     GeneratePrototypeHoleGuards(writer, obj, objId);
    1501           2 :     writer.loadDenseElementHoleResult(objId, indexId);
    1502           2 :     writer.typeMonitorResult();
    1503             : 
    1504           2 :     trackAttached("DenseElementHole");
    1505           2 :     return true;
    1506             : }
    1507             : 
    1508             : bool
    1509           0 : GetPropIRGenerator::tryAttachUnboxedArrayElement(HandleObject obj, ObjOperandId objId,
    1510             :                                                  uint32_t index, Int32OperandId indexId)
    1511             : {
    1512           0 :     if (!obj->is<UnboxedArrayObject>())
    1513           0 :         return false;
    1514             : 
    1515           0 :     if (index >= obj->as<UnboxedArrayObject>().initializedLength())
    1516           0 :         return false;
    1517             : 
    1518           0 :     writer.guardGroup(objId, obj->group());
    1519             : 
    1520           0 :     JSValueType elementType = obj->group()->unboxedLayoutDontCheckGeneration().elementType();
    1521           0 :     writer.loadUnboxedArrayElementResult(objId, indexId, elementType);
    1522             : 
    1523             :     // Only monitor the result if its type might change.
    1524           0 :     if (elementType == JSVAL_TYPE_OBJECT)
    1525           0 :         writer.typeMonitorResult();
    1526             :     else
    1527           0 :         writer.returnFromIC();
    1528             : 
    1529           0 :     trackAttached("UnboxedArrayElement");
    1530           0 :     return true;
    1531             : }
    1532             : 
    1533             : bool
    1534         147 : GetPropIRGenerator::tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
    1535             :                                           uint32_t index, Int32OperandId indexId)
    1536             : {
    1537         147 :     if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
    1538         147 :         return false;
    1539             : 
    1540           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
    1541           0 :         return false;
    1542             : 
    1543             :     // Ensure the index is in-bounds so the element type gets monitored.
    1544           0 :     if (obj->is<TypedArrayObject>() && index >= obj->as<TypedArrayObject>().length())
    1545           0 :         return false;
    1546             : 
    1547             :     // Don't attach typed object stubs if the underlying storage could be
    1548             :     // detached, as the stub will always bail out.
    1549           0 :     if (IsPrimitiveArrayTypedObject(obj) && cx_->compartment()->detachedTypedObjects)
    1550           0 :         return false;
    1551             : 
    1552           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    1553           0 :     if (layout != Layout_TypedArray)
    1554           0 :         writer.guardNoDetachedTypedObjects();
    1555             : 
    1556           0 :     writer.guardShape(objId, obj->as<ShapedObject>().shape());
    1557             : 
    1558           0 :     writer.loadTypedElementResult(objId, indexId, layout, TypedThingElementType(obj));
    1559             : 
    1560             :     // Reading from Uint32Array may produce an int32 now but a double value
    1561             :     // later, so ensure we monitor the result.
    1562           0 :     if (TypedThingElementType(obj) == Scalar::Type::Uint32)
    1563           0 :         writer.typeMonitorResult();
    1564             :     else
    1565           0 :         writer.returnFromIC();
    1566             : 
    1567           0 :     trackAttached("TypedElement");
    1568           0 :     return true;
    1569             : }
    1570             : 
    1571             : bool
    1572         181 : GetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId)
    1573             : {
    1574         181 :     if (!obj->is<ProxyObject>())
    1575         169 :         return false;
    1576             : 
    1577             :     // The proxy stubs don't currently support |super| access.
    1578          12 :     if (isSuper())
    1579           0 :         return false;
    1580             : 
    1581          12 :     writer.guardIsProxy(objId);
    1582             : 
    1583             :     // We are not guarding against DOM proxies here, because there is no other
    1584             :     // specialized DOM IC we could attach.
    1585             :     // We could call maybeEmitIdGuard here and then emit CallProxyGetResult,
    1586             :     // but for GetElem we prefer to attach a stub that can handle any Value
    1587             :     // so we don't attach a new stub for every id.
    1588          12 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem);
    1589          12 :     MOZ_ASSERT(!isSuper());
    1590          12 :     writer.callProxyGetByValueResult(objId, getElemKeyValueId());
    1591          12 :     writer.typeMonitorResult();
    1592             : 
    1593          12 :     trackAttached("ProxyElement");
    1594          12 :     return true;
    1595             : }
    1596             : 
    1597             : void
    1598        3833 : GetPropIRGenerator::trackAttached(const char* name)
    1599             : {
    1600             : #ifdef JS_CACHEIR_SPEW
    1601        3833 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    1602        3833 :     if (sp.enabled()) {
    1603           0 :         LockGuard<Mutex> guard(sp.lock());
    1604           0 :         sp.beginCache(guard, *this);
    1605           0 :         sp.valueProperty(guard, "base", val_);
    1606           0 :         sp.valueProperty(guard, "property", idVal_);
    1607           0 :         sp.attached(guard, name);
    1608           0 :         sp.endCache(guard);
    1609             :     }
    1610             : #endif
    1611        3833 : }
    1612             : 
    1613             : void
    1614         356 : GetPropIRGenerator::trackNotAttached()
    1615             : {
    1616             : #ifdef JS_CACHEIR_SPEW
    1617         356 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    1618         356 :     if (sp.enabled()) {
    1619           0 :         LockGuard<Mutex> guard(sp.lock());
    1620           0 :         sp.beginCache(guard, *this);
    1621           0 :         sp.valueProperty(guard, "base", val_);
    1622           0 :         sp.valueProperty(guard, "property", idVal_);
    1623           0 :         sp.endCache(guard);
    1624             :     }
    1625             : #endif
    1626         356 : }
    1627             : 
    1628             : void
    1629         911 : IRGenerator::emitIdGuard(ValOperandId valId, jsid id)
    1630             : {
    1631         911 :     if (JSID_IS_SYMBOL(id)) {
    1632         119 :         SymbolOperandId symId = writer.guardIsSymbol(valId);
    1633         119 :         writer.guardSpecificSymbol(symId, JSID_TO_SYMBOL(id));
    1634             :     } else {
    1635         792 :         MOZ_ASSERT(JSID_IS_ATOM(id));
    1636         792 :         StringOperandId strId = writer.guardIsString(valId);
    1637         792 :         writer.guardSpecificAtom(strId, JSID_TO_ATOM(id));
    1638             :     }
    1639         911 : }
    1640             : 
    1641             : void
    1642        3586 : GetPropIRGenerator::maybeEmitIdGuard(jsid id)
    1643             : {
    1644        3586 :     if (cacheKind_ == CacheKind::GetProp || cacheKind_ == CacheKind::GetPropSuper) {
    1645             :         // Constant PropertyName, no guards necessary.
    1646        3106 :         MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
    1647        3106 :         return;
    1648             :     }
    1649             : 
    1650         480 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetElem || cacheKind_ == CacheKind::GetElemSuper);
    1651         480 :     emitIdGuard(getElemKeyValueId(), id);
    1652             : }
    1653             : 
    1654             : void
    1655         947 : SetPropIRGenerator::maybeEmitIdGuard(jsid id)
    1656             : {
    1657         947 :     if (cacheKind_ == CacheKind::SetProp) {
    1658             :         // Constant PropertyName, no guards necessary.
    1659         639 :         MOZ_ASSERT(&idVal_.toString()->asAtom() == JSID_TO_ATOM(id));
    1660         639 :         return;
    1661             :     }
    1662             : 
    1663         308 :     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    1664         308 :     emitIdGuard(setElemKeyValueId(), id);
    1665             : }
    1666             : 
    1667         977 : GetNameIRGenerator::GetNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    1668             :                                        ICState::Mode mode, HandleObject env,
    1669         977 :                                        HandlePropertyName name)
    1670             :   : IRGenerator(cx, script, pc, CacheKind::GetName, mode),
    1671             :     env_(env),
    1672         977 :     name_(name)
    1673         977 : {}
    1674             : 
    1675             : bool
    1676         977 : GetNameIRGenerator::tryAttachStub()
    1677             : {
    1678         977 :     MOZ_ASSERT(cacheKind_ == CacheKind::GetName);
    1679             : 
    1680        1954 :     AutoAssertNoPendingException aanpe(cx_);
    1681             : 
    1682         977 :     ObjOperandId envId(writer.setInputOperandId(0));
    1683        1954 :     RootedId id(cx_, NameToId(name_));
    1684             : 
    1685         977 :     if (tryAttachGlobalNameValue(envId, id))
    1686         837 :         return true;
    1687         140 :     if (tryAttachGlobalNameGetter(envId, id))
    1688           5 :         return true;
    1689         135 :     if (tryAttachEnvironmentName(envId, id))
    1690          11 :         return true;
    1691             : 
    1692         124 :     trackNotAttached();
    1693         124 :     return false;
    1694             : }
    1695             : 
    1696             : bool
    1697         855 : CanAttachGlobalName(JSContext* cx, Handle<LexicalEnvironmentObject*> globalLexical, HandleId id,
    1698             :                     MutableHandleNativeObject holder, MutableHandleShape shape)
    1699             : {
    1700             :     // The property must be found, and it must be found as a normal data property.
    1701        1710 :     RootedNativeObject current(cx, globalLexical);
    1702             :     while (true) {
    1703        1589 :         shape.set(current->lookup(cx, id));
    1704        1589 :         if (shape)
    1705         849 :             break;
    1706             : 
    1707         740 :         if (current == globalLexical) {
    1708         732 :             current = &globalLexical->global();
    1709             :         } else {
    1710             :             // In the browser the global prototype chain should be immutable.
    1711           8 :             if (!current->staticPrototypeIsImmutable())
    1712           4 :                 return false;
    1713             : 
    1714           4 :             JSObject* proto = current->staticPrototype();
    1715           4 :             if (!proto || !proto->is<NativeObject>())
    1716           2 :                 return false;
    1717             : 
    1718           2 :             current = &proto->as<NativeObject>();
    1719             :         }
    1720         734 :     }
    1721             : 
    1722         849 :     holder.set(current);
    1723         849 :     return true;
    1724             : }
    1725             : 
    1726             : bool
    1727         977 : GetNameIRGenerator::tryAttachGlobalNameValue(ObjOperandId objId, HandleId id)
    1728             : {
    1729         977 :     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    1730         131 :         return false;
    1731             : 
    1732         846 :     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
    1733         846 :     MOZ_ASSERT(globalLexical->isGlobal());
    1734             : 
    1735        1692 :     RootedNativeObject holder(cx_);
    1736        1692 :     RootedShape shape(cx_);
    1737         846 :     if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
    1738           3 :         return false;
    1739             : 
    1740             :     // The property must be found, and it must be found as a normal data property.
    1741         843 :     if (!shape->hasDefaultGetter() || !shape->hasSlot())
    1742           6 :         return false;
    1743             : 
    1744             :     // This might still be an uninitialized lexical.
    1745         837 :     if (holder->getSlot(shape->slot()).isMagic())
    1746           0 :         return false;
    1747             : 
    1748             :     // Instantiate this global property, for use during Ion compilation.
    1749         837 :     if (IsIonEnabled(cx_))
    1750         837 :         EnsureTrackPropertyTypes(cx_, holder, id);
    1751             : 
    1752         837 :     if (holder == globalLexical) {
    1753             :         // There is no need to guard on the shape. Lexical bindings are
    1754             :         // non-configurable, and this stub cannot be shared across globals.
    1755         123 :         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
    1756         123 :         writer.loadDynamicSlotResult(objId, dynamicSlotOffset);
    1757             :     } else {
    1758             :         // Check the prototype chain from the global to the holder
    1759             :         // prototype. Ignore the global lexical scope as it doesn't figure
    1760             :         // into the prototype chain. We guard on the global lexical
    1761             :         // scope's shape independently.
    1762        1428 :         if (!IsCacheableGetPropReadSlotForIonOrCacheIR(&globalLexical->global(), holder,
    1763        1428 :                                                        PropertyResult(shape)))
    1764           0 :             return false;
    1765             : 
    1766             :         // Shape guard for global lexical.
    1767         714 :         writer.guardShape(objId, globalLexical->lastProperty());
    1768             : 
    1769             :         // Guard on the shape of the GlobalObject.
    1770         714 :         ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
    1771         714 :         writer.guardShape(globalId, globalLexical->global().lastProperty());
    1772             : 
    1773         714 :         ObjOperandId holderId = globalId;
    1774         714 :         if (holder != &globalLexical->global()) {
    1775             :             // Shape guard holder.
    1776           0 :             holderId = writer.loadObject(holder);
    1777           0 :             writer.guardShape(holderId, holder->lastProperty());
    1778             :         }
    1779             : 
    1780         714 :         EmitLoadSlotResult(writer, holderId, holder, shape);
    1781             :     }
    1782             : 
    1783         837 :     writer.typeMonitorResult();
    1784             : 
    1785         837 :     trackAttached("GlobalNameValue");
    1786         837 :     return true;
    1787             : }
    1788             : 
    1789             : bool
    1790         140 : GetNameIRGenerator::tryAttachGlobalNameGetter(ObjOperandId objId, HandleId id)
    1791             : {
    1792         140 :     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    1793         131 :         return false;
    1794             : 
    1795           9 :     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
    1796           9 :     MOZ_ASSERT(globalLexical->isGlobal());
    1797             : 
    1798          18 :     RootedNativeObject holder(cx_);
    1799          18 :     RootedShape shape(cx_);
    1800           9 :     if (!CanAttachGlobalName(cx_, globalLexical, id, &holder, &shape))
    1801           3 :         return false;
    1802             : 
    1803           6 :     if (holder == globalLexical)
    1804           0 :         return false;
    1805             : 
    1806           6 :     if (!IsCacheableGetPropCallNative(&globalLexical->global(), holder, shape))
    1807           1 :         return false;
    1808             : 
    1809           5 :     if (IsIonEnabled(cx_))
    1810           5 :         EnsureTrackPropertyTypes(cx_, holder, id);
    1811             : 
    1812             :     // Shape guard for global lexical.
    1813           5 :     writer.guardShape(objId, globalLexical->lastProperty());
    1814             : 
    1815             :     // Guard on the shape of the GlobalObject.
    1816           5 :     ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
    1817           5 :     writer.guardShape(globalId, globalLexical->global().lastProperty());
    1818             : 
    1819           5 :     if (holder != &globalLexical->global()) {
    1820             :         // Shape guard holder.
    1821           0 :         ObjOperandId holderId = writer.loadObject(holder);
    1822           0 :         writer.guardShape(holderId, holder->lastProperty());
    1823             :     }
    1824             : 
    1825           5 :     EmitCallGetterResultNoGuards(writer, &globalLexical->global(), holder, shape, globalId);
    1826             : 
    1827           5 :     trackAttached("GlobalNameGetter");
    1828           5 :     return true;
    1829             : }
    1830             : 
    1831             : static bool
    1832          23 : NeedEnvironmentShapeGuard(JSObject* envObj)
    1833             : {
    1834          23 :     if (!envObj->is<CallObject>())
    1835          19 :         return true;
    1836             : 
    1837             :     // We can skip a guard on the call object if the script's bindings are
    1838             :     // guaranteed to be immutable (and thus cannot introduce shadowing
    1839             :     // variables). The function might have been relazified under rare
    1840             :     // conditions. In that case, we pessimistically create the guard.
    1841           4 :     CallObject* callObj = &envObj->as<CallObject>();
    1842           4 :     JSFunction* fun = &callObj->callee();
    1843           4 :     if (!fun->hasScript() || fun->nonLazyScript()->funHasExtensibleScope())
    1844           4 :         return true;
    1845             : 
    1846           0 :     return false;
    1847             : }
    1848             : 
    1849             : bool
    1850         135 : GetNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
    1851             : {
    1852         135 :     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    1853         124 :         return false;
    1854             : 
    1855          22 :     RootedObject env(cx_, env_);
    1856          22 :     RootedShape shape(cx_);
    1857          22 :     RootedNativeObject holder(cx_);
    1858             : 
    1859          35 :     while (env) {
    1860          23 :         if (env->is<GlobalObject>()) {
    1861           4 :             shape = env->as<GlobalObject>().lookup(cx_, id);
    1862           4 :             if (shape)
    1863           4 :                 break;
    1864           0 :             return false;
    1865             :         }
    1866             : 
    1867          19 :         if (!env->is<EnvironmentObject>() || env->is<WithEnvironmentObject>())
    1868           0 :             return false;
    1869             : 
    1870          19 :         MOZ_ASSERT(!env->hasUncacheableProto());
    1871             : 
    1872             :         // Check for an 'own' property on the env. There is no need to
    1873             :         // check the prototype as non-with scopes do not inherit properties
    1874             :         // from any prototype.
    1875          19 :         shape = env->as<NativeObject>().lookup(cx_, id);
    1876          19 :         if (shape)
    1877           7 :             break;
    1878             : 
    1879          12 :         env = env->enclosingEnvironment();
    1880             :     }
    1881             : 
    1882          11 :     holder = &env->as<NativeObject>();
    1883          11 :     if (!IsCacheableGetPropReadSlotForIonOrCacheIR(holder, holder, PropertyResult(shape)))
    1884           0 :         return false;
    1885          11 :     if (holder->getSlot(shape->slot()).isMagic())
    1886           0 :         return false;
    1887             : 
    1888          11 :     ObjOperandId lastObjId = objId;
    1889          11 :     env = env_;
    1890          35 :     while (env) {
    1891          23 :         if (NeedEnvironmentShapeGuard(env))
    1892          23 :             writer.guardShape(lastObjId, env->maybeShape());
    1893             : 
    1894          23 :         if (env == holder)
    1895          11 :             break;
    1896             : 
    1897          12 :         lastObjId = writer.loadEnclosingEnvironment(lastObjId);
    1898          12 :         env = env->enclosingEnvironment();
    1899             :     }
    1900             : 
    1901          11 :     if (holder->isFixedSlot(shape->slot())) {
    1902           4 :         writer.loadEnvironmentFixedSlotResult(lastObjId, NativeObject::getFixedSlotOffset(shape->slot()));
    1903             :     } else {
    1904           7 :         size_t dynamicSlotOffset = holder->dynamicSlotIndex(shape->slot()) * sizeof(Value);
    1905           7 :         writer.loadEnvironmentDynamicSlotResult(lastObjId, dynamicSlotOffset);
    1906             :     }
    1907          11 :     writer.typeMonitorResult();
    1908             : 
    1909          11 :     trackAttached("EnvironmentName");
    1910          11 :     return true;
    1911             : }
    1912             : 
    1913             : void
    1914         853 : GetNameIRGenerator::trackAttached(const char* name)
    1915             : {
    1916             : #ifdef JS_CACHEIR_SPEW
    1917         853 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    1918         853 :     if (sp.enabled()) {
    1919           0 :         LockGuard<Mutex> guard(sp.lock());
    1920           0 :         sp.beginCache(guard, *this);
    1921           0 :         sp.valueProperty(guard, "base", ObjectValue(*env_));
    1922           0 :         sp.valueProperty(guard, "property", StringValue(name_));
    1923           0 :         sp.attached(guard, name);
    1924           0 :         sp.endCache(guard);
    1925             :     }
    1926             : #endif
    1927         853 : }
    1928             : 
    1929             : void
    1930         124 : GetNameIRGenerator::trackNotAttached()
    1931             : {
    1932             : #ifdef JS_CACHEIR_SPEW
    1933         124 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    1934         124 :     if (sp.enabled()) {
    1935           0 :         LockGuard<Mutex> guard(sp.lock());
    1936           0 :         sp.beginCache(guard, *this);
    1937           0 :         sp.valueProperty(guard, "base", ObjectValue(*env_));
    1938           0 :         sp.valueProperty(guard, "property", StringValue(name_));
    1939           0 :         sp.endCache(guard);
    1940             :     }
    1941             : #endif
    1942         124 : }
    1943             : 
    1944           0 : BindNameIRGenerator::BindNameIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    1945             :                                          ICState::Mode mode, HandleObject env,
    1946           0 :                                          HandlePropertyName name)
    1947             :   : IRGenerator(cx, script, pc, CacheKind::BindName, mode),
    1948             :     env_(env),
    1949           0 :     name_(name)
    1950           0 : {}
    1951             : 
    1952             : bool
    1953           0 : BindNameIRGenerator::tryAttachStub()
    1954             : {
    1955           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::BindName);
    1956             : 
    1957           0 :     AutoAssertNoPendingException aanpe(cx_);
    1958             : 
    1959           0 :     ObjOperandId envId(writer.setInputOperandId(0));
    1960           0 :     RootedId id(cx_, NameToId(name_));
    1961             : 
    1962           0 :     if (tryAttachGlobalName(envId, id))
    1963           0 :         return true;
    1964           0 :     if (tryAttachEnvironmentName(envId, id))
    1965           0 :         return true;
    1966             : 
    1967           0 :     trackNotAttached();
    1968           0 :     return false;
    1969             : }
    1970             : 
    1971             : bool
    1972           0 : BindNameIRGenerator::tryAttachGlobalName(ObjOperandId objId, HandleId id)
    1973             : {
    1974           0 :     if (!IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    1975           0 :         return false;
    1976             : 
    1977           0 :     Handle<LexicalEnvironmentObject*> globalLexical = env_.as<LexicalEnvironmentObject>();
    1978           0 :     MOZ_ASSERT(globalLexical->isGlobal());
    1979             : 
    1980           0 :     JSObject* result = nullptr;
    1981           0 :     if (Shape* shape = globalLexical->lookup(cx_, id)) {
    1982             :         // If this is an uninitialized lexical or a const, we need to return a
    1983             :         // RuntimeLexicalErrorObject.
    1984           0 :         if (globalLexical->getSlot(shape->slot()).isMagic() || !shape->writable())
    1985           0 :             return false;
    1986           0 :         result = globalLexical;
    1987             :     } else {
    1988           0 :         result = &globalLexical->global();
    1989             :     }
    1990             : 
    1991           0 :     if (result == globalLexical) {
    1992             :         // Lexical bindings are non-configurable so we can just return the
    1993             :         // global lexical.
    1994           0 :         writer.loadObjectResult(objId);
    1995             :     } else {
    1996             :         // If the property exists on the global and is non-configurable, it cannot be
    1997             :         // shadowed by the lexical scope so we can just return the global without a
    1998             :         // shape guard.
    1999           0 :         Shape* shape = result->as<GlobalObject>().lookup(cx_, id);
    2000           0 :         if (!shape || shape->configurable())
    2001           0 :             writer.guardShape(objId, globalLexical->lastProperty());
    2002           0 :         ObjOperandId globalId = writer.loadEnclosingEnvironment(objId);
    2003           0 :         writer.loadObjectResult(globalId);
    2004             :     }
    2005           0 :     writer.returnFromIC();
    2006             : 
    2007           0 :     trackAttached("GlobalName");
    2008           0 :     return true;
    2009             : }
    2010             : 
    2011             : bool
    2012           0 : BindNameIRGenerator::tryAttachEnvironmentName(ObjOperandId objId, HandleId id)
    2013             : {
    2014           0 :     if (IsGlobalOp(JSOp(*pc_)) || script_->hasNonSyntacticScope())
    2015           0 :         return false;
    2016             : 
    2017           0 :     RootedObject env(cx_, env_);
    2018           0 :     RootedShape shape(cx_);
    2019             :     while (true) {
    2020           0 :         if (!env->is<GlobalObject>() && !env->is<EnvironmentObject>())
    2021           0 :             return false;
    2022           0 :         if (env->is<WithEnvironmentObject>())
    2023           0 :             return false;
    2024             : 
    2025           0 :         MOZ_ASSERT(!env->hasUncacheableProto());
    2026             : 
    2027             :         // When we reach an unqualified variables object (like the global) we
    2028             :         // have to stop looking and return that object.
    2029           0 :         if (env->isUnqualifiedVarObj())
    2030           0 :             break;
    2031             : 
    2032             :         // Check for an 'own' property on the env. There is no need to
    2033             :         // check the prototype as non-with scopes do not inherit properties
    2034             :         // from any prototype.
    2035           0 :         shape = env->as<NativeObject>().lookup(cx_, id);
    2036           0 :         if (shape)
    2037           0 :             break;
    2038             : 
    2039           0 :         env = env->enclosingEnvironment();
    2040             :     }
    2041             : 
    2042             :     // If this is an uninitialized lexical or a const, we need to return a
    2043             :     // RuntimeLexicalErrorObject.
    2044           0 :     RootedNativeObject holder(cx_, &env->as<NativeObject>());
    2045           0 :     if (shape &&
    2046           0 :         holder->is<EnvironmentObject>() &&
    2047           0 :         (holder->getSlot(shape->slot()).isMagic() || !shape->writable()))
    2048             :     {
    2049           0 :         return false;
    2050             :     }
    2051             : 
    2052           0 :     ObjOperandId lastObjId = objId;
    2053           0 :     env = env_;
    2054           0 :     while (env) {
    2055           0 :         if (NeedEnvironmentShapeGuard(env) && !env->is<GlobalObject>())
    2056           0 :             writer.guardShape(lastObjId, env->maybeShape());
    2057             : 
    2058           0 :         if (env == holder)
    2059           0 :             break;
    2060             : 
    2061           0 :         lastObjId = writer.loadEnclosingEnvironment(lastObjId);
    2062           0 :         env = env->enclosingEnvironment();
    2063             :     }
    2064           0 :     writer.loadObjectResult(lastObjId);
    2065           0 :     writer.returnFromIC();
    2066             : 
    2067           0 :     trackAttached("EnvironmentName");
    2068           0 :     return true;
    2069             : }
    2070             : 
    2071             : void
    2072           0 : BindNameIRGenerator::trackAttached(const char* name)
    2073             : {
    2074             : #ifdef JS_CACHEIR_SPEW
    2075           0 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    2076           0 :     if (sp.enabled()) {
    2077           0 :         LockGuard<Mutex> guard(sp.lock());
    2078           0 :         sp.beginCache(guard, *this);
    2079           0 :         sp.valueProperty(guard, "base", ObjectValue(*env_));
    2080           0 :         sp.valueProperty(guard, "property", StringValue(name_));
    2081           0 :         sp.attached(guard, name);
    2082           0 :         sp.endCache(guard);
    2083             :     }
    2084             : #endif
    2085           0 : }
    2086             : 
    2087             : void
    2088           0 : BindNameIRGenerator::trackNotAttached()
    2089             : {
    2090             : #ifdef JS_CACHEIR_SPEW
    2091           0 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    2092           0 :     if (sp.enabled()) {
    2093           0 :         LockGuard<Mutex> guard(sp.lock());
    2094           0 :         sp.beginCache(guard, *this);
    2095           0 :         sp.valueProperty(guard, "base", ObjectValue(*env_));
    2096           0 :         sp.valueProperty(guard, "property", StringValue(name_));
    2097           0 :         sp.endCache(guard);
    2098             :     }
    2099             : #endif
    2100           0 : }
    2101             : 
    2102         184 : HasPropIRGenerator::HasPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    2103             :                                        CacheKind cacheKind, ICState::Mode mode,
    2104         184 :                                        HandleValue idVal, HandleValue val)
    2105             :   : IRGenerator(cx, script, pc, cacheKind, mode),
    2106             :     val_(val),
    2107         184 :     idVal_(idVal)
    2108         184 : { }
    2109             : 
    2110             : bool
    2111          39 : HasPropIRGenerator::tryAttachDense(HandleObject obj, ObjOperandId objId,
    2112             :                                    uint32_t index, Int32OperandId indexId)
    2113             : {
    2114          39 :     if (!obj->isNative())
    2115           2 :         return false;
    2116          37 :     if (!obj->as<NativeObject>().containsDenseElement(index))
    2117           0 :         return false;
    2118             : 
    2119             :     // Guard shape to ensure object class is NativeObject.
    2120          37 :     writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
    2121             : 
    2122          37 :     writer.loadDenseElementExistsResult(objId, indexId);
    2123          37 :     writer.returnFromIC();
    2124             : 
    2125          37 :     trackAttached("DenseHasProp");
    2126          37 :     return true;
    2127             : }
    2128             : 
    2129             : bool
    2130           2 : HasPropIRGenerator::tryAttachDenseHole(HandleObject obj, ObjOperandId objId,
    2131             :                                        uint32_t index, Int32OperandId indexId)
    2132             : {
    2133           2 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2134             : 
    2135           2 :     if (!obj->isNative())
    2136           2 :         return false;
    2137           0 :     if (obj->as<NativeObject>().containsDenseElement(index))
    2138           0 :         return false;
    2139           0 :     if (!CanAttachDenseElementHole(obj, hasOwn))
    2140           0 :         return false;
    2141             : 
    2142             :     // Guard shape to ensure class is NativeObject and to prevent non-dense
    2143             :     // elements being added. Also ensures prototype doesn't change if dynamic
    2144             :     // checks aren't emitted.
    2145           0 :     writer.guardShape(objId, obj->as<NativeObject>().lastProperty());
    2146             : 
    2147             :     // Generate prototype guards if needed. This includes monitoring that
    2148             :     // properties were not added in the chain.
    2149           0 :     if (!hasOwn)
    2150           0 :         GeneratePrototypeHoleGuards(writer, obj, objId);
    2151             : 
    2152           0 :     writer.loadDenseElementHoleExistsResult(objId, indexId);
    2153           0 :     writer.returnFromIC();
    2154             : 
    2155           0 :     trackAttached("DenseHasPropHole");
    2156           0 :     return true;
    2157             : }
    2158             : 
    2159             : bool
    2160         145 : HasPropIRGenerator::tryAttachNative(HandleObject obj, ObjOperandId objId,
    2161             :                                     HandleId key, ValOperandId keyId)
    2162             : {
    2163         145 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2164             : 
    2165         145 :     JSObject* holder = nullptr;
    2166         145 :     PropertyResult prop;
    2167             : 
    2168         145 :     if (hasOwn) {
    2169           0 :         if (!LookupOwnPropertyPure(cx_, obj, key, &prop))
    2170           0 :             return false;
    2171             : 
    2172           0 :         holder = obj;
    2173             :     } else {
    2174         145 :         if (!LookupPropertyPure(cx_, obj, key, &holder, &prop))
    2175          22 :             return false;
    2176             :     }
    2177         123 :     if (!prop.isFound())
    2178          52 :         return false;
    2179             : 
    2180             :     // Use MegamorphicHasOwnResult if applicable
    2181          71 :     if (hasOwn && mode_ == ICState::Mode::Megamorphic) {
    2182           0 :         writer.megamorphicHasOwnResult(objId, keyId);
    2183           0 :         writer.returnFromIC();
    2184           0 :         trackAttached("MegamorphicHasProp");
    2185           0 :         return true;
    2186             :     }
    2187             : 
    2188         142 :     Maybe<ObjOperandId> tempId;
    2189          71 :     emitIdGuard(keyId, key);
    2190          71 :     EmitReadSlotGuard(writer, obj, holder, objId, &tempId);
    2191          71 :     writer.loadBooleanResult(true);
    2192          71 :     writer.returnFromIC();
    2193             : 
    2194          71 :     trackAttached("NativeHasProp");
    2195          71 :     return true;
    2196             : }
    2197             : 
    2198             : bool
    2199          74 : HasPropIRGenerator::tryAttachNativeDoesNotExist(HandleObject obj, ObjOperandId objId,
    2200             :                                                 HandleId key, ValOperandId keyId)
    2201             : {
    2202          74 :     bool hasOwn = (cacheKind_ == CacheKind::HasOwn);
    2203             : 
    2204          74 :     if (hasOwn) {
    2205           0 :         if (!CheckHasNoSuchOwnProperty(cx_, obj, key))
    2206           0 :             return false;
    2207             :     } else {
    2208          74 :         if (!CheckHasNoSuchProperty(cx_, obj, key))
    2209          22 :             return false;
    2210             :     }
    2211             : 
    2212             :     // Use MegamorphicHasOwnResult if applicable
    2213          52 :     if (hasOwn && mode_ == ICState::Mode::Megamorphic) {
    2214           0 :         writer.megamorphicHasOwnResult(objId, keyId);
    2215           0 :         writer.returnFromIC();
    2216           0 :         trackAttached("MegamorphicHasOwn");
    2217           0 :         return true;
    2218             :     }
    2219             : 
    2220         104 :     Maybe<ObjOperandId> tempId;
    2221          52 :     emitIdGuard(keyId, key);
    2222          52 :     if (hasOwn) {
    2223           0 :         TestMatchingReceiver(writer, obj, objId, &tempId);
    2224             :     } else {
    2225          52 :         EmitReadSlotGuard(writer, obj, nullptr, objId, &tempId);
    2226             :     }
    2227          52 :     writer.loadBooleanResult(false);
    2228          52 :     writer.returnFromIC();
    2229             : 
    2230          52 :     trackAttached("NativeDoesNotExist");
    2231          52 :     return true;
    2232             : }
    2233             : 
    2234             : bool
    2235           0 : HasPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId,
    2236             :                                           ValOperandId keyId)
    2237             : {
    2238           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::HasOwn);
    2239             : 
    2240           0 :     if (!obj->is<ProxyObject>())
    2241           0 :         return false;
    2242             : 
    2243           0 :     writer.guardIsProxy(objId);
    2244           0 :     writer.callProxyHasOwnResult(objId, keyId);
    2245           0 :     writer.returnFromIC();
    2246             : 
    2247           0 :     trackAttached("ProxyHasProp");
    2248           0 :     return true;
    2249             : }
    2250             : 
    2251             : bool
    2252         184 : HasPropIRGenerator::tryAttachStub()
    2253             : {
    2254         184 :     MOZ_ASSERT(cacheKind_ == CacheKind::In ||
    2255             :                cacheKind_ == CacheKind::HasOwn);
    2256             : 
    2257         368 :     AutoAssertNoPendingException aanpe(cx_);
    2258             : 
    2259             :     // NOTE: Argument order is PROPERTY, OBJECT
    2260         184 :     ValOperandId keyId(writer.setInputOperandId(0));
    2261         184 :     ValOperandId valId(writer.setInputOperandId(1));
    2262             : 
    2263         184 :     if (!val_.isObject()) {
    2264           0 :         trackNotAttached();
    2265           0 :         return false;
    2266             :     }
    2267         368 :     RootedObject obj(cx_, &val_.toObject());
    2268         184 :     ObjOperandId objId = writer.guardIsObject(valId);
    2269             : 
    2270             :     // Optimize DOM Proxies for JSOP_HASOWN
    2271         184 :     if (cacheKind_ == CacheKind::HasOwn) {
    2272           0 :         if (tryAttachProxyElement(obj, objId, keyId))
    2273           0 :             return true;
    2274             :     }
    2275             : 
    2276         368 :     RootedId id(cx_);
    2277             :     bool nameOrSymbol;
    2278         184 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    2279           0 :         cx_->clearPendingException();
    2280           0 :         return false;
    2281             :     }
    2282             : 
    2283         184 :     if (nameOrSymbol) {
    2284         145 :         if (tryAttachNative(obj, objId, id, keyId))
    2285          71 :             return true;
    2286          74 :         if (tryAttachNativeDoesNotExist(obj, objId, id, keyId))
    2287          52 :             return true;
    2288             : 
    2289          22 :         trackNotAttached();
    2290          22 :         return false;
    2291             :     }
    2292             : 
    2293             :     uint32_t index;
    2294          39 :     Int32OperandId indexId;
    2295          39 :     if (maybeGuardInt32Index(idVal_, keyId, &index, &indexId)) {
    2296          39 :         if (tryAttachDense(obj, objId, index, indexId))
    2297          37 :             return true;
    2298           2 :         if (tryAttachDenseHole(obj, objId, index, indexId))
    2299           0 :             return true;
    2300             : 
    2301           2 :         trackNotAttached();
    2302           2 :         return false;
    2303             :     }
    2304             : 
    2305           0 :     trackNotAttached();
    2306           0 :     return false;
    2307             : }
    2308             : 
    2309             : void
    2310         160 : HasPropIRGenerator::trackAttached(const char* name)
    2311             : {
    2312             : #ifdef JS_CACHEIR_SPEW
    2313         160 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    2314         160 :     if (sp.enabled()) {
    2315           0 :         LockGuard<Mutex> guard(sp.lock());
    2316           0 :         sp.beginCache(guard, *this);
    2317           0 :         sp.valueProperty(guard, "base", val_);
    2318           0 :         sp.valueProperty(guard, "property", idVal_);
    2319           0 :         sp.attached(guard, name);
    2320           0 :         sp.endCache(guard);
    2321             :     }
    2322             : #endif
    2323         160 : }
    2324             : 
    2325             : void
    2326          24 : HasPropIRGenerator::trackNotAttached()
    2327             : {
    2328             : #ifdef JS_CACHEIR_SPEW
    2329          24 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    2330          24 :     if (sp.enabled()) {
    2331           0 :         LockGuard<Mutex> guard(sp.lock());
    2332           0 :         sp.beginCache(guard, *this);
    2333           0 :         sp.valueProperty(guard, "base", val_);
    2334           0 :         sp.valueProperty(guard, "property", idVal_);
    2335           0 :         sp.endCache(guard);
    2336             :     }
    2337             : #endif
    2338          24 : }
    2339             : 
    2340             : bool
    2341         353 : IRGenerator::maybeGuardInt32Index(const Value& index, ValOperandId indexId,
    2342             :                                   uint32_t* int32Index, Int32OperandId* int32IndexId)
    2343             : {
    2344         353 :     if (index.isNumber()) {
    2345             :         int32_t indexSigned;
    2346         322 :         if (index.isInt32()) {
    2347         322 :             indexSigned = index.toInt32();
    2348             :         } else {
    2349             :             // We allow negative zero here.
    2350           0 :             if (!mozilla::NumberEqualsInt32(index.toDouble(), &indexSigned))
    2351           0 :                 return false;
    2352           0 :             if (!cx_->runtime()->jitSupportsFloatingPoint)
    2353           0 :                 return false;
    2354             :         }
    2355             : 
    2356         322 :         if (indexSigned < 0)
    2357           0 :             return false;
    2358             : 
    2359         322 :         *int32Index = uint32_t(indexSigned);
    2360         322 :         *int32IndexId = writer.guardIsInt32Index(indexId);
    2361         322 :         return true;
    2362             :     }
    2363             : 
    2364          31 :     if (index.isString()) {
    2365           2 :         int32_t indexSigned = GetIndexFromString(index.toString());
    2366           2 :         if (indexSigned < 0)
    2367           0 :             return false;
    2368             : 
    2369           2 :         StringOperandId strId = writer.guardIsString(indexId);
    2370           2 :         *int32Index = uint32_t(indexSigned);
    2371           2 :         *int32IndexId = writer.guardAndGetIndexFromString(strId);
    2372           2 :         return true;
    2373             :     }
    2374             : 
    2375          29 :     return false;
    2376             : }
    2377             : 
    2378        1705 : SetPropIRGenerator::SetPropIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    2379             :                                        CacheKind cacheKind, ICState::Mode mode,
    2380             :                                        bool* isTemporarilyUnoptimizable,
    2381             :                                        HandleValue lhsVal, HandleValue idVal, HandleValue rhsVal,
    2382        1705 :                                        bool needsTypeBarrier, bool maybeHasExtraIndexedProps)
    2383             :   : IRGenerator(cx, script, pc, cacheKind, mode),
    2384             :     lhsVal_(lhsVal),
    2385             :     idVal_(idVal),
    2386             :     rhsVal_(rhsVal),
    2387             :     isTemporarilyUnoptimizable_(isTemporarilyUnoptimizable),
    2388             :     typeCheckInfo_(cx, needsTypeBarrier),
    2389             :     preliminaryObjectAction_(PreliminaryObjectAction::None),
    2390             :     attachedTypedArrayOOBStub_(false),
    2391        1705 :     maybeHasExtraIndexedProps_(maybeHasExtraIndexedProps)
    2392        1705 : {}
    2393             : 
    2394             : bool
    2395        1165 : SetPropIRGenerator::tryAttachStub()
    2396             : {
    2397        2330 :     AutoAssertNoPendingException aanpe(cx_);
    2398             : 
    2399        1165 :     ValOperandId objValId(writer.setInputOperandId(0));
    2400        1165 :     ValOperandId rhsValId;
    2401        1165 :     if (cacheKind_ == CacheKind::SetProp) {
    2402         690 :         rhsValId = ValOperandId(writer.setInputOperandId(1));
    2403             :     } else {
    2404         475 :         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    2405         475 :         MOZ_ASSERT(setElemKeyValueId().id() == 1);
    2406         475 :         writer.setInputOperandId(1);
    2407         475 :         rhsValId = ValOperandId(writer.setInputOperandId(2));
    2408             :     }
    2409             : 
    2410        2330 :     RootedId id(cx_);
    2411             :     bool nameOrSymbol;
    2412        1165 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    2413           0 :         cx_->clearPendingException();
    2414           0 :         return false;
    2415             :     }
    2416             : 
    2417        1165 :     if (lhsVal_.isObject()) {
    2418        2330 :         RootedObject obj(cx_, &lhsVal_.toObject());
    2419        1165 :         if (obj->watched())
    2420           0 :             return false;
    2421             : 
    2422        1165 :         ObjOperandId objId = writer.guardIsObject(objValId);
    2423        1165 :         if (nameOrSymbol) {
    2424        1020 :             if (tryAttachNativeSetSlot(obj, objId, id, rhsValId))
    2425         422 :                 return true;
    2426         598 :             if (tryAttachUnboxedExpandoSetSlot(obj, objId, id, rhsValId))
    2427           0 :                 return true;
    2428         598 :             if (tryAttachUnboxedProperty(obj, objId, id, rhsValId))
    2429          47 :                 return true;
    2430         551 :             if (tryAttachTypedObjectProperty(obj, objId, id, rhsValId))
    2431           0 :                 return true;
    2432         551 :             if (tryAttachSetArrayLength(obj, objId, id, rhsValId))
    2433          10 :                 return true;
    2434         541 :             if (IsPropertySetOp(JSOp(*pc_))) {
    2435         504 :                 if (tryAttachSetter(obj, objId, id, rhsValId))
    2436           9 :                     return true;
    2437         495 :                 if (tryAttachWindowProxy(obj, objId, id, rhsValId))
    2438           0 :                     return true;
    2439         495 :                 if (tryAttachProxy(obj, objId, id, rhsValId))
    2440          16 :                     return true;
    2441             :             }
    2442         516 :             return false;
    2443             :         }
    2444             : 
    2445         145 :         if (IsPropertySetOp(JSOp(*pc_))) {
    2446          76 :             if (tryAttachProxyElement(obj, objId, rhsValId))
    2447           0 :                 return true;
    2448             :         }
    2449             : 
    2450             :         uint32_t index;
    2451         145 :         Int32OperandId indexId;
    2452         145 :         if (maybeGuardInt32Index(idVal_, setElemKeyValueId(), &index, &indexId)) {
    2453         138 :             if (tryAttachSetDenseElement(obj, objId, index, indexId, rhsValId))
    2454          26 :                 return true;
    2455         112 :             if (tryAttachSetDenseElementHole(obj, objId, index, indexId, rhsValId))
    2456          96 :                 return true;
    2457          16 :             if (tryAttachSetUnboxedArrayElement(obj, objId, index, indexId, rhsValId))
    2458           0 :                 return true;
    2459          16 :             if (tryAttachSetUnboxedArrayElementHole(obj, objId, index, indexId, rhsValId))
    2460           0 :                 return true;
    2461          16 :             if (tryAttachSetTypedElement(obj, objId, index, indexId, rhsValId))
    2462           0 :                 return true;
    2463          16 :             return false;
    2464             :         }
    2465           7 :         return false;
    2466             :     }
    2467           0 :     return false;
    2468             : }
    2469             : 
    2470             : static void
    2471         422 : EmitStoreSlotAndReturn(CacheIRWriter& writer, ObjOperandId objId, NativeObject* nobj, Shape* shape,
    2472             :                        ValOperandId rhsId)
    2473             : {
    2474         422 :     if (nobj->isFixedSlot(shape->slot())) {
    2475         381 :         size_t offset = NativeObject::getFixedSlotOffset(shape->slot());
    2476         381 :         writer.storeFixedSlot(objId, offset, rhsId);
    2477             :     } else {
    2478          41 :         size_t offset = nobj->dynamicSlotIndex(shape->slot()) * sizeof(Value);
    2479          41 :         writer.storeDynamicSlot(objId, offset, rhsId);
    2480             :     }
    2481         422 :     writer.returnFromIC();
    2482         422 : }
    2483             : 
    2484             : static Shape*
    2485         957 : LookupShapeForSetSlot(NativeObject* obj, jsid id)
    2486             : {
    2487         957 :     Shape* shape = obj->lookupPure(id);
    2488         957 :     if (shape && shape->hasSlot() && shape->hasDefaultSetter() && shape->writable())
    2489         446 :         return shape;
    2490         511 :     return nullptr;
    2491             : }
    2492             : 
    2493             : static bool
    2494        1020 : CanAttachNativeSetSlot(JSContext* cx, HandleObject obj, HandleId id,
    2495             :                        bool* isTemporarilyUnoptimizable, MutableHandleShape propShape)
    2496             : {
    2497        1020 :     if (!obj->isNative())
    2498          63 :         return false;
    2499             : 
    2500         957 :     propShape.set(LookupShapeForSetSlot(&obj->as<NativeObject>(), id));
    2501         957 :     if (!propShape)
    2502         511 :         return false;
    2503             : 
    2504         446 :     ObjectGroup* group = JSObject::getGroup(cx, obj);
    2505         446 :     if (!group) {
    2506           0 :         cx->recoverFromOutOfMemory();
    2507           0 :         return false;
    2508             :     }
    2509             : 
    2510             :     // For some property writes, such as the initial overwrite of global
    2511             :     // properties, TI will not mark the property as having been
    2512             :     // overwritten. Don't attach a stub in this case, so that we don't
    2513             :     // execute another write to the property without TI seeing that write.
    2514         446 :     EnsureTrackPropertyTypes(cx, obj, id);
    2515         446 :     if (!PropertyHasBeenMarkedNonConstant(obj, id)) {
    2516          24 :         *isTemporarilyUnoptimizable = true;
    2517          24 :         return false;
    2518             :     }
    2519             : 
    2520         422 :     return true;
    2521             : }
    2522             : 
    2523             : bool
    2524        1020 : SetPropIRGenerator::tryAttachNativeSetSlot(HandleObject obj, ObjOperandId objId, HandleId id,
    2525             :                                            ValOperandId rhsId)
    2526             : {
    2527        2040 :     RootedShape propShape(cx_);
    2528        1020 :     if (!CanAttachNativeSetSlot(cx_, obj, id, isTemporarilyUnoptimizable_, &propShape))
    2529         598 :         return false;
    2530             : 
    2531         422 :     if (mode_ == ICState::Mode::Megamorphic && cacheKind_ == CacheKind::SetProp) {
    2532           0 :         writer.megamorphicStoreSlot(objId, JSID_TO_ATOM(id)->asPropertyName(), rhsId,
    2533           0 :                                     typeCheckInfo_.needsTypeBarrier());
    2534           0 :         writer.returnFromIC();
    2535           0 :         trackAttached("MegamorphicNativeSlot");
    2536           0 :         return true;
    2537             :     }
    2538             : 
    2539         422 :     maybeEmitIdGuard(id);
    2540             : 
    2541             :     // If we need a property type barrier (always in Baseline, sometimes in
    2542             :     // Ion), guard on both the shape and the group. If Ion knows the property
    2543             :     // types match, we don't need the group guard.
    2544         422 :     NativeObject* nobj = &obj->as<NativeObject>();
    2545         422 :     if (typeCheckInfo_.needsTypeBarrier())
    2546         422 :         writer.guardGroup(objId, nobj->group());
    2547         422 :     writer.guardShape(objId, nobj->lastProperty());
    2548             : 
    2549         422 :     if (IsPreliminaryObject(obj))
    2550         353 :         preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
    2551             :     else
    2552          69 :         preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    2553             : 
    2554         422 :     typeCheckInfo_.set(nobj->group(), id);
    2555         422 :     EmitStoreSlotAndReturn(writer, objId, nobj, propShape, rhsId);
    2556             : 
    2557         422 :     trackAttached("NativeSlot");
    2558         422 :     return true;
    2559             : }
    2560             : 
    2561             : bool
    2562         598 : SetPropIRGenerator::tryAttachUnboxedExpandoSetSlot(HandleObject obj, ObjOperandId objId,
    2563             :                                                    HandleId id, ValOperandId rhsId)
    2564             : {
    2565         598 :     if (!obj->is<UnboxedPlainObject>())
    2566         551 :         return false;
    2567             : 
    2568          47 :     UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    2569          47 :     if (!expando)
    2570          47 :         return false;
    2571             : 
    2572           0 :     Shape* propShape = LookupShapeForSetSlot(expando, id);
    2573           0 :     if (!propShape)
    2574           0 :         return false;
    2575             : 
    2576           0 :     maybeEmitIdGuard(id);
    2577           0 :     writer.guardGroup(objId, obj->group());
    2578           0 :     ObjOperandId expandoId = writer.guardAndLoadUnboxedExpando(objId);
    2579           0 :     writer.guardShape(expandoId, expando->lastProperty());
    2580             : 
    2581             :     // Property types must be added to the unboxed object's group, not the
    2582             :     // expando's group (it has unknown properties).
    2583           0 :     typeCheckInfo_.set(obj->group(), id);
    2584           0 :     EmitStoreSlotAndReturn(writer, expandoId, expando, propShape, rhsId);
    2585             : 
    2586           0 :     trackAttached("UnboxedExpando");
    2587           0 :     return true;
    2588             : }
    2589             : 
    2590             : static void
    2591          47 : EmitGuardUnboxedPropertyType(CacheIRWriter& writer, JSValueType propType, ValOperandId valId)
    2592             : {
    2593          47 :     if (propType == JSVAL_TYPE_OBJECT) {
    2594             :         // Unboxed objects store NullValue as nullptr object.
    2595          21 :         writer.guardIsObjectOrNull(valId);
    2596             :     } else {
    2597          26 :         writer.guardType(valId, propType);
    2598             :     }
    2599          47 : }
    2600             : 
    2601             : bool
    2602         598 : SetPropIRGenerator::tryAttachUnboxedProperty(HandleObject obj, ObjOperandId objId, HandleId id,
    2603             :                                              ValOperandId rhsId)
    2604             : {
    2605         598 :     if (!obj->is<UnboxedPlainObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
    2606         551 :         return false;
    2607             : 
    2608          47 :     const UnboxedLayout::Property* property = obj->as<UnboxedPlainObject>().layout().lookup(id);
    2609          47 :     if (!property)
    2610           0 :         return false;
    2611             : 
    2612          47 :     maybeEmitIdGuard(id);
    2613          47 :     writer.guardGroup(objId, obj->group());
    2614          47 :     EmitGuardUnboxedPropertyType(writer, property->type, rhsId);
    2615          47 :     writer.storeUnboxedProperty(objId, property->type,
    2616          47 :                                 UnboxedPlainObject::offsetOfData() + property->offset,
    2617          47 :                                 rhsId);
    2618          47 :     writer.returnFromIC();
    2619             : 
    2620          47 :     typeCheckInfo_.set(obj->group(), id);
    2621          47 :     preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    2622             : 
    2623          47 :     trackAttached("Unboxed");
    2624          47 :     return true;
    2625             : }
    2626             : 
    2627             : bool
    2628         551 : SetPropIRGenerator::tryAttachTypedObjectProperty(HandleObject obj, ObjOperandId objId, HandleId id,
    2629             :                                                  ValOperandId rhsId)
    2630             : {
    2631         551 :     if (!obj->is<TypedObject>() || !cx_->runtime()->jitSupportsFloatingPoint)
    2632         551 :         return false;
    2633             : 
    2634           0 :     if (cx_->compartment()->detachedTypedObjects)
    2635           0 :         return false;
    2636             : 
    2637           0 :     if (!obj->as<TypedObject>().typeDescr().is<StructTypeDescr>())
    2638           0 :         return false;
    2639             : 
    2640           0 :     StructTypeDescr* structDescr = &obj->as<TypedObject>().typeDescr().as<StructTypeDescr>();
    2641             :     size_t fieldIndex;
    2642           0 :     if (!structDescr->fieldIndex(id, &fieldIndex))
    2643           0 :         return false;
    2644             : 
    2645           0 :     TypeDescr* fieldDescr = &structDescr->fieldDescr(fieldIndex);
    2646           0 :     if (!fieldDescr->is<SimpleTypeDescr>())
    2647           0 :         return false;
    2648             : 
    2649           0 :     uint32_t fieldOffset = structDescr->fieldOffset(fieldIndex);
    2650           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    2651             : 
    2652           0 :     maybeEmitIdGuard(id);
    2653           0 :     writer.guardNoDetachedTypedObjects();
    2654           0 :     writer.guardShape(objId, obj->as<TypedObject>().shape());
    2655           0 :     writer.guardGroup(objId, obj->group());
    2656             : 
    2657           0 :     typeCheckInfo_.set(obj->group(), id);
    2658             : 
    2659             :     // Scalar types can always be stored without a type update stub.
    2660           0 :     if (fieldDescr->is<ScalarTypeDescr>()) {
    2661           0 :         Scalar::Type type = fieldDescr->as<ScalarTypeDescr>().type();
    2662           0 :         writer.storeTypedObjectScalarProperty(objId, fieldOffset, layout, type, rhsId);
    2663           0 :         writer.returnFromIC();
    2664             : 
    2665           0 :         trackAttached("TypedObject");
    2666           0 :         return true;
    2667             :     }
    2668             : 
    2669             :     // For reference types, guard on the RHS type first, so that
    2670             :     // StoreTypedObjectReferenceProperty is infallible.
    2671           0 :     ReferenceTypeDescr::Type type = fieldDescr->as<ReferenceTypeDescr>().type();
    2672           0 :     switch (type) {
    2673             :       case ReferenceTypeDescr::TYPE_ANY:
    2674           0 :         break;
    2675             :       case ReferenceTypeDescr::TYPE_OBJECT:
    2676           0 :         writer.guardIsObjectOrNull(rhsId);
    2677           0 :         break;
    2678             :       case ReferenceTypeDescr::TYPE_STRING:
    2679           0 :         writer.guardType(rhsId, JSVAL_TYPE_STRING);
    2680           0 :         break;
    2681             :     }
    2682             : 
    2683           0 :     writer.storeTypedObjectReferenceProperty(objId, fieldOffset, layout, type, rhsId);
    2684           0 :     writer.returnFromIC();
    2685             : 
    2686           0 :     trackAttached("TypedObject");
    2687           0 :     return true;
    2688             : }
    2689             : 
    2690             : void
    2691        1070 : SetPropIRGenerator::trackAttached(const char* name)
    2692             : {
    2693             : #ifdef JS_CACHEIR_SPEW
    2694        1070 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    2695        1070 :     if (sp.enabled()) {
    2696           0 :         LockGuard<Mutex> guard(sp.lock());
    2697           0 :         sp.beginCache(guard, *this);
    2698           0 :         sp.valueProperty(guard, "base", lhsVal_);
    2699           0 :         sp.valueProperty(guard, "property", idVal_);
    2700           0 :         sp.valueProperty(guard, "value", rhsVal_);
    2701           0 :         sp.attached(guard, name);
    2702           0 :         sp.endCache(guard);
    2703             :     }
    2704             : #endif
    2705        1070 : }
    2706             : 
    2707             : void
    2708          96 : SetPropIRGenerator::trackNotAttached()
    2709             : {
    2710             : #ifdef JS_CACHEIR_SPEW
    2711          96 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    2712          96 :     if (sp.enabled()) {
    2713           0 :         LockGuard<Mutex> guard(sp.lock());
    2714           0 :         sp.beginCache(guard, *this);
    2715           0 :         sp.valueProperty(guard, "base", lhsVal_);
    2716           0 :         sp.valueProperty(guard, "property", idVal_);
    2717           0 :         sp.valueProperty(guard, "value", rhsVal_);
    2718           0 :         sp.endCache(guard);
    2719             :     }
    2720             : #endif
    2721          96 : }
    2722             : 
    2723             : static bool
    2724         505 : CanAttachSetter(JSContext* cx, jsbytecode* pc, HandleObject obj, HandleId id,
    2725             :                 MutableHandleObject holder, MutableHandleShape propShape,
    2726             :                 bool* isTemporarilyUnoptimizable)
    2727             : {
    2728             :     // Don't attach a setter stub for ops like JSOP_INITELEM.
    2729         505 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc)));
    2730             : 
    2731         505 :     PropertyResult prop;
    2732         505 :     if (!LookupPropertyPure(cx, obj, id, holder.address(), &prop))
    2733          31 :         return false;
    2734             : 
    2735         474 :     if (prop.isNonNativeProperty())
    2736           0 :         return false;
    2737             : 
    2738         474 :     propShape.set(prop.maybeShape());
    2739         944 :     if (!IsCacheableSetPropCallScripted(obj, holder, propShape, isTemporarilyUnoptimizable) &&
    2740         470 :         !IsCacheableSetPropCallNative(obj, holder, propShape))
    2741             :     {
    2742         464 :         return false;
    2743             :     }
    2744             : 
    2745          10 :     return true;
    2746             : }
    2747             : 
    2748             : static void
    2749          10 : EmitCallSetterNoGuards(CacheIRWriter& writer, JSObject* obj, JSObject* holder,
    2750             :                        Shape* shape, ObjOperandId objId, ValOperandId rhsId)
    2751             : {
    2752          10 :     if (IsCacheableSetPropCallNative(obj, holder, shape)) {
    2753           6 :         JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
    2754           6 :         MOZ_ASSERT(target->isNative());
    2755           6 :         writer.callNativeSetter(objId, target, rhsId);
    2756           6 :         writer.returnFromIC();
    2757           6 :         return;
    2758             :     }
    2759             : 
    2760           4 :     MOZ_ASSERT(IsCacheableSetPropCallScripted(obj, holder, shape));
    2761             : 
    2762           4 :     JSFunction* target = &shape->setterValue().toObject().as<JSFunction>();
    2763           4 :     MOZ_ASSERT(target->hasJITCode());
    2764           4 :     writer.callScriptedSetter(objId, target, rhsId);
    2765           4 :     writer.returnFromIC();
    2766             : }
    2767             : 
    2768             : bool
    2769         504 : SetPropIRGenerator::tryAttachSetter(HandleObject obj, ObjOperandId objId, HandleId id,
    2770             :                                     ValOperandId rhsId)
    2771             : {
    2772        1008 :     RootedObject holder(cx_);
    2773        1008 :     RootedShape propShape(cx_);
    2774         504 :     if (!CanAttachSetter(cx_, pc_, obj, id, &holder, &propShape, isTemporarilyUnoptimizable_))
    2775         495 :         return false;
    2776             : 
    2777           9 :     maybeEmitIdGuard(id);
    2778             : 
    2779             :     // Use the megamorphic guard if we're in megamorphic mode, except if |obj|
    2780             :     // is a Window as GuardHasGetterSetter doesn't support this yet (Window may
    2781             :     // require outerizing).
    2782           9 :     if (mode_ == ICState::Mode::Specialized || IsWindow(obj)) {
    2783          18 :         Maybe<ObjOperandId> expandoId;
    2784           9 :         TestMatchingReceiver(writer, obj, objId, &expandoId);
    2785             : 
    2786           9 :         if (obj != holder) {
    2787           9 :             GeneratePrototypeGuards(writer, obj, holder, objId);
    2788             : 
    2789             :             // Guard on the holder's shape.
    2790           9 :             ObjOperandId holderId = writer.loadObject(holder);
    2791           9 :             writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
    2792             :         }
    2793             :     } else {
    2794           0 :         writer.guardHasGetterSetter(objId, propShape);
    2795             :     }
    2796             : 
    2797           9 :     EmitCallSetterNoGuards(writer, obj, holder, propShape, objId, rhsId);
    2798             : 
    2799           9 :     trackAttached("Setter");
    2800           9 :     return true;
    2801             : }
    2802             : 
    2803             : bool
    2804         551 : SetPropIRGenerator::tryAttachSetArrayLength(HandleObject obj, ObjOperandId objId, HandleId id,
    2805             :                                             ValOperandId rhsId)
    2806             : {
    2807        1112 :     if (!obj->is<ArrayObject>() ||
    2808         561 :         !JSID_IS_ATOM(id, cx_->names().length) ||
    2809          10 :         !obj->as<ArrayObject>().lengthIsWritable())
    2810             :     {
    2811         541 :         return false;
    2812             :     }
    2813             : 
    2814          10 :     maybeEmitIdGuard(id);
    2815          10 :     writer.guardClass(objId, GuardClassKind::Array);
    2816          10 :     writer.callSetArrayLength(objId, IsStrictSetPC(pc_), rhsId);
    2817          10 :     writer.returnFromIC();
    2818             : 
    2819          10 :     trackAttached("SetArrayLength");
    2820          10 :     return true;
    2821             : }
    2822             : 
    2823             : bool
    2824         138 : SetPropIRGenerator::tryAttachSetDenseElement(HandleObject obj, ObjOperandId objId, uint32_t index,
    2825             :                                              Int32OperandId indexId, ValOperandId rhsId)
    2826             : {
    2827         138 :     if (!obj->isNative())
    2828           0 :         return false;
    2829             : 
    2830         138 :     NativeObject* nobj = &obj->as<NativeObject>();
    2831         138 :     if (!nobj->containsDenseElement(index) || nobj->getElementsHeader()->isFrozen())
    2832         112 :         return false;
    2833             : 
    2834          26 :     if (typeCheckInfo_.needsTypeBarrier())
    2835          26 :         writer.guardGroup(objId, nobj->group());
    2836          26 :     writer.guardShape(objId, nobj->shape());
    2837             : 
    2838          26 :     writer.storeDenseElement(objId, indexId, rhsId);
    2839          26 :     writer.returnFromIC();
    2840             : 
    2841             :     // Type inference uses JSID_VOID for the element types.
    2842          26 :     typeCheckInfo_.set(nobj->group(), JSID_VOID);
    2843             : 
    2844          26 :     trackAttached("SetDenseElement");
    2845          26 :     return true;
    2846             : }
    2847             : 
    2848             : static bool
    2849         123 : CanAttachAddElement(JSObject* obj, bool isInit)
    2850             : {
    2851             :     // Make sure the objects on the prototype don't have any indexed properties
    2852             :     // or that such properties can't appear without a shape change.
    2853             :     do {
    2854             :         // The first two checks are also relevant to the receiver object.
    2855         123 :         if (obj->isIndexed())
    2856           0 :             return false;
    2857             : 
    2858         123 :         const Class* clasp = obj->getClass();
    2859         177 :         if ((clasp != &ArrayObject::class_ && clasp != &UnboxedArrayObject::class_) &&
    2860         108 :             (clasp->getAddProperty() ||
    2861         108 :              clasp->getResolve() ||
    2862         108 :              clasp->getOpsLookupProperty() ||
    2863         108 :              clasp->getSetProperty() ||
    2864          54 :              clasp->getOpsSetProperty()))
    2865             :         {
    2866           0 :             return false;
    2867             :         }
    2868             : 
    2869             :         // If we're initializing a property instead of setting one, the objects
    2870             :         // on the prototype are not relevant.
    2871         123 :         if (isInit)
    2872          69 :             break;
    2873             : 
    2874          54 :         JSObject* proto = obj->staticPrototype();
    2875          54 :         if (!proto)
    2876          27 :             break;
    2877             : 
    2878          27 :         if (!proto->isNative())
    2879           0 :             return false;
    2880             : 
    2881          27 :         obj = proto;
    2882             :     } while (true);
    2883             : 
    2884          96 :     return true;
    2885             : }
    2886             : 
    2887             : static void
    2888        1078 : ShapeGuardProtoChain(CacheIRWriter& writer, JSObject* obj, ObjOperandId objId)
    2889             : {
    2890             :     while (true) {
    2891             :         // Guard on the proto if the shape does not imply the proto. Singleton
    2892             :         // objects always trigger a shape change when the proto changes, so we
    2893             :         // don't need a guard in that case.
    2894        1078 :         bool guardProto = obj->hasUncacheableProto() && !obj->isSingleton();
    2895             : 
    2896        1078 :         obj = obj->staticPrototype();
    2897        1078 :         if (!obj)
    2898         942 :             return;
    2899             : 
    2900         607 :         objId = writer.loadProto(objId);
    2901         607 :         if (guardProto)
    2902           0 :             writer.guardSpecificObject(objId, obj);
    2903         607 :         writer.guardShape(objId, obj->as<NativeObject>().shape());
    2904         607 :     }
    2905             : }
    2906             : 
    2907             : bool
    2908         112 : SetPropIRGenerator::tryAttachSetDenseElementHole(HandleObject obj, ObjOperandId objId,
    2909             :                                                  uint32_t index, Int32OperandId indexId,
    2910             :                                                  ValOperandId rhsId)
    2911             : {
    2912         112 :     if (!obj->isNative() || rhsVal_.isMagic(JS_ELEMENTS_HOLE))
    2913           0 :         return false;
    2914             : 
    2915         112 :     JSOp op = JSOp(*pc_);
    2916         112 :     MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
    2917             : 
    2918         112 :     if (op == JSOP_INITHIDDENELEM)
    2919           0 :         return false;
    2920             : 
    2921         112 :     NativeObject* nobj = &obj->as<NativeObject>();
    2922         112 :     if (!nobj->nonProxyIsExtensible())
    2923           0 :         return false;
    2924             : 
    2925         112 :     MOZ_ASSERT(!nobj->getElementsHeader()->isFrozen(),
    2926             :                "Extensible objects should not have frozen elements");
    2927             : 
    2928         112 :     uint32_t initLength = nobj->getDenseInitializedLength();
    2929             : 
    2930             :     // Optimize if we're adding an element at initLength or writing to a hole.
    2931             :     // Don't handle the adding case if the current accesss is in bounds, to
    2932             :     // ensure we always call noteArrayWriteHole.
    2933         112 :     bool isAdd = index == initLength;
    2934         112 :     bool isHoleInBounds = index < initLength && !nobj->containsDenseElement(index);
    2935         112 :     if (!isAdd && !isHoleInBounds)
    2936          16 :         return false;
    2937             : 
    2938             :     // Can't add new elements to arrays with non-writable length.
    2939          96 :     if (isAdd && nobj->is<ArrayObject>() && !nobj->as<ArrayObject>().lengthIsWritable())
    2940           0 :         return false;
    2941             : 
    2942             :     // Typed arrays don't have dense elements.
    2943          96 :     if (nobj->is<TypedArrayObject>())
    2944           0 :         return false;
    2945             : 
    2946             :     // Check for other indexed properties or class hooks.
    2947          96 :     if (!CanAttachAddElement(nobj, IsPropertyInitOp(op)))
    2948           0 :         return false;
    2949             : 
    2950          96 :     if (typeCheckInfo_.needsTypeBarrier())
    2951          96 :         writer.guardGroup(objId, nobj->group());
    2952          96 :     writer.guardShape(objId, nobj->shape());
    2953             : 
    2954             :     // Also shape guard the proto chain, unless this is an INITELEM or we know
    2955             :     // the proto chain has no indexed props.
    2956          96 :     if (IsPropertySetOp(op) && maybeHasExtraIndexedProps_)
    2957          27 :         ShapeGuardProtoChain(writer, obj, objId);
    2958             : 
    2959          96 :     writer.storeDenseElementHole(objId, indexId, rhsId, isAdd);
    2960          96 :     writer.returnFromIC();
    2961             : 
    2962             :     // Type inference uses JSID_VOID for the element types.
    2963          96 :     typeCheckInfo_.set(nobj->group(), JSID_VOID);
    2964             : 
    2965          96 :     trackAttached(isAdd ? "AddDenseElement" : "StoreDenseElementHole");
    2966          96 :     return true;
    2967             : }
    2968             : 
    2969             : bool
    2970          16 : SetPropIRGenerator::tryAttachSetUnboxedArrayElement(HandleObject obj, ObjOperandId objId,
    2971             :                                                     uint32_t index, Int32OperandId indexId,
    2972             :                                                     ValOperandId rhsId)
    2973             : {
    2974          16 :     if (!obj->is<UnboxedArrayObject>())
    2975          16 :         return false;
    2976             : 
    2977           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint)
    2978           0 :         return false;
    2979             : 
    2980           0 :     if (index >= obj->as<UnboxedArrayObject>().initializedLength())
    2981           0 :         return false;
    2982             : 
    2983           0 :     writer.guardGroup(objId, obj->group());
    2984             : 
    2985           0 :     JSValueType elementType = obj->group()->unboxedLayoutDontCheckGeneration().elementType();
    2986           0 :     EmitGuardUnboxedPropertyType(writer, elementType, rhsId);
    2987             : 
    2988           0 :     writer.storeUnboxedArrayElement(objId, indexId, rhsId, elementType);
    2989           0 :     writer.returnFromIC();
    2990             : 
    2991             :     // Type inference uses JSID_VOID for the element types.
    2992           0 :     typeCheckInfo_.set(obj->group(), JSID_VOID);
    2993             : 
    2994           0 :     trackAttached("SetUnboxedArrayElement");
    2995           0 :     return true;
    2996             : }
    2997             : 
    2998             : bool
    2999          16 : SetPropIRGenerator::tryAttachSetTypedElement(HandleObject obj, ObjOperandId objId,
    3000             :                                              uint32_t index, Int32OperandId indexId,
    3001             :                                              ValOperandId rhsId)
    3002             : {
    3003          16 :     if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
    3004          16 :         return false;
    3005             : 
    3006           0 :     if (!rhsVal_.isNumber())
    3007           0 :         return false;
    3008             : 
    3009           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint && TypedThingRequiresFloatingPoint(obj))
    3010           0 :         return false;
    3011             : 
    3012           0 :     bool handleOutOfBounds = false;
    3013           0 :     if (obj->is<TypedArrayObject>()) {
    3014           0 :         handleOutOfBounds = (index >= obj->as<TypedArrayObject>().length());
    3015             :     } else {
    3016             :         // Typed objects throw on out of bounds accesses. Don't attach
    3017             :         // a stub in this case.
    3018           0 :         if (index >= obj->as<TypedObject>().length())
    3019           0 :             return false;
    3020             : 
    3021             :         // Don't attach stubs if the underlying storage for typed objects
    3022             :         // in the compartment could be detached, as the stub will always
    3023             :         // bail out.
    3024           0 :         if (cx_->compartment()->detachedTypedObjects)
    3025           0 :             return false;
    3026             :     }
    3027             : 
    3028           0 :     Scalar::Type elementType = TypedThingElementType(obj);
    3029           0 :     TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    3030             : 
    3031           0 :     if (!obj->is<TypedArrayObject>())
    3032           0 :         writer.guardNoDetachedTypedObjects();
    3033             : 
    3034           0 :     writer.guardShape(objId, obj->as<ShapedObject>().shape());
    3035           0 :     writer.storeTypedElement(objId, indexId, rhsId, layout, elementType, handleOutOfBounds);
    3036           0 :     writer.returnFromIC();
    3037             : 
    3038           0 :     if (handleOutOfBounds)
    3039           0 :         attachedTypedArrayOOBStub_ = true;
    3040             : 
    3041           0 :     trackAttached(handleOutOfBounds ? "SetTypedElementOOB" : "SetTypedElement");
    3042           0 :     return true;
    3043             : }
    3044             : 
    3045             : bool
    3046          16 : SetPropIRGenerator::tryAttachSetUnboxedArrayElementHole(HandleObject obj, ObjOperandId objId,
    3047             :                                                         uint32_t index, Int32OperandId indexId,
    3048             :                                                         ValOperandId rhsId)
    3049             : {
    3050          16 :     if (!obj->is<UnboxedArrayObject>() || rhsVal_.isMagic(JS_ELEMENTS_HOLE))
    3051          16 :         return false;
    3052             : 
    3053           0 :     if (!cx_->runtime()->jitSupportsFloatingPoint)
    3054           0 :         return false;
    3055             : 
    3056           0 :     JSOp op = JSOp(*pc_);
    3057           0 :     MOZ_ASSERT(IsPropertySetOp(op) || IsPropertyInitOp(op));
    3058             : 
    3059           0 :     if (op == JSOP_INITHIDDENELEM)
    3060           0 :         return false;
    3061             : 
    3062             :     // Optimize if we're adding an element at initLength. Unboxed arrays don't
    3063             :     // have holes at indexes < initLength.
    3064           0 :     UnboxedArrayObject* aobj = &obj->as<UnboxedArrayObject>();
    3065           0 :     if (index != aobj->initializedLength() || index >= aobj->capacity())
    3066           0 :         return false;
    3067             : 
    3068             :     // Check for other indexed properties or class hooks.
    3069           0 :     if (!CanAttachAddElement(aobj, IsPropertyInitOp(op)))
    3070           0 :         return false;
    3071             : 
    3072           0 :     writer.guardGroup(objId, aobj->group());
    3073             : 
    3074           0 :     JSValueType elementType = aobj->group()->unboxedLayoutDontCheckGeneration().elementType();
    3075           0 :     EmitGuardUnboxedPropertyType(writer, elementType, rhsId);
    3076             : 
    3077             :     // Also shape guard the proto chain, unless this is an INITELEM.
    3078           0 :     if (IsPropertySetOp(op))
    3079           0 :         ShapeGuardProtoChain(writer, aobj, objId);
    3080             : 
    3081           0 :     writer.storeUnboxedArrayElementHole(objId, indexId, rhsId, elementType);
    3082           0 :     writer.returnFromIC();
    3083             : 
    3084             :     // Type inference uses JSID_VOID for the element types.
    3085           0 :     typeCheckInfo_.set(aobj->group(), JSID_VOID);
    3086             : 
    3087           0 :     trackAttached("StoreUnboxedArrayElementHole");
    3088           0 :     return true;
    3089             : }
    3090             : 
    3091             : bool
    3092          15 : SetPropIRGenerator::tryAttachGenericProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    3093             :                                           ValOperandId rhsId, bool handleDOMProxies)
    3094             : {
    3095          15 :     MOZ_ASSERT(obj->is<ProxyObject>());
    3096             : 
    3097          15 :     writer.guardIsProxy(objId);
    3098             : 
    3099          15 :     if (!handleDOMProxies) {
    3100             :         // Ensure that the incoming object is not a DOM proxy, so that we can
    3101             :         // get to the specialized stubs. If handleDOMProxies is true, we were
    3102             :         // unable to attach a specialized DOM stub, so we just handle all
    3103             :         // proxies here.
    3104          14 :         writer.guardNotDOMProxy(objId);
    3105             :     }
    3106             : 
    3107          15 :     if (cacheKind_ == CacheKind::SetProp || mode_ == ICState::Mode::Specialized) {
    3108          14 :         maybeEmitIdGuard(id);
    3109          14 :         writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
    3110             :     } else {
    3111             :         // Attach a stub that handles every id.
    3112           1 :         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    3113           1 :         MOZ_ASSERT(mode_ == ICState::Mode::Megamorphic);
    3114           1 :         writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
    3115             :     }
    3116             : 
    3117          15 :     writer.returnFromIC();
    3118             : 
    3119          15 :     trackAttached("GenericProxy");
    3120          15 :     return true;
    3121             : }
    3122             : 
    3123             : bool
    3124           0 : SetPropIRGenerator::tryAttachDOMProxyShadowed(HandleObject obj, ObjOperandId objId, HandleId id,
    3125             :                                               ValOperandId rhsId)
    3126             : {
    3127           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    3128             : 
    3129           0 :     maybeEmitIdGuard(id);
    3130           0 :     writer.guardShape(objId, obj->maybeShape());
    3131             : 
    3132             :     // No need for more guards: we know this is a DOM proxy, since the shape
    3133             :     // guard enforces a given JSClass, so just go ahead and emit the call to
    3134             :     // ProxySet.
    3135           0 :     writer.callProxySet(objId, id, rhsId, IsStrictSetPC(pc_));
    3136           0 :     writer.returnFromIC();
    3137             : 
    3138           0 :     trackAttached("DOMProxyShadowed");
    3139           0 :     return true;
    3140             : }
    3141             : 
    3142             : bool
    3143           1 : SetPropIRGenerator::tryAttachDOMProxyUnshadowed(HandleObject obj, ObjOperandId objId, HandleId id,
    3144             :                                                 ValOperandId rhsId)
    3145             : {
    3146           1 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    3147             : 
    3148           2 :     RootedObject proto(cx_, obj->staticPrototype());
    3149           1 :     if (!proto)
    3150           0 :         return false;
    3151             : 
    3152           2 :     RootedObject holder(cx_);
    3153           2 :     RootedShape propShape(cx_);
    3154           1 :     if (!CanAttachSetter(cx_, pc_, proto, id, &holder, &propShape, isTemporarilyUnoptimizable_))
    3155           0 :         return false;
    3156             : 
    3157           1 :     maybeEmitIdGuard(id);
    3158           1 :     writer.guardShape(objId, obj->maybeShape());
    3159             : 
    3160             :     // Guard that our expando object hasn't started shadowing this property.
    3161           1 :     CheckDOMProxyExpandoDoesNotShadow(writer, obj, id, objId);
    3162             : 
    3163           1 :     GeneratePrototypeGuards(writer, obj, holder, objId);
    3164             : 
    3165             :     // Guard on the holder of the property.
    3166           1 :     ObjOperandId holderId = writer.loadObject(holder);
    3167           1 :     writer.guardShape(holderId, holder->as<NativeObject>().lastProperty());
    3168             : 
    3169             :     // EmitCallSetterNoGuards expects |obj| to be the object the property is
    3170             :     // on to do some checks. Since we actually looked at proto, and no extra
    3171             :     // guards will be generated, we can just pass that instead.
    3172           1 :     EmitCallSetterNoGuards(writer, proto, holder, propShape, objId, rhsId);
    3173             : 
    3174           1 :     trackAttached("DOMProxyUnshadowed");
    3175           1 :     return true;
    3176             : }
    3177             : 
    3178             : bool
    3179           0 : SetPropIRGenerator::tryAttachDOMProxyExpando(HandleObject obj, ObjOperandId objId, HandleId id,
    3180             :                                              ValOperandId rhsId)
    3181             : {
    3182           0 :     MOZ_ASSERT(IsCacheableDOMProxy(obj));
    3183             : 
    3184           0 :     RootedValue expandoVal(cx_, GetProxyPrivate(obj));
    3185           0 :     RootedObject expandoObj(cx_);
    3186           0 :     if (expandoVal.isObject()) {
    3187           0 :         expandoObj = &expandoVal.toObject();
    3188             :     } else {
    3189           0 :         MOZ_ASSERT(!expandoVal.isUndefined(),
    3190             :                    "How did a missing expando manage to shadow things?");
    3191           0 :         auto expandoAndGeneration = static_cast<ExpandoAndGeneration*>(expandoVal.toPrivate());
    3192           0 :         MOZ_ASSERT(expandoAndGeneration);
    3193           0 :         expandoObj = &expandoAndGeneration->expando.toObject();
    3194             :     }
    3195             : 
    3196           0 :     RootedShape propShape(cx_);
    3197           0 :     if (CanAttachNativeSetSlot(cx_, expandoObj, id, isTemporarilyUnoptimizable_, &propShape)) {
    3198           0 :         maybeEmitIdGuard(id);
    3199             :         ObjOperandId expandoObjId =
    3200           0 :             guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
    3201             : 
    3202           0 :         NativeObject* nativeExpandoObj = &expandoObj->as<NativeObject>();
    3203           0 :         writer.guardGroup(expandoObjId, nativeExpandoObj->group());
    3204           0 :         typeCheckInfo_.set(nativeExpandoObj->group(), id);
    3205             : 
    3206           0 :         EmitStoreSlotAndReturn(writer, expandoObjId, nativeExpandoObj, propShape, rhsId);
    3207           0 :         trackAttached("DOMProxyExpandoSlot");
    3208           0 :         return true;
    3209             :     }
    3210             : 
    3211           0 :     RootedObject holder(cx_);
    3212           0 :     if (CanAttachSetter(cx_, pc_, expandoObj, id, &holder, &propShape,
    3213             :                         isTemporarilyUnoptimizable_))
    3214             :     {
    3215             :         // Note that we don't actually use the expandoObjId here after the
    3216             :         // shape guard. The DOM proxy (objId) is passed to the setter as
    3217             :         // |this|.
    3218           0 :         maybeEmitIdGuard(id);
    3219           0 :         guardDOMProxyExpandoObjectAndShape(obj, objId, expandoVal, expandoObj);
    3220             : 
    3221           0 :         MOZ_ASSERT(holder == expandoObj);
    3222           0 :         EmitCallSetterNoGuards(writer, expandoObj, expandoObj, propShape, objId, rhsId);
    3223           0 :         trackAttached("DOMProxyExpandoSetter");
    3224           0 :         return true;
    3225             :     }
    3226             : 
    3227           0 :     return false;
    3228             : }
    3229             : 
    3230             : bool
    3231         495 : SetPropIRGenerator::tryAttachProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    3232             :                                    ValOperandId rhsId)
    3233             : {
    3234             :     // Don't attach a proxy stub for ops like JSOP_INITELEM.
    3235         495 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
    3236             : 
    3237         495 :     ProxyStubType type = GetProxyStubType(cx_, obj, id);
    3238         495 :     if (type == ProxyStubType::None)
    3239         479 :         return false;
    3240             : 
    3241          16 :     if (mode_ == ICState::Mode::Megamorphic)
    3242           1 :         return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
    3243             : 
    3244          15 :     switch (type) {
    3245             :       case ProxyStubType::None:
    3246           0 :         break;
    3247             :       case ProxyStubType::DOMExpando:
    3248           0 :         if (tryAttachDOMProxyExpando(obj, objId, id, rhsId))
    3249           0 :             return true;
    3250           0 :         if (*isTemporarilyUnoptimizable_) {
    3251             :             // Scripted setter without JIT code. Just wait.
    3252           0 :             return false;
    3253             :         }
    3254             :         MOZ_FALLTHROUGH; // Fall through to the generic shadowed case.
    3255             :       case ProxyStubType::DOMShadowed:
    3256           0 :         return tryAttachDOMProxyShadowed(obj, objId, id, rhsId);
    3257             :       case ProxyStubType::DOMUnshadowed:
    3258           1 :         if (tryAttachDOMProxyUnshadowed(obj, objId, id, rhsId))
    3259           1 :             return true;
    3260           0 :         if (*isTemporarilyUnoptimizable_) {
    3261             :             // Scripted setter without JIT code. Just wait.
    3262           0 :             return false;
    3263             :         }
    3264           0 :         return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ true);
    3265             :       case ProxyStubType::Generic:
    3266          14 :         return tryAttachGenericProxy(obj, objId, id, rhsId, /* handleDOMProxies = */ false);
    3267             :     }
    3268             : 
    3269           0 :     MOZ_CRASH("Unexpected ProxyStubType");
    3270             : }
    3271             : 
    3272             : bool
    3273          76 : SetPropIRGenerator::tryAttachProxyElement(HandleObject obj, ObjOperandId objId, ValOperandId rhsId)
    3274             : {
    3275             :     // Don't attach a proxy stub for ops like JSOP_INITELEM.
    3276          76 :     MOZ_ASSERT(IsPropertySetOp(JSOp(*pc_)));
    3277             : 
    3278          76 :     if (!obj->is<ProxyObject>())
    3279          76 :         return false;
    3280             : 
    3281           0 :     writer.guardIsProxy(objId);
    3282             : 
    3283             :     // Like GetPropIRGenerator::tryAttachProxyElement, don't check for DOM
    3284             :     // proxies here as we don't have specialized DOM stubs for this.
    3285           0 :     MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    3286           0 :     writer.callProxySetByValue(objId, setElemKeyValueId(), rhsId, IsStrictSetPC(pc_));
    3287           0 :     writer.returnFromIC();
    3288             : 
    3289           0 :     trackAttached("ProxyElement");
    3290           0 :     return true;
    3291             : }
    3292             : 
    3293             : bool
    3294         495 : SetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id,
    3295             :                                          ValOperandId rhsId)
    3296             : {
    3297             :     // Attach a stub when the receiver is a WindowProxy and we can do the set
    3298             :     // on the Window (the global object).
    3299             : 
    3300         495 :     if (!IsWindowProxy(obj))
    3301         495 :         return false;
    3302             : 
    3303             :     // If we're megamorphic prefer a generic proxy stub that handles a lot more
    3304             :     // cases.
    3305           0 :     if (mode_ == ICState::Mode::Megamorphic)
    3306           0 :         return false;
    3307             : 
    3308             :     // This must be a WindowProxy for the current Window/global. Else it would
    3309             :     // be a cross-compartment wrapper and IsWindowProxy returns false for
    3310             :     // those.
    3311           0 :     MOZ_ASSERT(obj->getClass() == cx_->runtime()->maybeWindowProxyClass());
    3312           0 :     MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());
    3313             : 
    3314             :     // Now try to do the set on the Window (the current global).
    3315           0 :     Handle<GlobalObject*> windowObj = cx_->global();
    3316             : 
    3317           0 :     RootedShape propShape(cx_);
    3318           0 :     if (!CanAttachNativeSetSlot(cx_, windowObj, id, isTemporarilyUnoptimizable_, &propShape))
    3319           0 :         return false;
    3320             : 
    3321           0 :     maybeEmitIdGuard(id);
    3322             : 
    3323           0 :     writer.guardClass(objId, GuardClassKind::WindowProxy);
    3324           0 :     ObjOperandId windowObjId = writer.loadObject(windowObj);
    3325             : 
    3326           0 :     writer.guardShape(windowObjId, windowObj->lastProperty());
    3327           0 :     writer.guardGroup(windowObjId, windowObj->group());
    3328           0 :     typeCheckInfo_.set(windowObj->group(), id);
    3329             : 
    3330           0 :     EmitStoreSlotAndReturn(writer, windowObjId, windowObj, propShape, rhsId);
    3331             : 
    3332           0 :     trackAttached("WindowProxySlot");
    3333           0 :     return true;
    3334             : }
    3335             : 
    3336             : bool
    3337         540 : SetPropIRGenerator::tryAttachAddSlotStub(HandleObjectGroup oldGroup, HandleShape oldShape)
    3338             : {
    3339        1080 :     AutoAssertNoPendingException aanpe(cx_);
    3340             : 
    3341         540 :     ValOperandId objValId(writer.setInputOperandId(0));
    3342         540 :     ValOperandId rhsValId;
    3343         540 :     if (cacheKind_ == CacheKind::SetProp) {
    3344         211 :         rhsValId = ValOperandId(writer.setInputOperandId(1));
    3345             :     } else {
    3346         329 :         MOZ_ASSERT(cacheKind_ == CacheKind::SetElem);
    3347         329 :         MOZ_ASSERT(setElemKeyValueId().id() == 1);
    3348         329 :         writer.setInputOperandId(1);
    3349         329 :         rhsValId = ValOperandId(writer.setInputOperandId(2));
    3350             :     }
    3351             : 
    3352        1080 :     RootedId id(cx_);
    3353             :     bool nameOrSymbol;
    3354         540 :     if (!ValueToNameOrSymbolId(cx_, idVal_, &id, &nameOrSymbol)) {
    3355           0 :         cx_->clearPendingException();
    3356           0 :         return false;
    3357             :     }
    3358             : 
    3359         540 :     if (!lhsVal_.isObject() || !nameOrSymbol)
    3360          23 :         return false;
    3361             : 
    3362        1034 :     RootedObject obj(cx_, &lhsVal_.toObject());
    3363         517 :     if (obj->watched())
    3364           0 :         return false;
    3365             : 
    3366         517 :     PropertyResult prop;
    3367             :     JSObject* holder;
    3368         517 :     if (!LookupPropertyPure(cx_, obj, id, &holder, &prop))
    3369           0 :         return false;
    3370         517 :     if (obj != holder)
    3371          26 :         return false;
    3372             : 
    3373         491 :     Shape* propShape = nullptr;
    3374         491 :     NativeObject* holderOrExpando = nullptr;
    3375             : 
    3376         491 :     if (obj->isNative()) {
    3377         491 :         propShape = prop.shape();
    3378         491 :         holderOrExpando = &obj->as<NativeObject>();
    3379             :     } else {
    3380           0 :         if (!obj->is<UnboxedPlainObject>())
    3381           0 :             return false;
    3382           0 :         UnboxedExpandoObject* expando = obj->as<UnboxedPlainObject>().maybeExpando();
    3383           0 :         if (!expando)
    3384           0 :             return false;
    3385           0 :         propShape = expando->lookupPure(id);
    3386           0 :         if (!propShape)
    3387           0 :             return false;
    3388           0 :         holderOrExpando = expando;
    3389             :     }
    3390             : 
    3391         491 :     MOZ_ASSERT(propShape);
    3392             : 
    3393             :     // The property must be the last added property of the object.
    3394         491 :     if (holderOrExpando->lastProperty() != propShape)
    3395          22 :         return false;
    3396             : 
    3397             :     // Object must be extensible, oldShape must be immediate parent of
    3398             :     // current shape.
    3399         469 :     if (!obj->nonProxyIsExtensible() || propShape->previous() != oldShape)
    3400           5 :         return false;
    3401             : 
    3402             :     // Basic shape checks.
    3403        1384 :     if (propShape->inDictionary() ||
    3404         909 :         !propShape->hasSlot() ||
    3405        1370 :         !propShape->hasDefaultSetter() ||
    3406         453 :         !propShape->writable())
    3407             :     {
    3408          11 :         return false;
    3409             :     }
    3410             : 
    3411             :     // Watch out for resolve hooks.
    3412         453 :     if (ClassMayResolveId(cx_->names(), obj->getClass(), id, obj)) {
    3413             :         // The JSFunction resolve hook defines a (non-configurable and
    3414             :         // non-enumerable) |prototype| property on certain functions. Scripts
    3415             :         // often assign a custom |prototype| object and we want to optimize
    3416             :         // this |prototype| set and eliminate the default object allocation.
    3417             :         //
    3418             :         // We check group->maybeInterpretedFunction() here and guard on the
    3419             :         // group. The group is unique for a particular function so this ensures
    3420             :         // we don't add the default prototype property to functions that don't
    3421             :         // have it.
    3422          27 :         if (!obj->is<JSFunction>() ||
    3423          10 :             !JSID_IS_ATOM(id, cx_->names().prototype) ||
    3424          21 :             !oldGroup->maybeInterpretedFunction() ||
    3425           5 :             !obj->as<JSFunction>().needsPrototypeProperty())
    3426             :         {
    3427           6 :             return false;
    3428             :         }
    3429           5 :         MOZ_ASSERT(!propShape->configurable());
    3430           5 :         MOZ_ASSERT(!propShape->enumerable());
    3431             :     }
    3432             : 
    3433             :     // Also watch out for addProperty hooks. Ignore the Array addProperty hook,
    3434             :     // because it doesn't do anything for non-index properties.
    3435         894 :     DebugOnly<uint32_t> index;
    3436         447 :     MOZ_ASSERT_IF(obj->is<ArrayObject>(), !IdIsIndex(id, &index));
    3437         447 :     if (!obj->is<ArrayObject>() && obj->getClass()->getAddProperty())
    3438           3 :         return false;
    3439             : 
    3440             :     // Walk up the object prototype chain and ensure that all prototypes are
    3441             :     // native, and that all prototypes have no setter defined on the property.
    3442        1024 :     for (JSObject* proto = obj->staticPrototype(); proto; proto = proto->staticPrototype()) {
    3443         580 :         if (!proto->isNative())
    3444           0 :             return false;
    3445             : 
    3446             :         // If prototype defines this property in a non-plain way, don't optimize.
    3447         580 :         Shape* protoShape = proto->as<NativeObject>().lookup(cx_, id);
    3448         580 :         if (protoShape && !protoShape->hasDefaultSetter())
    3449           0 :             return false;
    3450             : 
    3451             :         // Otherwise, if there's no such property, watch out for a resolve hook
    3452             :         // that would need to be invoked and thus prevent inlining of property
    3453             :         // addition. Allow the JSFunction resolve hook as it only defines plain
    3454             :         // data properties and we don't need to invoke it for objects on the
    3455             :         // proto chain.
    3456         585 :         if (ClassMayResolveId(cx_->names(), proto->getClass(), id, proto) &&
    3457           5 :             !proto->is<JSFunction>())
    3458             :         {
    3459           0 :             return false;
    3460             :         }
    3461             :     }
    3462             : 
    3463         444 :     ObjOperandId objId = writer.guardIsObject(objValId);
    3464         444 :     maybeEmitIdGuard(id);
    3465             : 
    3466         444 :     writer.guardGroup(objId, oldGroup);
    3467             : 
    3468             :     // If we are adding a property to an object for which the new script
    3469             :     // properties analysis hasn't been performed yet, make sure the stub fails
    3470             :     // after we run the analysis as a group change may be required here. The
    3471             :     // group change is not required for correctness but improves type
    3472             :     // information elsewhere.
    3473         444 :     if (oldGroup->newScript() && !oldGroup->newScript()->analyzed()) {
    3474         111 :         writer.guardGroupHasUnanalyzedNewScript(oldGroup);
    3475         111 :         MOZ_ASSERT(IsPreliminaryObject(obj));
    3476         111 :         preliminaryObjectAction_ = PreliminaryObjectAction::NotePreliminary;
    3477             :     } else {
    3478         333 :         preliminaryObjectAction_ = PreliminaryObjectAction::Unlink;
    3479             :     }
    3480             : 
    3481             :     // Shape guard the holder.
    3482         444 :     ObjOperandId holderId = objId;
    3483         444 :     if (!obj->isNative()) {
    3484           0 :         MOZ_ASSERT(obj->as<UnboxedPlainObject>().maybeExpando());
    3485           0 :         holderId = writer.guardAndLoadUnboxedExpando(objId);
    3486             :     }
    3487         444 :     writer.guardShape(holderId, oldShape);
    3488             : 
    3489         444 :     ShapeGuardProtoChain(writer, obj, objId);
    3490             : 
    3491         444 :     ObjectGroup* newGroup = obj->group();
    3492             : 
    3493             :     // Check if we have to change the object's group. If we're adding an
    3494             :     // unboxed expando property, we pass the expando object to AddAndStore*Slot.
    3495             :     // That's okay because we only have to do a group change if the object is a
    3496             :     // PlainObject.
    3497         444 :     bool changeGroup = oldGroup != newGroup;
    3498         444 :     MOZ_ASSERT_IF(changeGroup, obj->is<PlainObject>());
    3499             : 
    3500         444 :     if (holderOrExpando->isFixedSlot(propShape->slot())) {
    3501         110 :         size_t offset = NativeObject::getFixedSlotOffset(propShape->slot());
    3502         110 :         writer.addAndStoreFixedSlot(holderId, offset, rhsValId, propShape,
    3503         220 :                                     changeGroup, newGroup);
    3504         110 :         trackAttached("AddSlot");
    3505             :     } else {
    3506         334 :         size_t offset = holderOrExpando->dynamicSlotIndex(propShape->slot()) * sizeof(Value);
    3507         334 :         uint32_t numOldSlots = NativeObject::dynamicSlotsCount(oldShape);
    3508         334 :         uint32_t numNewSlots = NativeObject::dynamicSlotsCount(propShape);
    3509         334 :         if (numOldSlots == numNewSlots) {
    3510         274 :             writer.addAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
    3511         548 :                                           changeGroup, newGroup);
    3512         274 :             trackAttached("AddSlot");
    3513             :         } else {
    3514          60 :             MOZ_ASSERT(numNewSlots > numOldSlots);
    3515          60 :             writer.allocateAndStoreDynamicSlot(holderId, offset, rhsValId, propShape,
    3516         120 :                                                changeGroup, newGroup, numNewSlots);
    3517          60 :             trackAttached("AllocateSlot");
    3518             :         }
    3519             :     }
    3520         444 :     writer.returnFromIC();
    3521             : 
    3522         444 :     typeCheckInfo_.set(oldGroup, id);
    3523         444 :     return true;
    3524             : }
    3525             : 
    3526          80 : TypeOfIRGenerator::TypeOfIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    3527          80 :                                      ICState::Mode mode, HandleValue value)
    3528             :   : IRGenerator(cx, script, pc, CacheKind::TypeOf, mode),
    3529          80 :     val_(value)
    3530          80 : { }
    3531             : 
    3532             : bool
    3533          80 : TypeOfIRGenerator::tryAttachStub()
    3534             : {
    3535          80 :     MOZ_ASSERT(cacheKind_ == CacheKind::TypeOf);
    3536             : 
    3537         160 :     AutoAssertNoPendingException aanpe(cx_);
    3538             : 
    3539          80 :     ValOperandId valId(writer.setInputOperandId(0));
    3540             : 
    3541          80 :     if (tryAttachPrimitive(valId))
    3542          61 :         return true;
    3543             : 
    3544          19 :     MOZ_ALWAYS_TRUE(tryAttachObject(valId));
    3545          19 :     return true;
    3546             : }
    3547             : 
    3548             : bool
    3549          80 : TypeOfIRGenerator::tryAttachPrimitive(ValOperandId valId)
    3550             : {
    3551          80 :     if (!val_.isPrimitive())
    3552          19 :         return false;
    3553             : 
    3554          61 :     writer.guardType(valId, val_.isNumber() ? JSVAL_TYPE_DOUBLE : val_.extractNonDoubleType());
    3555          61 :     writer.loadStringResult(TypeName(js::TypeOfValue(val_), cx_->names()));
    3556          61 :     writer.returnFromIC();
    3557             : 
    3558          61 :     return true;
    3559             : }
    3560             : 
    3561             : bool
    3562          19 : TypeOfIRGenerator::tryAttachObject(ValOperandId valId)
    3563             : {
    3564          19 :     if (!val_.isObject())
    3565           0 :         return false;
    3566             : 
    3567          19 :     ObjOperandId objId = writer.guardIsObject(valId);
    3568          19 :     writer.loadTypeOfObjectResult(objId);
    3569          19 :     writer.returnFromIC();
    3570             : 
    3571          19 :     return true;
    3572             : }
    3573             : 
    3574             : 
    3575        3981 : CallIRGenerator::CallIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    3576             :                                  ICState::Mode mode, uint32_t argc,
    3577        3981 :                                  HandleValue callee, HandleValue thisval, HandleValueArray args)
    3578             :   : IRGenerator(cx, script, pc, CacheKind::Call, mode),
    3579             :     argc_(argc),
    3580             :     callee_(callee),
    3581             :     thisval_(thisval),
    3582             :     args_(args),
    3583        3981 :     cachedStrategy_()
    3584        3981 : { }
    3585             : 
    3586             : CallIRGenerator::OptStrategy
    3587        3981 : CallIRGenerator::canOptimize()
    3588             : {
    3589             :     // Ensure callee is a function.
    3590        3981 :     if (!callee_.isObject() || !callee_.toObject().is<JSFunction>())
    3591         815 :         return OptStrategy::None;
    3592             : 
    3593        6332 :     RootedFunction calleeFunc(cx_, &callee_.toObject().as<JSFunction>());
    3594             : 
    3595             :     OptStrategy strategy;
    3596        3166 :     if ((strategy = canOptimizeStringSplit(calleeFunc)) != OptStrategy::None) {
    3597           1 :         return strategy;
    3598             :     }
    3599             : 
    3600        3165 :     return OptStrategy::None;
    3601             : }
    3602             : 
    3603             : CallIRGenerator::OptStrategy
    3604        3166 : CallIRGenerator::canOptimizeStringSplit(HandleFunction calleeFunc)
    3605             : {
    3606        3166 :     if (argc_ != 2 || !args_[0].isString() || !args_[1].isString())
    3607        3062 :         return OptStrategy::None;
    3608             : 
    3609             :     // Just for now: if they're both atoms, then do not optimize using
    3610             :     // CacheIR and allow the legacy "ConstStringSplit" BaselineIC optimization
    3611             :     // to proceed.
    3612         104 :     if (args_[0].toString()->isAtom() && args_[1].toString()->isAtom())
    3613          43 :         return OptStrategy::None;
    3614             : 
    3615          61 :     if (!calleeFunc->isNative())
    3616          53 :         return OptStrategy::None;
    3617             : 
    3618           8 :     if (calleeFunc->native() != js::intrinsic_StringSplitString)
    3619           7 :         return OptStrategy::None;
    3620             : 
    3621           1 :     return OptStrategy::StringSplit;
    3622             : }
    3623             : 
    3624             : bool
    3625           1 : CallIRGenerator::tryAttachStringSplit()
    3626             : {
    3627             :     // Get the object group to use for this location.
    3628           2 :     RootedObjectGroup group(cx_, ObjectGroupCompartment::getStringSplitStringGroup(cx_));
    3629           1 :     if (!group) {
    3630           0 :         return false;
    3631             :     }
    3632             : 
    3633           2 :     AutoAssertNoPendingException aanpe(cx_);
    3634           1 :     Int32OperandId argcId(writer.setInputOperandId(0));
    3635             : 
    3636             :     // Ensure argc == 1.
    3637           1 :     writer.guardSpecificInt32Immediate(argcId, 2);
    3638             : 
    3639             :     // 1 argument only.  Stack-layout here is (bottom to top):
    3640             :     //
    3641             :     //  3: Callee
    3642             :     //  2: ThisValue
    3643             :     //  1: Arg0
    3644             :     //  0: Arg1 <-- Top of stack
    3645             : 
    3646             :     // Ensure callee is an object and is the function that matches the callee optimized
    3647             :     // against during stub generation (i.e. the String_split function object).
    3648           1 :     ValOperandId calleeValId = writer.loadStackValue(3);
    3649           1 :     ObjOperandId calleeObjId = writer.guardIsObject(calleeValId);
    3650           1 :     writer.guardIsNativeFunction(calleeObjId, js::intrinsic_StringSplitString);
    3651             : 
    3652             :     // Ensure arg0 is a string.
    3653           1 :     ValOperandId arg0ValId = writer.loadStackValue(1);
    3654           1 :     StringOperandId arg0StrId = writer.guardIsString(arg0ValId);
    3655             : 
    3656             :     // Ensure arg1 is a string.
    3657           1 :     ValOperandId arg1ValId = writer.loadStackValue(0);
    3658           1 :     StringOperandId arg1StrId = writer.guardIsString(arg1ValId);
    3659             : 
    3660             :     // Call custom string splitter VM-function.
    3661           1 :     writer.callStringSplitResult(arg0StrId, arg1StrId, group);
    3662           1 :     writer.typeMonitorResult();
    3663             : 
    3664           1 :     return true;
    3665             : }
    3666             : 
    3667             : CallIRGenerator::OptStrategy
    3668        3982 : CallIRGenerator::getOptStrategy(bool* optimizeAfterCall)
    3669             : {
    3670        3982 :     if (!cachedStrategy_) {
    3671        3981 :         cachedStrategy_ = mozilla::Some(canOptimize());
    3672             :     }
    3673        3982 :     if (optimizeAfterCall != nullptr) {
    3674        3981 :         MOZ_ASSERT(cachedStrategy_.isSome());
    3675        3981 :         switch (cachedStrategy_.value()) {
    3676             :           case OptStrategy::StringSplit:
    3677           1 :             *optimizeAfterCall = true;
    3678           1 :             break;
    3679             : 
    3680             :           default:
    3681        3980 :             *optimizeAfterCall = false;
    3682             :         }
    3683             :     }
    3684        3982 :     return cachedStrategy_.value();
    3685             : }
    3686             : 
    3687             : bool
    3688           1 : CallIRGenerator::tryAttachStub()
    3689             : {
    3690           1 :     OptStrategy strategy = getOptStrategy();
    3691             : 
    3692           1 :     if (strategy == OptStrategy::StringSplit) {
    3693           1 :         return tryAttachStringSplit();
    3694             :     }
    3695             : 
    3696           0 :     MOZ_ASSERT(strategy == OptStrategy::None);
    3697           0 :     return false;
    3698             : }
    3699             : 
    3700         998 : CompareIRGenerator::CompareIRGenerator(JSContext* cx, HandleScript script, jsbytecode* pc,
    3701             :                                        ICState::Mode mode, JSOp op,
    3702         998 :                                        HandleValue lhsVal, HandleValue rhsVal)
    3703             :   : IRGenerator(cx, script, pc, CacheKind::Compare, mode),
    3704         998 :     op_(op), lhsVal_(lhsVal), rhsVal_(rhsVal)
    3705         998 : { }
    3706             : 
    3707             : bool
    3708         747 : CompareIRGenerator::tryAttachString(ValOperandId lhsId, ValOperandId rhsId)
    3709             : {
    3710         747 :     MOZ_ASSERT(IsEqualityOp(op_));
    3711             : 
    3712         747 :     if (!lhsVal_.isString() || !rhsVal_.isString())
    3713         381 :         return false;
    3714             : 
    3715         366 :     StringOperandId lhsStrId = writer.guardIsString(lhsId);
    3716         366 :     StringOperandId rhsStrId = writer.guardIsString(rhsId);
    3717         366 :     writer.compareStringResult(op_, lhsStrId, rhsStrId);
    3718         366 :     writer.returnFromIC();
    3719             : 
    3720         366 :     trackAttached("String");
    3721         366 :     return true;
    3722             : }
    3723             : 
    3724             : bool
    3725         381 : CompareIRGenerator::tryAttachObject(ValOperandId lhsId, ValOperandId rhsId)
    3726             : {
    3727         381 :     MOZ_ASSERT(IsEqualityOp(op_));
    3728             : 
    3729         381 :     if (!lhsVal_.isObject() || !rhsVal_.isObject())
    3730         362 :         return false;
    3731             : 
    3732          19 :     ObjOperandId lhsObjId = writer.guardIsObject(lhsId);
    3733          19 :     ObjOperandId rhsObjId = writer.guardIsObject(rhsId);
    3734          19 :     writer.compareObjectResult(op_, lhsObjId, rhsObjId);
    3735          19 :     writer.returnFromIC();
    3736             : 
    3737          19 :     trackAttached("Object");
    3738          19 :     return true;
    3739             : }
    3740             : 
    3741             : bool
    3742         362 : CompareIRGenerator::tryAttachSymbol(ValOperandId lhsId, ValOperandId rhsId)
    3743             : {
    3744         362 :     MOZ_ASSERT(IsEqualityOp(op_));
    3745             : 
    3746         362 :     if (!lhsVal_.isSymbol() || !rhsVal_.isSymbol())
    3747         362 :         return false;
    3748             : 
    3749           0 :     SymbolOperandId lhsSymId = writer.guardIsSymbol(lhsId);
    3750           0 :     SymbolOperandId rhsSymId = writer.guardIsSymbol(rhsId);
    3751           0 :     writer.compareSymbolResult(op_, lhsSymId, rhsSymId);
    3752           0 :     writer.returnFromIC();
    3753             : 
    3754           0 :     trackAttached("Symbol");
    3755           0 :     return true;
    3756             : }
    3757             : 
    3758             : bool
    3759         998 : CompareIRGenerator::tryAttachStub()
    3760             : {
    3761         998 :     MOZ_ASSERT(cacheKind_ == CacheKind::Compare);
    3762         998 :     MOZ_ASSERT(IsEqualityOp(op_) ||
    3763             :                op_ == JSOP_LE || op_ == JSOP_LT ||
    3764             :                op_ == JSOP_GE || op_ == JSOP_GT);
    3765             : 
    3766        1996 :     AutoAssertNoPendingException aanpe(cx_);
    3767             : 
    3768         998 :     ValOperandId lhsId(writer.setInputOperandId(0));
    3769         998 :     ValOperandId rhsId(writer.setInputOperandId(1));
    3770             : 
    3771         998 :     if (IsEqualityOp(op_)) {
    3772         747 :         if (tryAttachString(lhsId, rhsId))
    3773         366 :             return true;
    3774         381 :         if (tryAttachObject(lhsId, rhsId))
    3775          19 :             return true;
    3776         362 :         if (tryAttachSymbol(lhsId, rhsId))
    3777           0 :             return true;
    3778             : 
    3779         362 :         trackNotAttached();
    3780         362 :         return false;
    3781             :     }
    3782             : 
    3783         251 :     trackNotAttached();
    3784         251 :     return false;
    3785             : }
    3786             : 
    3787             : void
    3788         385 : CompareIRGenerator::trackAttached(const char* name)
    3789             : {
    3790             : #ifdef JS_CACHEIR_SPEW
    3791         385 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    3792         385 :     if (sp.enabled()) {
    3793           0 :         LockGuard<Mutex> guard(sp.lock());
    3794           0 :         sp.beginCache(guard, *this);
    3795           0 :         sp.valueProperty(guard, "lhs", lhsVal_);
    3796           0 :         sp.valueProperty(guard, "rhs", rhsVal_);
    3797           0 :         sp.attached(guard, name);
    3798           0 :         sp.endCache(guard);
    3799             :     }
    3800             : #endif
    3801         385 : }
    3802             : 
    3803             : void
    3804         613 : CompareIRGenerator::trackNotAttached()
    3805             : {
    3806             : #ifdef JS_CACHEIR_SPEW
    3807         613 :     CacheIRSpewer& sp = CacheIRSpewer::singleton();
    3808         613 :     if (sp.enabled()) {
    3809           0 :         LockGuard<Mutex> guard(sp.lock());
    3810           0 :         sp.beginCache(guard, *this);
    3811           0 :         sp.valueProperty(guard, "lhs", lhsVal_);
    3812           0 :         sp.valueProperty(guard, "rhs", rhsVal_);
    3813           0 :         sp.endCache(guard);
    3814             :     }
    3815             : #endif
    3816         613 : }

Generated by: LCOV version 1.13