LCOV - code coverage report
Current view: top level - js/src/vm - ObjectGroup.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 536 969 55.3 %
Date: 2017-07-14 16:53:18 Functions: 63 96 65.6 %
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 "vm/ObjectGroup.h"
       8             : 
       9             : #include "jsexn.h"
      10             : #include "jshashutil.h"
      11             : #include "jsobj.h"
      12             : 
      13             : #include "builtin/DataViewObject.h"
      14             : #include "gc/Marking.h"
      15             : #include "gc/Policy.h"
      16             : #include "gc/StoreBuffer.h"
      17             : #include "gc/Zone.h"
      18             : #include "js/CharacterEncoding.h"
      19             : #include "vm/ArrayObject.h"
      20             : #include "vm/RegExpObject.h"
      21             : #include "vm/Shape.h"
      22             : #include "vm/TaggedProto.h"
      23             : #include "vm/UnboxedObject.h"
      24             : 
      25             : #include "jsobjinlines.h"
      26             : 
      27             : #include "vm/UnboxedObject-inl.h"
      28             : 
      29             : using namespace js;
      30             : 
      31             : using mozilla::DebugOnly;
      32             : using mozilla::PodZero;
      33             : 
      34             : /////////////////////////////////////////////////////////////////////
      35             : // ObjectGroup
      36             : /////////////////////////////////////////////////////////////////////
      37             : 
      38       43831 : ObjectGroup::ObjectGroup(const Class* clasp, TaggedProto proto, JSCompartment* comp,
      39       43831 :                          ObjectGroupFlags initialFlags)
      40             : {
      41       43831 :     PodZero(this);
      42             : 
      43             :     /* Windows may not appear on prototype chains. */
      44       43831 :     MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject()));
      45       43831 :     MOZ_ASSERT(JS::StringIsASCII(clasp->name));
      46             : 
      47       43831 :     this->clasp_ = clasp;
      48       43831 :     this->proto_ = proto;
      49       43831 :     this->compartment_ = comp;
      50       43831 :     this->flags_ = initialFlags;
      51             : 
      52       43831 :     setGeneration(zone()->types.generation);
      53       43831 : }
      54             : 
      55             : void
      56           0 : ObjectGroup::finalize(FreeOp* fop)
      57             : {
      58           0 :     if (newScriptDontCheckGeneration())
      59           0 :         newScriptDontCheckGeneration()->clear();
      60           0 :     fop->delete_(newScriptDontCheckGeneration());
      61           0 :     fop->delete_(maybeUnboxedLayoutDontCheckGeneration());
      62           0 :     if (maybePreliminaryObjectsDontCheckGeneration())
      63           0 :         maybePreliminaryObjectsDontCheckGeneration()->clear();
      64           0 :     fop->delete_(maybePreliminaryObjectsDontCheckGeneration());
      65           0 : }
      66             : 
      67             : void
      68        9769 : ObjectGroup::setProtoUnchecked(TaggedProto proto)
      69             : {
      70        9769 :     proto_ = proto;
      71        9769 :     MOZ_ASSERT_IF(proto_.isObject() && proto_.toObject()->isNative(),
      72             :                   proto_.toObject()->isDelegate());
      73        9769 : }
      74             : 
      75             : void
      76        2908 : ObjectGroup::setProto(TaggedProto proto)
      77             : {
      78        2908 :     MOZ_ASSERT(singleton());
      79        2908 :     setProtoUnchecked(proto);
      80        2908 : }
      81             : 
      82             : size_t
      83           0 : ObjectGroup::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
      84             : {
      85           0 :     size_t n = 0;
      86           0 :     if (TypeNewScript* newScript = newScriptDontCheckGeneration())
      87           0 :         n += newScript->sizeOfIncludingThis(mallocSizeOf);
      88           0 :     if (UnboxedLayout* layout = maybeUnboxedLayoutDontCheckGeneration())
      89           0 :         n += layout->sizeOfIncludingThis(mallocSizeOf);
      90           0 :     return n;
      91             : }
      92             : 
      93             : void
      94       26324 : ObjectGroup::setAddendum(AddendumKind kind, void* addendum, bool writeBarrier /* = true */)
      95             : {
      96       26324 :     MOZ_ASSERT(!needsSweep());
      97       26325 :     MOZ_ASSERT(kind <= (OBJECT_FLAG_ADDENDUM_MASK >> OBJECT_FLAG_ADDENDUM_SHIFT));
      98             : 
      99       26325 :     if (writeBarrier) {
     100             :         // Manually trigger barriers if we are clearing new script or
     101             :         // preliminary object information. Other addendums are immutable.
     102       26325 :         switch (addendumKind()) {
     103             :           case Addendum_PreliminaryObjects:
     104          58 :             PreliminaryObjectArrayWithTemplate::writeBarrierPre(maybePreliminaryObjects());
     105          58 :             break;
     106             :           case Addendum_NewScript:
     107           9 :             TypeNewScript::writeBarrierPre(newScript());
     108           9 :             break;
     109             :           case Addendum_None:
     110       25947 :             break;
     111             :           default:
     112         311 :             MOZ_ASSERT(addendumKind() == kind);
     113             :         }
     114             :     }
     115             : 
     116       26325 :     flags_ &= ~OBJECT_FLAG_ADDENDUM_MASK;
     117       26325 :     flags_ |= kind << OBJECT_FLAG_ADDENDUM_SHIFT;
     118       26325 :     addendum_ = addendum;
     119       26325 : }
     120             : 
     121             : /* static */ bool
     122       27530 : ObjectGroup::useSingletonForClone(JSFunction* fun)
     123             : {
     124       27530 :     if (!fun->isInterpreted())
     125           0 :         return false;
     126             : 
     127       27530 :     if (fun->isArrow())
     128        2452 :         return false;
     129             : 
     130       25078 :     if (fun->isSingleton())
     131           0 :         return false;
     132             : 
     133             :     /*
     134             :      * When a function is being used as a wrapper for another function, it
     135             :      * improves precision greatly to distinguish between different instances of
     136             :      * the wrapper; otherwise we will conflate much of the information about
     137             :      * the wrapped functions.
     138             :      *
     139             :      * An important example is the Class.create function at the core of the
     140             :      * Prototype.js library, which looks like:
     141             :      *
     142             :      * var Class = {
     143             :      *   create: function() {
     144             :      *     return function() {
     145             :      *       this.initialize.apply(this, arguments);
     146             :      *     }
     147             :      *   }
     148             :      * };
     149             :      *
     150             :      * Each instance of the innermost function will have a different wrapped
     151             :      * initialize method. We capture this, along with similar cases, by looking
     152             :      * for short scripts which use both .apply and arguments. For such scripts,
     153             :      * whenever creating a new instance of the function we both give that
     154             :      * instance a singleton type and clone the underlying script.
     155             :      */
     156             : 
     157             :     uint32_t begin, end;
     158       25078 :     if (fun->hasScript()) {
     159       22448 :         if (!fun->nonLazyScript()->isLikelyConstructorWrapper())
     160       22448 :             return false;
     161           0 :         begin = fun->nonLazyScript()->sourceStart();
     162           0 :         end = fun->nonLazyScript()->sourceEnd();
     163             :     } else {
     164        2630 :         if (!fun->lazyScript()->isLikelyConstructorWrapper())
     165        2630 :             return false;
     166           0 :         begin = fun->lazyScript()->begin();
     167           0 :         end = fun->lazyScript()->end();
     168             :     }
     169             : 
     170           0 :     return end - begin <= 100;
     171             : }
     172             : 
     173             : /* static */ bool
     174        6279 : ObjectGroup::useSingletonForNewObject(JSContext* cx, JSScript* script, jsbytecode* pc)
     175             : {
     176             :     /*
     177             :      * Make a heuristic guess at a use of JSOP_NEW that the constructed object
     178             :      * should have a fresh group. We do this when the NEW is immediately
     179             :      * followed by a simple assignment to an object's .prototype field.
     180             :      * This is designed to catch common patterns for subclassing in JS:
     181             :      *
     182             :      * function Super() { ... }
     183             :      * function Sub1() { ... }
     184             :      * function Sub2() { ... }
     185             :      *
     186             :      * Sub1.prototype = new Super();
     187             :      * Sub2.prototype = new Super();
     188             :      *
     189             :      * Using distinct groups for the particular prototypes of Sub1 and
     190             :      * Sub2 lets us continue to distinguish the two subclasses and any extra
     191             :      * properties added to those prototype objects.
     192             :      */
     193        6279 :     if (script->isStarGenerator() || script->isLegacyGenerator() || script->isAsync())
     194          92 :         return false;
     195        6187 :     if (JSOp(*pc) != JSOP_NEW)
     196        5738 :         return false;
     197         449 :     pc += JSOP_NEW_LENGTH;
     198         449 :     if (JSOp(*pc) == JSOP_SETPROP) {
     199           4 :         if (script->getName(pc) == cx->names().prototype)
     200           0 :             return true;
     201             :     }
     202         449 :     return false;
     203             : }
     204             : 
     205             : /* static */ bool
     206       10230 : ObjectGroup::useSingletonForAllocationSite(JSScript* script, jsbytecode* pc, JSProtoKey key)
     207             : {
     208             :     // The return value of this method can either be tested like a boolean or
     209             :     // passed to a NewObject method.
     210             :     JS_STATIC_ASSERT(GenericObject == 0);
     211             : 
     212             :     /*
     213             :      * Objects created outside loops in global and eval scripts should have
     214             :      * singleton types. For now this is only done for plain objects, but not
     215             :      * typed arrays or normal arrays.
     216             :      */
     217             : 
     218       10230 :     if (script->functionNonDelazifying() && !script->treatAsRunOnce())
     219        7403 :         return GenericObject;
     220             : 
     221        2827 :     if (key != JSProto_Object)
     222        1516 :         return GenericObject;
     223             : 
     224             :     // All loops in the script will have a try note indicating their boundary.
     225             : 
     226        1311 :     if (!script->hasTrynotes())
     227        1231 :         return SingletonObject;
     228             : 
     229          80 :     unsigned offset = script->pcToOffset(pc);
     230             : 
     231          80 :     JSTryNote* tn = script->trynotes()->vector;
     232          80 :     JSTryNote* tnlimit = tn + script->trynotes()->length;
     233         710 :     for (; tn < tnlimit; tn++) {
     234         319 :         if (tn->kind != JSTRY_FOR_IN && tn->kind != JSTRY_FOR_OF && tn->kind != JSTRY_LOOP)
     235         240 :             continue;
     236             : 
     237          79 :         unsigned startOffset = script->mainOffset() + tn->start;
     238          79 :         unsigned endOffset = startOffset + tn->length;
     239             : 
     240          79 :         if (offset >= startOffset && offset < endOffset)
     241           4 :             return GenericObject;
     242             :     }
     243             : 
     244          76 :     return SingletonObject;
     245             : }
     246             : 
     247             : /* static */ bool
     248           0 : ObjectGroup::useSingletonForAllocationSite(JSScript* script, jsbytecode* pc, const Class* clasp)
     249             : {
     250           0 :     return useSingletonForAllocationSite(script, pc, JSCLASS_CACHED_PROTO_KEY(clasp));
     251             : }
     252             : 
     253             : /////////////////////////////////////////////////////////////////////
     254             : // JSObject
     255             : /////////////////////////////////////////////////////////////////////
     256             : 
     257             : bool
     258         311 : JSObject::shouldSplicePrototype()
     259             : {
     260             :     /*
     261             :      * During bootstrapping, if inference is enabled we need to make sure not
     262             :      * to splice a new prototype in for Function.prototype or the global
     263             :      * object if their __proto__ had previously been set to null, as this
     264             :      * will change the prototype for all other objects with the same type.
     265             :      */
     266         311 :     if (staticPrototype() != nullptr)
     267           0 :         return false;
     268         311 :     return isSingleton();
     269             : }
     270             : 
     271             : /* static */ bool
     272        2908 : JSObject::splicePrototype(JSContext* cx, HandleObject obj, const Class* clasp,
     273             :                           Handle<TaggedProto> proto)
     274             : {
     275        2908 :     MOZ_ASSERT(cx->compartment() == obj->compartment());
     276             : 
     277             :     /*
     278             :      * For singleton groups representing only a single JSObject, the proto
     279             :      * can be rearranged as needed without destroying type information for
     280             :      * the old or new types.
     281             :      */
     282        2908 :     MOZ_ASSERT(obj->isSingleton());
     283             : 
     284             :     // Windows may not appear on prototype chains.
     285        2908 :     MOZ_ASSERT_IF(proto.isObject(), !IsWindow(proto.toObject()));
     286             : 
     287        2908 :     if (proto.isObject()) {
     288        5804 :         RootedObject protoObj(cx, proto.toObject());
     289        2902 :         if (!JSObject::setDelegate(cx, protoObj))
     290           0 :             return false;
     291             :     }
     292             : 
     293             :     // Force type instantiation when splicing lazy group.
     294        5816 :     RootedObjectGroup group(cx, JSObject::getGroup(cx, obj));
     295        2908 :     if (!group)
     296           0 :         return false;
     297        5816 :     RootedObjectGroup protoGroup(cx, nullptr);
     298        2908 :     if (proto.isObject()) {
     299        5804 :         RootedObject protoObj(cx, proto.toObject());
     300        2902 :         protoGroup = JSObject::getGroup(cx, protoObj);
     301        2902 :         if (!protoGroup)
     302           0 :             return false;
     303             :     }
     304             : 
     305        2908 :     group->setClasp(clasp);
     306        2908 :     group->setProto(proto);
     307        2908 :     return true;
     308             : }
     309             : 
     310             : /* static */ ObjectGroup*
     311        3859 : JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
     312             : {
     313        3859 :     MOZ_ASSERT(obj->hasLazyGroup());
     314        3859 :     MOZ_ASSERT(cx->compartment() == obj->compartment());
     315             : 
     316             :     // Find flags which need to be specified immediately on the object.
     317             :     // Don't track whether singletons are packed.
     318        3859 :     ObjectGroupFlags initialFlags = OBJECT_FLAG_SINGLETON | OBJECT_FLAG_NON_PACKED;
     319             : 
     320        3859 :     if (obj->isIteratedSingleton())
     321           3 :         initialFlags |= OBJECT_FLAG_ITERATED;
     322             : 
     323        3859 :     if (obj->isIndexed())
     324           0 :         initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
     325             : 
     326        3859 :     if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
     327           0 :         initialFlags |= OBJECT_FLAG_LENGTH_OVERFLOW;
     328             : 
     329        7718 :     Rooted<TaggedProto> proto(cx, obj->taggedProto());
     330        7718 :     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, obj->getClass(), proto,
     331        3859 :                                                            initialFlags);
     332        3859 :     if (!group)
     333           0 :         return nullptr;
     334             : 
     335        7718 :     AutoEnterAnalysis enter(cx);
     336             : 
     337             :     /* Fill in the type according to the state of this object. */
     338             : 
     339        3859 :     if (obj->is<JSFunction>() && obj->as<JSFunction>().isInterpreted())
     340         528 :         group->setInterpretedFunction(&obj->as<JSFunction>());
     341             : 
     342        3859 :     obj->group_ = group;
     343             : 
     344        3859 :     return group;
     345             : }
     346             : 
     347             : /* static */ bool
     348         913 : JSObject::setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj)
     349             : {
     350         913 :     ObjectGroup::setDefaultNewGroupUnknown(cx, clasp, obj);
     351         913 :     return JSObject::setFlags(cx, obj, BaseShape::NEW_GROUP_UNKNOWN);
     352             : }
     353             : 
     354             : /////////////////////////////////////////////////////////////////////
     355             : // ObjectGroupCompartment NewTable
     356             : /////////////////////////////////////////////////////////////////////
     357             : 
     358             : /*
     359             :  * Entries for the per-compartment set of groups which are the default
     360             :  * types to use for some prototype. An optional associated object is used which
     361             :  * allows multiple groups to be created with the same prototype. The
     362             :  * associated object may be a function (for types constructed with 'new') or a
     363             :  * type descriptor (for typed objects). These entries are also used for the set
     364             :  * of lazy groups in the compartment, which use a null associated object
     365             :  * (though there are only a few of these per compartment).
     366             :  */
     367       38763 : struct ObjectGroupCompartment::NewEntry
     368             : {
     369             :     ReadBarrieredObjectGroup group;
     370             : 
     371             :     // Note: This pointer is only used for equality and does not need a read barrier.
     372             :     JSObject* associated;
     373             : 
     374       13007 :     NewEntry(ObjectGroup* group, JSObject* associated)
     375       13007 :       : group(group), associated(associated)
     376       13007 :     {}
     377             : 
     378             :     struct Lookup {
     379             :         const Class* clasp;
     380             :         TaggedProto proto;
     381             :         JSObject* associated;
     382             : 
     383      115698 :         Lookup(const Class* clasp, TaggedProto proto, JSObject* associated)
     384      115698 :           : clasp(clasp), proto(proto), associated(associated)
     385      115697 :         {}
     386             : 
     387           0 :         explicit Lookup(const NewEntry& entry)
     388           0 :           : clasp(entry.group.unbarrieredGet()->clasp()),
     389           0 :             proto(entry.group.unbarrieredGet()->proto()),
     390           0 :             associated(entry.associated)
     391             :         {
     392           0 :             if (associated && associated->is<JSFunction>())
     393           0 :                 clasp = nullptr;
     394           0 :         }
     395             :     };
     396             : 
     397         915 :     static bool hasHash(const Lookup& l) {
     398         917 :         return MovableCellHasher<TaggedProto>::hasHash(l.proto) &&
     399         917 :                MovableCellHasher<JSObject*>::hasHash(l.associated);
     400             :     }
     401             : 
     402      114784 :     static bool ensureHash(const Lookup& l) {
     403      229568 :         return MovableCellHasher<TaggedProto>::ensureHash(l.proto) &&
     404      229568 :                MovableCellHasher<JSObject*>::ensureHash(l.associated);
     405             :     }
     406             : 
     407      114786 :     static inline HashNumber hash(const Lookup& lookup) {
     408      114786 :         HashNumber hash = uintptr_t(lookup.clasp);
     409      114786 :         hash = mozilla::RotateLeft(hash, 4) ^ MovableCellHasher<TaggedProto>::hash(lookup.proto);
     410      114787 :         hash = mozilla::RotateLeft(hash, 4) ^ MovableCellHasher<JSObject*>::hash(lookup.associated);
     411      114787 :         return hash;
     412             :     }
     413             : 
     414      101779 :     static inline bool match(const ObjectGroupCompartment::NewEntry& key, const Lookup& lookup) {
     415      101779 :         if (lookup.clasp && key.group.unbarrieredGet()->clasp() != lookup.clasp)
     416           0 :             return false;
     417             : 
     418      101779 :         TaggedProto proto = key.group.unbarrieredGet()->proto();
     419      101779 :         if (!MovableCellHasher<TaggedProto>::match(proto, lookup.proto))
     420           0 :             return false;
     421             : 
     422      101779 :         return MovableCellHasher<JSObject*>::match(key.associated, lookup.associated);
     423             :     }
     424             : 
     425             :     static void rekey(NewEntry& k, const NewEntry& newKey) { k = newKey; }
     426             : 
     427           0 :     bool needsSweep() {
     428           0 :         return IsAboutToBeFinalized(&group) ||
     429           0 :                (associated && IsAboutToBeFinalizedUnbarriered(&associated));
     430             :     }
     431             : 
     432           0 :     bool operator==(const NewEntry& other) const {
     433           0 :         return group == other.group && associated == other.associated;
     434             :     }
     435             : };
     436             : 
     437             : namespace js {
     438             : template <>
     439             : struct FallibleHashMethods<ObjectGroupCompartment::NewEntry>
     440             : {
     441         915 :     template <typename Lookup> static bool hasHash(Lookup&& l) {
     442         915 :         return ObjectGroupCompartment::NewEntry::hasHash(mozilla::Forward<Lookup>(l));
     443             :     }
     444      114784 :     template <typename Lookup> static bool ensureHash(Lookup&& l) {
     445      114784 :         return ObjectGroupCompartment::NewEntry::ensureHash(mozilla::Forward<Lookup>(l));
     446             :     }
     447             : };
     448             : } // namespace js
     449             : 
     450           0 : class ObjectGroupCompartment::NewTable : public JS::WeakCache<js::GCHashSet<NewEntry, NewEntry,
     451             :                                                                             SystemAllocPolicy>>
     452             : {
     453             :     using Table = js::GCHashSet<NewEntry, NewEntry, SystemAllocPolicy>;
     454             :     using Base = JS::WeakCache<Table>;
     455             : 
     456             :   public:
     457         622 :     explicit NewTable(Zone* zone) : Base(zone) {}
     458             : };
     459             : 
     460             : MOZ_ALWAYS_INLINE ObjectGroup*
     461      129966 : ObjectGroupCompartment::DefaultNewGroupCache::lookup(const Class* clasp, TaggedProto proto,
     462             :                                                      JSObject* associated)
     463             : {
     464      389563 :     if (group_ &&
     465      258418 :         associated_ == associated &&
     466      450191 :         group_->proto() == proto &&
     467       97516 :         (!clasp || group_->clasp() == clasp))
     468             :     {
     469       93874 :         return group_;
     470             :     }
     471       36092 :     return nullptr;
     472             : }
     473             : 
     474             : /* static */ ObjectGroup*
     475      129966 : ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
     476             :                              TaggedProto proto, JSObject* associated)
     477             : {
     478      129966 :     MOZ_ASSERT_IF(associated, proto.isObject());
     479      129966 :     MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
     480             : 
     481             :     // A null lookup clasp is used for 'new' groups with an associated
     482             :     // function. The group starts out as a plain object but might mutate into an
     483             :     // unboxed plain object.
     484      129963 :     MOZ_ASSERT_IF(!clasp, !!associated);
     485             : 
     486      129963 :     if (associated && !associated->is<TypeDescr>()) {
     487         528 :         MOZ_ASSERT(!clasp);
     488         528 :         if (associated->is<JSFunction>()) {
     489             : 
     490             :             // Canonicalize new functions to use the original one associated with its script.
     491         528 :             JSFunction* fun = &associated->as<JSFunction>();
     492         528 :             if (fun->hasScript())
     493         528 :                 associated = fun->nonLazyScript()->functionNonDelazifying();
     494           0 :             else if (fun->isInterpretedLazy() && !fun->isSelfHostedBuiltin())
     495           0 :                 associated = fun->lazyScript()->functionNonDelazifying();
     496             :             else
     497           0 :                 associated = nullptr;
     498             : 
     499             :             // If we have previously cleared the 'new' script information for this
     500             :             // function, don't try to construct another one.
     501         528 :             if (associated && associated->wasNewScriptCleared())
     502          28 :                 associated = nullptr;
     503             : 
     504             :         } else {
     505           0 :             associated = nullptr;
     506             :         }
     507             : 
     508         528 :         if (!associated)
     509          28 :             clasp = &PlainObject::class_;
     510             :     }
     511             : 
     512      129963 :     ObjectGroupCompartment& groups = cx->compartment()->objectGroups;
     513             : 
     514      129963 :     if (ObjectGroup* group = groups.defaultNewGroupCache.lookup(clasp, proto, associated))
     515       93874 :         return group;
     516             : 
     517       72184 :     AutoEnterAnalysis enter(cx);
     518             : 
     519       36092 :     ObjectGroupCompartment::NewTable*& table = groups.defaultNewTable;
     520             : 
     521       36092 :     if (!table) {
     522         311 :         table = cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());
     523         311 :         if (!table || !table->init()) {
     524           0 :             js_delete(table);
     525           0 :             table = nullptr;
     526           0 :             ReportOutOfMemory(cx);
     527           0 :             return nullptr;
     528             :         }
     529             :     }
     530             : 
     531       36092 :     if (proto.isObject() && !proto.toObject()->isDelegate()) {
     532        6092 :         RootedObject protoObj(cx, proto.toObject());
     533        3046 :         if (!JSObject::setDelegate(cx, protoObj))
     534           0 :             return nullptr;
     535             : 
     536             :         // Objects which are prototypes of one another should be singletons, so
     537             :         // that their type information can be tracked more precisely. Limit
     538             :         // this group change to plain objects, to avoid issues with other types
     539             :         // of singletons like typed arrays.
     540        3046 :         if (protoObj->is<PlainObject>() && !protoObj->isSingleton()) {
     541         109 :             if (!JSObject::changeToSingleton(cx, protoObj))
     542           0 :                 return nullptr;
     543             :         }
     544             :     }
     545             : 
     546             :     ObjectGroupCompartment::NewTable::AddPtr p =
     547       36092 :         table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated));
     548       36092 :     if (p) {
     549       27167 :         ObjectGroup* group = p->group;
     550       27167 :         MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
     551       27167 :         MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
     552             :                               group->clasp() == &UnboxedPlainObject::class_);
     553       27167 :         MOZ_ASSERT(group->proto() == proto);
     554       27167 :         groups.defaultNewGroupCache.put(group, associated);
     555       27167 :         return group;
     556             :     }
     557             : 
     558        8925 :     ObjectGroupFlags initialFlags = 0;
     559        8925 :     if (proto.isDynamic() || (proto.isObject() && proto.toObject()->isNewGroupUnknown()))
     560        3295 :         initialFlags = OBJECT_FLAG_DYNAMIC_MASK;
     561             : 
     562       17850 :     Rooted<TaggedProto> protoRoot(cx, proto);
     563       17850 :     ObjectGroup* group = ObjectGroupCompartment::makeGroup(cx, clasp ? clasp : &PlainObject::class_,
     564        8925 :                                                            protoRoot, initialFlags);
     565        8925 :     if (!group)
     566           0 :         return nullptr;
     567             : 
     568        8925 :     if (!table->add(p, ObjectGroupCompartment::NewEntry(group, associated))) {
     569           0 :         ReportOutOfMemory(cx);
     570           0 :         return nullptr;
     571             :     }
     572             : 
     573        8925 :     if (associated) {
     574         195 :         if (associated->is<JSFunction>()) {
     575         195 :             if (!TypeNewScript::make(cx, group, &associated->as<JSFunction>()))
     576           0 :                 return nullptr;
     577             :         } else {
     578           0 :             group->setTypeDescr(&associated->as<TypeDescr>());
     579             :         }
     580             :     }
     581             : 
     582             :     /*
     583             :      * Some builtin objects have slotful native properties baked in at
     584             :      * creation via the Shape::{insert,get}initialShape mechanism. Since
     585             :      * these properties are never explicitly defined on new objects, update
     586             :      * the type information for them here.
     587             :      */
     588             : 
     589        8925 :     const JSAtomState& names = cx->names();
     590             : 
     591        8925 :     if (clasp == &RegExpObject::class_) {
     592          77 :         AddTypePropertyId(cx, group, nullptr, NameToId(names.lastIndex), TypeSet::Int32Type());
     593        8848 :     } else if (clasp == &StringObject::class_) {
     594          49 :         AddTypePropertyId(cx, group, nullptr, NameToId(names.length), TypeSet::Int32Type());
     595        8799 :     } else if (ErrorObject::isErrorClass(clasp)) {
     596           3 :         AddTypePropertyId(cx, group, nullptr, NameToId(names.fileName), TypeSet::StringType());
     597           3 :         AddTypePropertyId(cx, group, nullptr, NameToId(names.lineNumber), TypeSet::Int32Type());
     598           3 :         AddTypePropertyId(cx, group, nullptr, NameToId(names.columnNumber), TypeSet::Int32Type());
     599             :     }
     600             : 
     601        8925 :     groups.defaultNewGroupCache.put(group, associated);
     602        8925 :     return group;
     603             : }
     604             : 
     605             : /* static */ ObjectGroup*
     606       78692 : ObjectGroup::lazySingletonGroup(JSContext* cx, const Class* clasp, TaggedProto proto)
     607             : {
     608       78692 :     MOZ_ASSERT_IF(proto.isObject(), cx->compartment() == proto.toObject()->compartment());
     609             : 
     610       78692 :     ObjectGroupCompartment::NewTable*& table = cx->compartment()->objectGroups.lazyTable;
     611             : 
     612       78692 :     if (!table) {
     613         311 :         table = cx->new_<ObjectGroupCompartment::NewTable>(cx->zone());
     614         311 :         if (!table || !table->init()) {
     615           0 :             ReportOutOfMemory(cx);
     616           0 :             js_delete(table);
     617           0 :             table = nullptr;
     618           0 :             return nullptr;
     619             :         }
     620             :     }
     621             : 
     622             :     ObjectGroupCompartment::NewTable::AddPtr p =
     623       78692 :         table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, nullptr));
     624       78692 :     if (p) {
     625       74611 :         ObjectGroup* group = p->group;
     626       74610 :         MOZ_ASSERT(group->lazy());
     627             : 
     628       74610 :         return group;
     629             :     }
     630             : 
     631        8162 :     AutoEnterAnalysis enter(cx);
     632             : 
     633        8162 :     Rooted<TaggedProto> protoRoot(cx, proto);
     634             :     ObjectGroup* group =
     635        8162 :         ObjectGroupCompartment::makeGroup(cx, clasp, protoRoot,
     636        4081 :                                           OBJECT_FLAG_SINGLETON | OBJECT_FLAG_LAZY_SINGLETON);
     637        4081 :     if (!group)
     638           0 :         return nullptr;
     639             : 
     640        4081 :     if (!table->add(p, ObjectGroupCompartment::NewEntry(group, nullptr))) {
     641           0 :         ReportOutOfMemory(cx);
     642           0 :         return nullptr;
     643             :     }
     644             : 
     645        4081 :     return group;
     646             : }
     647             : 
     648             : /* static */ void
     649         913 : ObjectGroup::setDefaultNewGroupUnknown(JSContext* cx, const Class* clasp, HandleObject obj)
     650             : {
     651             :     // If the object already has a new group, mark that group as unknown.
     652         913 :     ObjectGroupCompartment::NewTable* table = cx->compartment()->objectGroups.defaultNewTable;
     653         913 :     if (table) {
     654        1826 :         Rooted<TaggedProto> taggedProto(cx, TaggedProto(obj));
     655         913 :         auto lookup = ObjectGroupCompartment::NewEntry::Lookup(clasp, taggedProto, nullptr);
     656         913 :         auto p = table->lookup(lookup);
     657         913 :         if (p)
     658           0 :             MarkObjectGroupUnknownProperties(cx, p->group);
     659             :     }
     660         913 : }
     661             : 
     662             : #ifdef DEBUG
     663             : /* static */ bool
     664           0 : ObjectGroup::hasDefaultNewGroup(JSObject* proto, const Class* clasp, ObjectGroup* group)
     665             : {
     666           0 :     ObjectGroupCompartment::NewTable* table = proto->compartment()->objectGroups.defaultNewTable;
     667             : 
     668           0 :     if (table) {
     669           0 :         auto lookup = ObjectGroupCompartment::NewEntry::Lookup(clasp, TaggedProto(proto), nullptr);
     670           0 :         auto p = table->lookup(lookup);
     671           0 :         return p && p->group == group;
     672             :     }
     673           0 :     return false;
     674             : }
     675             : #endif /* DEBUG */
     676             : 
     677             : inline const Class*
     678        2071 : GetClassForProtoKey(JSProtoKey key)
     679             : {
     680        2071 :     switch (key) {
     681             :       case JSProto_Null:
     682             :       case JSProto_Object:
     683         588 :         return &PlainObject::class_;
     684             :       case JSProto_Array:
     685        1483 :         return &ArrayObject::class_;
     686             : 
     687             :       case JSProto_Number:
     688           0 :         return &NumberObject::class_;
     689             :       case JSProto_Boolean:
     690           0 :         return &BooleanObject::class_;
     691             :       case JSProto_String:
     692           0 :         return &StringObject::class_;
     693             :       case JSProto_Symbol:
     694           0 :         return &SymbolObject::class_;
     695             :       case JSProto_RegExp:
     696           0 :         return &RegExpObject::class_;
     697             : 
     698             :       case JSProto_Int8Array:
     699             :       case JSProto_Uint8Array:
     700             :       case JSProto_Int16Array:
     701             :       case JSProto_Uint16Array:
     702             :       case JSProto_Int32Array:
     703             :       case JSProto_Uint32Array:
     704             :       case JSProto_Float32Array:
     705             :       case JSProto_Float64Array:
     706             :       case JSProto_Uint8ClampedArray:
     707           0 :         return &TypedArrayObject::classes[key - JSProto_Int8Array];
     708             : 
     709             :       case JSProto_ArrayBuffer:
     710           0 :         return &ArrayBufferObject::class_;
     711             : 
     712             :       case JSProto_SharedArrayBuffer:
     713           0 :         return &SharedArrayBufferObject::class_;
     714             : 
     715             :       case JSProto_DataView:
     716           0 :         return &DataViewObject::class_;
     717             : 
     718             :       default:
     719           0 :         MOZ_CRASH("Bad proto key");
     720             :     }
     721             : }
     722             : 
     723             : /* static */ ObjectGroup*
     724           0 : ObjectGroup::defaultNewGroup(JSContext* cx, JSProtoKey key)
     725             : {
     726           0 :     RootedObject proto(cx);
     727           0 :     if (key != JSProto_Null && !GetBuiltinPrototype(cx, key, &proto))
     728           0 :         return nullptr;
     729           0 :     return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto.get()));
     730             : }
     731             : 
     732             : /////////////////////////////////////////////////////////////////////
     733             : // ObjectGroupCompartment ArrayObjectTable
     734             : /////////////////////////////////////////////////////////////////////
     735             : 
     736             : struct ObjectGroupCompartment::ArrayObjectKey : public DefaultHasher<ArrayObjectKey>
     737             : {
     738             :     TypeSet::Type type;
     739             : 
     740             :     ArrayObjectKey()
     741             :       : type(TypeSet::UndefinedType())
     742             :     {}
     743             : 
     744         927 :     explicit ArrayObjectKey(TypeSet::Type type)
     745         927 :       : type(type)
     746         927 :     {}
     747             : 
     748         927 :     static inline uint32_t hash(const ArrayObjectKey& v) {
     749         927 :         return v.type.raw();
     750             :     }
     751             : 
     752         811 :     static inline bool match(const ArrayObjectKey& v1, const ArrayObjectKey& v2) {
     753         811 :         return v1.type == v2.type;
     754             :     }
     755             : 
     756             :     bool operator==(const ArrayObjectKey& other) {
     757             :         return type == other.type;
     758             :     }
     759             : 
     760             :     bool operator!=(const ArrayObjectKey& other) {
     761             :         return !(*this == other);
     762             :     }
     763             : 
     764           0 :     bool needsSweep() {
     765           0 :         MOZ_ASSERT(type.isUnknown() || !type.isSingleton());
     766           0 :         if (!type.isUnknown() && type.isGroup()) {
     767           0 :             ObjectGroup* group = type.groupNoBarrier();
     768           0 :             if (IsAboutToBeFinalizedUnbarriered(&group))
     769           0 :                 return true;
     770           0 :             if (group != type.groupNoBarrier())
     771           0 :                 type = TypeSet::ObjectType(group);
     772             :         }
     773           0 :         return false;
     774             :     }
     775             : };
     776             : 
     777             : static inline bool
     778           4 : NumberTypes(TypeSet::Type a, TypeSet::Type b)
     779             : {
     780           8 :     return (a.isPrimitive(JSVAL_TYPE_INT32) || a.isPrimitive(JSVAL_TYPE_DOUBLE))
     781           4 :         && (b.isPrimitive(JSVAL_TYPE_INT32) || b.isPrimitive(JSVAL_TYPE_DOUBLE));
     782             : }
     783             : 
     784             : /*
     785             :  * As for GetValueType, but requires object types to be non-singletons with
     786             :  * their default prototype. These are the only values that should appear in
     787             :  * arrays and objects whose type can be fixed.
     788             :  */
     789             : static inline TypeSet::Type
     790       16081 : GetValueTypeForTable(const Value& v)
     791             : {
     792       16081 :     TypeSet::Type type = TypeSet::GetValueType(v);
     793       16081 :     MOZ_ASSERT(!type.isSingleton());
     794       16081 :     return type;
     795             : }
     796             : 
     797             : /* static */ JSObject*
     798        2304 : ObjectGroup::newArrayObject(JSContext* cx,
     799             :                             const Value* vp, size_t length,
     800             :                             NewObjectKind newKind, NewArrayKind arrayKind)
     801             : {
     802        2304 :     MOZ_ASSERT(newKind != SingletonObject);
     803             : 
     804             :     // If we are making a copy on write array, don't try to adjust the group as
     805             :     // getOrFixupCopyOnWriteObject will do this before any objects are copied
     806             :     // from this one.
     807        2304 :     if (arrayKind == NewArrayKind::CopyOnWrite) {
     808        1435 :         ArrayObject* obj = NewDenseCopiedArray(cx, length, vp, nullptr, newKind);
     809        1436 :         if (!obj || !ObjectElements::MakeElementsCopyOnWrite(cx, obj))
     810           0 :             return nullptr;
     811        1436 :         return obj;
     812             :     }
     813             : 
     814             :     // Get a type which captures all the elements in the array to be created.
     815        1738 :     Rooted<TypeSet::Type> elementType(cx, TypeSet::UnknownType());
     816         869 :     if (arrayKind != NewArrayKind::UnknownIndex && length != 0) {
     817          52 :         elementType = GetValueTypeForTable(vp[0]);
     818         145 :         for (unsigned i = 1; i < length; i++) {
     819          97 :             TypeSet::Type ntype = GetValueTypeForTable(vp[i]);
     820          97 :             if (ntype != elementType) {
     821           4 :                 if (NumberTypes(elementType, ntype)) {
     822           0 :                     elementType = TypeSet::DoubleType();
     823             :                 } else {
     824           4 :                     elementType = TypeSet::UnknownType();
     825           4 :                     break;
     826             :                 }
     827             :             }
     828             :         }
     829             :     }
     830             : 
     831             :     ObjectGroupCompartment::ArrayObjectTable*& table =
     832         869 :         cx->compartment()->objectGroups.arrayObjectTable;
     833             : 
     834         869 :     if (!table) {
     835          53 :         table = cx->new_<ObjectGroupCompartment::ArrayObjectTable>();
     836          53 :         if (!table || !table->init()) {
     837           0 :             ReportOutOfMemory(cx);
     838           0 :             js_delete(table);
     839           0 :             table = nullptr;
     840           0 :             return nullptr;
     841             :         }
     842             :     }
     843             : 
     844         869 :     ObjectGroupCompartment::ArrayObjectKey key(elementType);
     845         869 :     DependentAddPtr<ObjectGroupCompartment::ArrayObjectTable> p(cx, *table, key);
     846             : 
     847        1738 :     RootedObjectGroup group(cx);
     848         869 :     if (p) {
     849         811 :         group = p->value();
     850             :     } else {
     851         116 :         RootedObject proto(cx);
     852          58 :         if (!GetBuiltinPrototype(cx, JSProto_Array, &proto))
     853           0 :             return nullptr;
     854         116 :         Rooted<TaggedProto> taggedProto(cx, TaggedProto(proto));
     855          58 :         group = ObjectGroupCompartment::makeGroup(cx, &ArrayObject::class_, taggedProto);
     856          58 :         if (!group)
     857           0 :             return nullptr;
     858             : 
     859          58 :         AddTypePropertyId(cx, group, nullptr, JSID_VOID, elementType);
     860             : 
     861          58 :         if (elementType != TypeSet::UnknownType()) {
     862             :             // Keep track of the initial objects we create with this type.
     863             :             // If the initial ones have a consistent shape and property types, we
     864             :             // will try to use an unboxed layout for the group.
     865             :             PreliminaryObjectArrayWithTemplate* preliminaryObjects =
     866          10 :                 cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
     867          10 :             if (!preliminaryObjects)
     868           0 :                 return nullptr;
     869          10 :             group->setPreliminaryObjects(preliminaryObjects);
     870             :         }
     871             : 
     872          58 :         if (!p.add(cx, *table, ObjectGroupCompartment::ArrayObjectKey(elementType), group))
     873           0 :             return nullptr;
     874             :     }
     875             : 
     876             :     // The type of the elements being added will already be reflected in type
     877             :     // information, but make sure when creating an unboxed array that the
     878             :     // common element type is suitable for the unboxed representation.
     879         869 :     ShouldUpdateTypes updateTypes = ShouldUpdateTypes::DontUpdate;
     880         869 :     if (!MaybeAnalyzeBeforeCreatingLargeArray(cx, group, vp, length))
     881           0 :         return nullptr;
     882         869 :     if (group->maybePreliminaryObjects())
     883          48 :         group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
     884         869 :     if (group->maybeUnboxedLayout()) {
     885           0 :         switch (group->unboxedLayout().elementType()) {
     886             :           case JSVAL_TYPE_BOOLEAN:
     887           0 :             if (elementType != TypeSet::BooleanType())
     888           0 :                 updateTypes = ShouldUpdateTypes::Update;
     889           0 :             break;
     890             :           case JSVAL_TYPE_INT32:
     891           0 :             if (elementType != TypeSet::Int32Type())
     892           0 :                 updateTypes = ShouldUpdateTypes::Update;
     893           0 :             break;
     894             :           case JSVAL_TYPE_DOUBLE:
     895           0 :             if (elementType != TypeSet::Int32Type() && elementType != TypeSet::DoubleType())
     896           0 :                 updateTypes = ShouldUpdateTypes::Update;
     897           0 :             break;
     898             :           case JSVAL_TYPE_STRING:
     899           0 :             if (elementType != TypeSet::StringType())
     900           0 :                 updateTypes = ShouldUpdateTypes::Update;
     901           0 :             break;
     902             :           case JSVAL_TYPE_OBJECT:
     903           0 :             if (elementType != TypeSet::NullType() && !elementType.get().isObjectUnchecked())
     904           0 :                 updateTypes = ShouldUpdateTypes::Update;
     905           0 :             break;
     906             :           default:
     907           0 :             MOZ_CRASH();
     908             :         }
     909             :     }
     910             : 
     911         869 :     return NewCopiedArrayTryUseGroup(cx, group, vp, length, newKind, updateTypes);
     912             : }
     913             : 
     914             : // Try to change the group of |source| to match that of |target|.
     915             : static bool
     916           0 : GiveObjectGroup(JSContext* cx, JSObject* source, JSObject* target)
     917             : {
     918           0 :     MOZ_ASSERT(source->group() != target->group());
     919             : 
     920           0 :     if (!target->is<ArrayObject>() && !target->is<UnboxedArrayObject>())
     921           0 :         return true;
     922             : 
     923           0 :     if (target->group()->maybePreliminaryObjects()) {
     924           0 :         bool force = IsInsideNursery(source);
     925           0 :         target->group()->maybePreliminaryObjects()->maybeAnalyze(cx, target->group(), force);
     926             :     }
     927             : 
     928           0 :     if (target->is<ArrayObject>()) {
     929           0 :         ObjectGroup* sourceGroup = source->group();
     930             : 
     931           0 :         if (source->is<UnboxedArrayObject>()) {
     932           0 :             Shape* shape = target->as<ArrayObject>().lastProperty();
     933           0 :             if (!UnboxedArrayObject::convertToNativeWithGroup(cx, source, target->group(), shape))
     934           0 :                 return false;
     935           0 :         } else if (source->is<ArrayObject>()) {
     936           0 :             source->setGroup(target->group());
     937             :         } else {
     938           0 :             return true;
     939             :         }
     940             : 
     941           0 :         if (sourceGroup->maybePreliminaryObjects())
     942           0 :             sourceGroup->maybePreliminaryObjects()->unregisterObject(source);
     943           0 :         if (target->group()->maybePreliminaryObjects())
     944           0 :             target->group()->maybePreliminaryObjects()->registerNewObject(source);
     945             : 
     946           0 :         for (size_t i = 0; i < source->as<ArrayObject>().getDenseInitializedLength(); i++) {
     947           0 :             Value v = source->as<ArrayObject>().getDenseElement(i);
     948           0 :             AddTypePropertyId(cx, source->group(), source, JSID_VOID, v);
     949             :         }
     950             : 
     951           0 :         return true;
     952             :     }
     953             : 
     954           0 :     if (target->is<UnboxedArrayObject>()) {
     955           0 :         if (!source->is<UnboxedArrayObject>())
     956           0 :             return true;
     957           0 :         if (source->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_INT32)
     958           0 :             return true;
     959           0 :         if (target->as<UnboxedArrayObject>().elementType() != JSVAL_TYPE_DOUBLE)
     960           0 :             return true;
     961             : 
     962           0 :         return source->as<UnboxedArrayObject>().convertInt32ToDouble(cx, target->group());
     963             :     }
     964             : 
     965           0 :     return true;
     966             : }
     967             : 
     968             : static bool
     969          19 : SameGroup(JSObject* first, JSObject* second)
     970             : {
     971          19 :     return first->group() == second->group();
     972             : }
     973             : 
     974             : // When generating a multidimensional array of literals, such as
     975             : // [[1,2],[3,4],[5.5,6.5]], try to ensure that each element of the array has
     976             : // the same group. This is mainly important when the elements might have
     977             : // different native vs. unboxed layouts, or different unboxed layouts, and
     978             : // accessing the heterogenous layouts from JIT code will be much slower than
     979             : // if they were homogenous.
     980             : //
     981             : // To do this, with each new array element we compare it with one of the
     982             : // previous ones, and try to mutate the group of the new element to fit that
     983             : // of the old element. If this isn't possible, the groups for all old elements
     984             : // are mutated to fit that of the new element.
     985             : bool
     986         483 : js::CombineArrayElementTypes(JSContext* cx, JSObject* newObj,
     987             :                              const Value* compare, size_t ncompare)
     988             : {
     989         483 :     if (!ncompare || !compare[0].isObject())
     990         464 :         return true;
     991             : 
     992          19 :     JSObject* oldObj = &compare[0].toObject();
     993          19 :     if (SameGroup(oldObj, newObj))
     994          19 :         return true;
     995             : 
     996           0 :     if (!GiveObjectGroup(cx, newObj, oldObj))
     997           0 :         return false;
     998             : 
     999           0 :     if (SameGroup(oldObj, newObj))
    1000           0 :         return true;
    1001             : 
    1002           0 :     if (!GiveObjectGroup(cx, oldObj, newObj))
    1003           0 :         return false;
    1004             : 
    1005           0 :     if (SameGroup(oldObj, newObj)) {
    1006           0 :         for (size_t i = 1; i < ncompare; i++) {
    1007           0 :             if (compare[i].isObject() && !SameGroup(&compare[i].toObject(), newObj)) {
    1008           0 :                 if (!GiveObjectGroup(cx, &compare[i].toObject(), newObj))
    1009           0 :                     return false;
    1010             :             }
    1011             :         }
    1012             :     }
    1013             : 
    1014           0 :     return true;
    1015             : }
    1016             : 
    1017             : // Similarly to CombineArrayElementTypes, if we are generating an array of
    1018             : // plain objects with a consistent property layout, such as
    1019             : // [{p:[1,2]},{p:[3,4]},{p:[5.5,6.5]}], where those plain objects in
    1020             : // turn have arrays as their own properties, try to ensure that a consistent
    1021             : // group is given to each array held by the same property of the plain objects.
    1022             : bool
    1023         736 : js::CombinePlainObjectPropertyTypes(JSContext* cx, JSObject* newObj,
    1024             :                                     const Value* compare, size_t ncompare)
    1025             : {
    1026         736 :     if (!ncompare || !compare[0].isObject())
    1027         736 :         return true;
    1028             : 
    1029           0 :     JSObject* oldObj = &compare[0].toObject();
    1030           0 :     if (!SameGroup(oldObj, newObj))
    1031           0 :         return true;
    1032             : 
    1033           0 :     if (newObj->is<PlainObject>()) {
    1034           0 :         if (newObj->as<PlainObject>().lastProperty() != oldObj->as<PlainObject>().lastProperty())
    1035           0 :             return true;
    1036             : 
    1037           0 :         for (size_t slot = 0; slot < newObj->as<PlainObject>().slotSpan(); slot++) {
    1038           0 :             Value newValue = newObj->as<PlainObject>().getSlot(slot);
    1039           0 :             Value oldValue = oldObj->as<PlainObject>().getSlot(slot);
    1040             : 
    1041           0 :             if (!newValue.isObject() || !oldValue.isObject())
    1042           0 :                 continue;
    1043             : 
    1044           0 :             JSObject* newInnerObj = &newValue.toObject();
    1045           0 :             JSObject* oldInnerObj = &oldValue.toObject();
    1046             : 
    1047           0 :             if (SameGroup(oldInnerObj, newInnerObj))
    1048           0 :                 continue;
    1049             : 
    1050           0 :             if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
    1051           0 :                 return false;
    1052             : 
    1053           0 :             if (SameGroup(oldInnerObj, newInnerObj))
    1054           0 :                 continue;
    1055             : 
    1056           0 :             if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
    1057           0 :                 return false;
    1058             : 
    1059           0 :             if (SameGroup(oldInnerObj, newInnerObj)) {
    1060           0 :                 for (size_t i = 1; i < ncompare; i++) {
    1061           0 :                     if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
    1062           0 :                         Value otherValue = compare[i].toObject().as<PlainObject>().getSlot(slot);
    1063           0 :                         if (otherValue.isObject() && !SameGroup(&otherValue.toObject(), newInnerObj)) {
    1064           0 :                             if (!GiveObjectGroup(cx, &otherValue.toObject(), newInnerObj))
    1065           0 :                                 return false;
    1066             :                         }
    1067             :                     }
    1068             :                 }
    1069             :             }
    1070             :         }
    1071           0 :     } else if (newObj->is<UnboxedPlainObject>()) {
    1072           0 :         const UnboxedLayout& layout = newObj->as<UnboxedPlainObject>().layout();
    1073           0 :         const int32_t* traceList = layout.traceList();
    1074           0 :         if (!traceList)
    1075           0 :             return true;
    1076             : 
    1077           0 :         uint8_t* newData = newObj->as<UnboxedPlainObject>().data();
    1078           0 :         uint8_t* oldData = oldObj->as<UnboxedPlainObject>().data();
    1079             : 
    1080           0 :         for (; *traceList != -1; traceList++) {}
    1081           0 :         traceList++;
    1082           0 :         for (; *traceList != -1; traceList++) {
    1083           0 :             JSObject* newInnerObj = *reinterpret_cast<JSObject**>(newData + *traceList);
    1084           0 :             JSObject* oldInnerObj = *reinterpret_cast<JSObject**>(oldData + *traceList);
    1085             : 
    1086           0 :             if (!newInnerObj || !oldInnerObj || SameGroup(oldInnerObj, newInnerObj))
    1087           0 :                 continue;
    1088             : 
    1089           0 :             if (!GiveObjectGroup(cx, newInnerObj, oldInnerObj))
    1090           0 :                 return false;
    1091             : 
    1092           0 :             if (SameGroup(oldInnerObj, newInnerObj))
    1093           0 :                 continue;
    1094             : 
    1095           0 :             if (!GiveObjectGroup(cx, oldInnerObj, newInnerObj))
    1096           0 :                 return false;
    1097             : 
    1098           0 :             if (SameGroup(oldInnerObj, newInnerObj)) {
    1099           0 :                 for (size_t i = 1; i < ncompare; i++) {
    1100           0 :                     if (compare[i].isObject() && SameGroup(&compare[i].toObject(), newObj)) {
    1101           0 :                         uint8_t* otherData = compare[i].toObject().as<UnboxedPlainObject>().data();
    1102           0 :                         JSObject* otherInnerObj = *reinterpret_cast<JSObject**>(otherData + *traceList);
    1103           0 :                         if (otherInnerObj && !SameGroup(otherInnerObj, newInnerObj)) {
    1104           0 :                             if (!GiveObjectGroup(cx, otherInnerObj, newInnerObj))
    1105           0 :                                 return false;
    1106             :                         }
    1107             :                     }
    1108             :                 }
    1109             :             }
    1110             :         }
    1111             :     }
    1112             : 
    1113           0 :     return true;
    1114             : }
    1115             : 
    1116             : /////////////////////////////////////////////////////////////////////
    1117             : // ObjectGroupCompartment PlainObjectTable
    1118             : /////////////////////////////////////////////////////////////////////
    1119             : 
    1120             : struct ObjectGroupCompartment::PlainObjectKey
    1121             : {
    1122             :     jsid* properties;
    1123             :     uint32_t nproperties;
    1124             : 
    1125             :     struct Lookup {
    1126             :         IdValuePair* properties;
    1127             :         uint32_t nproperties;
    1128             : 
    1129        4922 :         Lookup(IdValuePair* properties, uint32_t nproperties)
    1130        4922 :           : properties(properties), nproperties(nproperties)
    1131        4922 :         {}
    1132             :     };
    1133             : 
    1134        7799 :     static inline HashNumber hash(const Lookup& lookup) {
    1135        7799 :         return (HashNumber) (HashId(lookup.properties[lookup.nproperties - 1].id) ^
    1136        7799 :                              lookup.nproperties);
    1137             :     }
    1138             : 
    1139        5183 :     static inline bool match(const PlainObjectKey& v, const Lookup& lookup) {
    1140        5183 :         if (lookup.nproperties != v.nproperties)
    1141           0 :             return false;
    1142       21177 :         for (size_t i = 0; i < lookup.nproperties; i++) {
    1143       16255 :             if (lookup.properties[i].id != v.properties[i])
    1144         261 :                 return false;
    1145             :         }
    1146        4922 :         return true;
    1147             :     }
    1148             : 
    1149           0 :     bool needsSweep() {
    1150           0 :         for (unsigned i = 0; i < nproperties; i++) {
    1151           0 :             if (gc::IsAboutToBeFinalizedUnbarriered(&properties[i]))
    1152           0 :                 return true;
    1153             :         }
    1154           0 :         return false;
    1155             :     }
    1156             : };
    1157             : 
    1158       14260 : struct ObjectGroupCompartment::PlainObjectEntry
    1159             : {
    1160             :     ReadBarrieredObjectGroup group;
    1161             :     ReadBarrieredShape shape;
    1162             :     TypeSet::Type* types;
    1163             : 
    1164           0 :     bool needsSweep(unsigned nproperties) {
    1165           0 :         if (IsAboutToBeFinalized(&group))
    1166           0 :             return true;
    1167           0 :         if (IsAboutToBeFinalized(&shape))
    1168           0 :             return true;
    1169           0 :         for (unsigned i = 0; i < nproperties; i++) {
    1170           0 :             MOZ_ASSERT(!types[i].isSingleton());
    1171           0 :             if (types[i].isGroup()) {
    1172           0 :                 ObjectGroup* group = types[i].groupNoBarrier();
    1173           0 :                 if (IsAboutToBeFinalizedUnbarriered(&group))
    1174           0 :                     return true;
    1175           0 :                 if (group != types[i].groupNoBarrier())
    1176           0 :                     types[i] = TypeSet::ObjectType(group);
    1177             :             }
    1178             :         }
    1179           0 :         return false;
    1180             :     }
    1181             : };
    1182             : 
    1183             : static bool
    1184        2877 : CanShareObjectGroup(IdValuePair* properties, size_t nproperties)
    1185             : {
    1186             :     // Don't reuse groups for objects containing indexed properties, which
    1187             :     // might end up as dense elements.
    1188       14470 :     for (size_t i = 0; i < nproperties; i++) {
    1189             :         uint32_t index;
    1190       11593 :         if (IdIsIndex(properties[i].id, &index))
    1191           0 :             return false;
    1192             :     }
    1193        2877 :     return true;
    1194             : }
    1195             : 
    1196             : static bool
    1197        3944 : AddPlainObjectProperties(JSContext* cx, HandlePlainObject obj,
    1198             :                          IdValuePair* properties, size_t nproperties)
    1199             : {
    1200        7888 :     RootedId propid(cx);
    1201        7888 :     RootedValue value(cx);
    1202             : 
    1203       16794 :     for (size_t i = 0; i < nproperties; i++) {
    1204       12850 :         propid = properties[i].id;
    1205       12850 :         value = properties[i].value;
    1206       12850 :         if (!NativeDefineProperty(cx, obj, propid, value, nullptr, nullptr, JSPROP_ENUMERATE))
    1207           0 :             return false;
    1208             :     }
    1209             : 
    1210        3944 :     return true;
    1211             : }
    1212             : 
    1213             : PlainObject*
    1214        1067 : js::NewPlainObjectWithProperties(JSContext* cx, IdValuePair* properties, size_t nproperties,
    1215             :                                  NewObjectKind newKind)
    1216             : {
    1217        1067 :     gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
    1218        2134 :     RootedPlainObject obj(cx, NewBuiltinClassInstance<PlainObject>(cx, allocKind, newKind));
    1219        1067 :     if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
    1220           0 :         return nullptr;
    1221        1067 :     return obj;
    1222             : }
    1223             : 
    1224             : /* static */ JSObject*
    1225        5989 : ObjectGroup::newPlainObject(JSContext* cx, IdValuePair* properties, size_t nproperties,
    1226             :                             NewObjectKind newKind)
    1227             : {
    1228             :     // Watch for simple cases where we don't try to reuse plain object groups.
    1229        5989 :     if (newKind == SingletonObject || nproperties == 0 || nproperties >= PropertyTree::MAX_HEIGHT)
    1230        1067 :         return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
    1231             : 
    1232             :     ObjectGroupCompartment::PlainObjectTable*& table =
    1233        4922 :         cx->compartment()->objectGroups.plainObjectTable;
    1234             : 
    1235        4922 :     if (!table) {
    1236         202 :         table = cx->new_<ObjectGroupCompartment::PlainObjectTable>();
    1237         202 :         if (!table || !table->init()) {
    1238           0 :             ReportOutOfMemory(cx);
    1239           0 :             js_delete(table);
    1240           0 :             table = nullptr;
    1241           0 :             return nullptr;
    1242             :         }
    1243             :     }
    1244             : 
    1245        4922 :     ObjectGroupCompartment::PlainObjectKey::Lookup lookup(properties, nproperties);
    1246        4922 :     ObjectGroupCompartment::PlainObjectTable::Ptr p = table->lookup(lookup);
    1247             : 
    1248        4922 :     if (!p) {
    1249        2877 :         if (!CanShareObjectGroup(properties, nproperties))
    1250           0 :             return NewPlainObjectWithProperties(cx, properties, nproperties, newKind);
    1251             : 
    1252        5754 :         RootedObject proto(cx);
    1253        2877 :         if (!GetBuiltinPrototype(cx, JSProto_Object, &proto))
    1254           0 :             return nullptr;
    1255             : 
    1256        5754 :         Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
    1257        5754 :         RootedObjectGroup group(cx, ObjectGroupCompartment::makeGroup(cx, &PlainObject::class_,
    1258        5754 :                                                                       tagged));
    1259        2877 :         if (!group)
    1260           0 :             return nullptr;
    1261             : 
    1262        2877 :         gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
    1263        5754 :         RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group,
    1264        5754 :                                                                   allocKind, TenuredObject));
    1265        2877 :         if (!obj || !AddPlainObjectProperties(cx, obj, properties, nproperties))
    1266           0 :             return nullptr;
    1267             : 
    1268             :         // Don't make entries with duplicate property names, which will show up
    1269             :         // here as objects with fewer properties than we thought we were
    1270             :         // adding to the object. In this case, reset the object's group to the
    1271             :         // default (which will have unknown properties) so that the group we
    1272             :         // just created will be collected by the GC.
    1273        2877 :         if (obj->slotSpan() != nproperties) {
    1274           0 :             ObjectGroup* group = defaultNewGroup(cx, obj->getClass(), obj->taggedProto());
    1275           0 :             if (!group)
    1276           0 :                 return nullptr;
    1277           0 :             obj->setGroup(group);
    1278           0 :             return obj;
    1279             :         }
    1280             : 
    1281             :         // Keep track of the initial objects we create with this type.
    1282             :         // If the initial ones have a consistent shape and property types, we
    1283             :         // will try to use an unboxed layout for the group.
    1284             :         PreliminaryObjectArrayWithTemplate* preliminaryObjects =
    1285        2877 :             cx->new_<PreliminaryObjectArrayWithTemplate>(obj->lastProperty());
    1286        2877 :         if (!preliminaryObjects)
    1287           0 :             return nullptr;
    1288        2877 :         group->setPreliminaryObjects(preliminaryObjects);
    1289        2877 :         preliminaryObjects->registerNewObject(obj);
    1290             : 
    1291        5754 :         ScopedJSFreePtr<jsid> ids(group->zone()->pod_calloc<jsid>(nproperties));
    1292        2877 :         if (!ids) {
    1293           0 :             ReportOutOfMemory(cx);
    1294           0 :             return nullptr;
    1295             :         }
    1296             : 
    1297             :         ScopedJSFreePtr<TypeSet::Type> types(
    1298        5754 :             group->zone()->pod_calloc<TypeSet::Type>(nproperties));
    1299        2877 :         if (!types) {
    1300           0 :             ReportOutOfMemory(cx);
    1301           0 :             return nullptr;
    1302             :         }
    1303             : 
    1304       14470 :         for (size_t i = 0; i < nproperties; i++) {
    1305       11593 :             ids[i] = properties[i].id;
    1306       11593 :             types[i] = GetValueTypeForTable(obj->getSlot(i));
    1307       11592 :             AddTypePropertyId(cx, group, nullptr, IdToTypeId(ids[i]), types[i]);
    1308             :         }
    1309             : 
    1310             :         ObjectGroupCompartment::PlainObjectKey key;
    1311        2877 :         key.properties = ids;
    1312        2877 :         key.nproperties = nproperties;
    1313        2877 :         MOZ_ASSERT(ObjectGroupCompartment::PlainObjectKey::match(key, lookup));
    1314             : 
    1315        5754 :         ObjectGroupCompartment::PlainObjectEntry entry;
    1316        2877 :         entry.group.set(group);
    1317        2877 :         entry.shape.set(obj->lastProperty());
    1318        2877 :         entry.types = types;
    1319             : 
    1320        2877 :         ObjectGroupCompartment::PlainObjectTable::AddPtr np = table->lookupForAdd(lookup);
    1321        2877 :         if (!table->add(np, key, entry)) {
    1322           0 :             ReportOutOfMemory(cx);
    1323           0 :             return nullptr;
    1324             :         }
    1325             : 
    1326        2877 :         ids.forget();
    1327        2877 :         types.forget();
    1328             : 
    1329        2877 :         return obj;
    1330             :     }
    1331             : 
    1332        4090 :     RootedObjectGroup group(cx, p->value().group);
    1333             : 
    1334             :     // Watch for existing groups which now use an unboxed layout.
    1335        2045 :     if (group->maybeUnboxedLayout()) {
    1336           0 :         MOZ_ASSERT(group->unboxedLayout().properties().length() == nproperties);
    1337           0 :         return UnboxedPlainObject::createWithProperties(cx, group, newKind, properties);
    1338             :     }
    1339             : 
    1340             :     // Update property types according to the properties we are about to add.
    1341             :     // Do this before we do anything which can GC, which might move or remove
    1342             :     // this table entry.
    1343        2045 :     if (!group->unknownProperties()) {
    1344        6385 :         for (size_t i = 0; i < nproperties; i++) {
    1345        4340 :             TypeSet::Type type = p->value().types[i];
    1346        4340 :             TypeSet::Type ntype = GetValueTypeForTable(properties[i].value);
    1347        4340 :             if (ntype == type)
    1348        4334 :                 continue;
    1349           8 :             if (ntype.isPrimitive(JSVAL_TYPE_INT32) &&
    1350           2 :                 type.isPrimitive(JSVAL_TYPE_DOUBLE))
    1351             :             {
    1352             :                 // The property types already reflect 'int32'.
    1353             :             } else {
    1354           6 :                 if (ntype.isPrimitive(JSVAL_TYPE_DOUBLE) &&
    1355           0 :                     type.isPrimitive(JSVAL_TYPE_INT32))
    1356             :                 {
    1357             :                     // Include 'double' in the property types to avoid the update below later.
    1358           0 :                     p->value().types[i] = TypeSet::DoubleType();
    1359             :                 }
    1360           6 :                 AddTypePropertyId(cx, group, nullptr, IdToTypeId(properties[i].id), ntype);
    1361             :             }
    1362             :         }
    1363             :     }
    1364             : 
    1365        4090 :     RootedShape shape(cx, p->value().shape);
    1366             : 
    1367        2045 :     if (group->maybePreliminaryObjects())
    1368        1362 :         newKind = TenuredObject;
    1369             : 
    1370        2045 :     gc::AllocKind allocKind = gc::GetGCObjectKind(nproperties);
    1371        4090 :     RootedPlainObject obj(cx, NewObjectWithGroup<PlainObject>(cx, group, allocKind,
    1372        4090 :                                                               newKind));
    1373             : 
    1374        2045 :     if (!obj || !obj->setLastProperty(cx, shape))
    1375           0 :         return nullptr;
    1376             : 
    1377        6385 :     for (size_t i = 0; i < nproperties; i++)
    1378        4340 :         obj->setSlot(i, properties[i].value);
    1379             : 
    1380        2045 :     if (group->maybePreliminaryObjects()) {
    1381        1362 :         group->maybePreliminaryObjects()->registerNewObject(obj);
    1382        1362 :         group->maybePreliminaryObjects()->maybeAnalyze(cx, group);
    1383             :     }
    1384             : 
    1385        2045 :     return obj;
    1386             : }
    1387             : 
    1388             : /////////////////////////////////////////////////////////////////////
    1389             : // ObjectGroupCompartment AllocationSiteTable
    1390             : /////////////////////////////////////////////////////////////////////
    1391             : 
    1392       16073 : struct ObjectGroupCompartment::AllocationSiteKey : public DefaultHasher<AllocationSiteKey> {
    1393             :     ReadBarrieredScript script;
    1394             : 
    1395             :     uint32_t offset : 24;
    1396             :     JSProtoKey kind : 8;
    1397             : 
    1398             :     ReadBarrieredObject proto;
    1399             : 
    1400             :     static const uint32_t OFFSET_LIMIT = (1 << 23);
    1401             : 
    1402        5035 :     AllocationSiteKey(JSScript* script_, uint32_t offset_, JSProtoKey kind_, JSObject* proto_)
    1403        5035 :       : script(script_), offset(offset_), kind(kind_), proto(proto_)
    1404             :     {
    1405        5035 :         MOZ_ASSERT(offset_ < OFFSET_LIMIT);
    1406        5035 :     }
    1407             : 
    1408        7106 :     AllocationSiteKey(const AllocationSiteKey& key)
    1409        7106 :       : script(key.script),
    1410        7106 :         offset(key.offset),
    1411        7106 :         kind(key.kind),
    1412       21318 :         proto(key.proto)
    1413        7106 :     { }
    1414             : 
    1415        5991 :     AllocationSiteKey(AllocationSiteKey&& key)
    1416       11982 :       : script(mozilla::Move(key.script)),
    1417        5991 :         offset(key.offset),
    1418        5991 :         kind(key.kind),
    1419       23964 :         proto(mozilla::Move(key.proto))
    1420        5991 :     { }
    1421             : 
    1422           0 :     void operator=(AllocationSiteKey&& key) {
    1423           0 :         script = mozilla::Move(key.script);
    1424           0 :         offset = key.offset;
    1425           0 :         kind = key.kind;
    1426           0 :         proto = mozilla::Move(key.proto);
    1427           0 :     }
    1428             : 
    1429        5043 :     static inline uint32_t hash(AllocationSiteKey key) {
    1430        5043 :         return uint32_t(size_t(key.script.unbarrieredGet()->offsetToPC(key.offset)) ^ key.kind ^
    1431        5043 :                         MovableCellHasher<JSObject*>::hash(key.proto.unbarrieredGet()));
    1432             :     }
    1433             : 
    1434        2989 :     static inline bool match(const AllocationSiteKey& a, const AllocationSiteKey& b) {
    1435        2989 :         return DefaultHasher<JSScript*>::match(a.script.unbarrieredGet(),
    1436        5969 :                                                b.script.unbarrieredGet()) &&
    1437        5956 :                a.offset == b.offset &&
    1438        8941 :                a.kind == b.kind &&
    1439        5965 :                MovableCellHasher<JSObject*>::match(a.proto, b.proto);
    1440             :     }
    1441             : 
    1442           0 :     void trace(JSTracer* trc) {
    1443           0 :         TraceRoot(trc, &script, "AllocationSiteKey script");
    1444           0 :         TraceNullableRoot(trc, &proto, "AllocationSiteKey proto");
    1445           0 :     }
    1446             : 
    1447           0 :     bool needsSweep() {
    1448           0 :         return IsAboutToBeFinalizedUnbarriered(script.unsafeGet()) ||
    1449           0 :             (proto && IsAboutToBeFinalizedUnbarriered(proto.unsafeGet()));
    1450             :     }
    1451             : 
    1452           0 :     bool operator==(const AllocationSiteKey& other) const {
    1453           0 :         return script == other.script &&
    1454           0 :                offset == other.offset &&
    1455           0 :                kind == other.kind &&
    1456           0 :                proto == other.proto;
    1457             :     }
    1458             : };
    1459             : 
    1460           0 : class ObjectGroupCompartment::AllocationSiteTable
    1461             :   : public JS::WeakCache<js::GCHashMap<AllocationSiteKey, ReadBarrieredObjectGroup,
    1462             :                                        AllocationSiteKey, SystemAllocPolicy>>
    1463             : {
    1464             :     using Table = js::GCHashMap<AllocationSiteKey, ReadBarrieredObjectGroup,
    1465             :                                 AllocationSiteKey, SystemAllocPolicy>;
    1466             :     using Base = JS::WeakCache<Table>;
    1467             : 
    1468             :   public:
    1469         272 :     explicit AllocationSiteTable(Zone* zone) : Base(zone) {}
    1470             : };
    1471             : 
    1472             : /* static */ ObjectGroup*
    1473        5031 : ObjectGroup::allocationSiteGroup(JSContext* cx, JSScript* scriptArg, jsbytecode* pc,
    1474             :                                  JSProtoKey kind, HandleObject protoArg /* = nullptr */)
    1475             : {
    1476        5031 :     MOZ_ASSERT(!useSingletonForAllocationSite(scriptArg, pc, kind));
    1477        5031 :     MOZ_ASSERT_IF(protoArg, kind == JSProto_Array);
    1478             : 
    1479        5031 :     uint32_t offset = scriptArg->pcToOffset(pc);
    1480             : 
    1481        5031 :     if (offset >= ObjectGroupCompartment::AllocationSiteKey::OFFSET_LIMIT) {
    1482           0 :         if (protoArg)
    1483           0 :             return defaultNewGroup(cx, GetClassForProtoKey(kind), TaggedProto(protoArg));
    1484           0 :         return defaultNewGroup(cx, kind);
    1485             :     }
    1486             : 
    1487             :     ObjectGroupCompartment::AllocationSiteTable*& table =
    1488        5031 :         cx->compartment()->objectGroups.allocationSiteTable;
    1489             : 
    1490        5031 :     if (!table) {
    1491         272 :         table = cx->new_<ObjectGroupCompartment::AllocationSiteTable>(cx->zone());
    1492         272 :         if (!table || !table->init()) {
    1493           0 :             ReportOutOfMemory(cx);
    1494           0 :             js_delete(table);
    1495           0 :             table = nullptr;
    1496           0 :             return nullptr;
    1497             :         }
    1498             :     }
    1499             : 
    1500       10062 :     RootedScript script(cx, scriptArg);
    1501       10062 :     RootedObject proto(cx, protoArg);
    1502        5031 :     if (!proto && kind != JSProto_Null && !GetBuiltinPrototype(cx, kind, &proto))
    1503           0 :         return nullptr;
    1504             : 
    1505             :     Rooted<ObjectGroupCompartment::AllocationSiteKey> key(cx,
    1506       10062 :         ObjectGroupCompartment::AllocationSiteKey(script, offset, kind, proto));
    1507             : 
    1508        5031 :     ObjectGroupCompartment::AllocationSiteTable::AddPtr p = table->lookupForAdd(key);
    1509        5031 :     if (p)
    1510        2972 :         return p->value();
    1511             : 
    1512        4118 :     AutoEnterAnalysis enter(cx);
    1513             : 
    1514        4118 :     Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
    1515        4118 :     ObjectGroup* res = ObjectGroupCompartment::makeGroup(cx, GetClassForProtoKey(kind), tagged,
    1516        2059 :                                                          OBJECT_FLAG_FROM_ALLOCATION_SITE);
    1517        2059 :     if (!res)
    1518           0 :         return nullptr;
    1519             : 
    1520        2059 :     if (JSOp(*pc) == JSOP_NEWOBJECT) {
    1521             :         // Keep track of the preliminary objects with this group, so we can try
    1522             :         // to use an unboxed layout for the object once some are allocated.
    1523         546 :         Shape* shape = script->getObject(pc)->as<PlainObject>().lastProperty();
    1524         546 :         if (!shape->isEmptyShape()) {
    1525             :             PreliminaryObjectArrayWithTemplate* preliminaryObjects =
    1526         391 :                 cx->new_<PreliminaryObjectArrayWithTemplate>(shape);
    1527         391 :             if (preliminaryObjects)
    1528         391 :                 res->setPreliminaryObjects(preliminaryObjects);
    1529             :             else
    1530           0 :                 cx->recoverFromOutOfMemory();
    1531             :         }
    1532             :     }
    1533             : 
    1534        3530 :     if (kind == JSProto_Array &&
    1535        3560 :         (JSOp(*pc) == JSOP_NEWARRAY || IsCallPC(pc)) &&
    1536         698 :         cx->options().unboxedArrays())
    1537             :     {
    1538             :         PreliminaryObjectArrayWithTemplate* preliminaryObjects =
    1539           0 :             cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
    1540           0 :         if (preliminaryObjects)
    1541           0 :             res->setPreliminaryObjects(preliminaryObjects);
    1542             :         else
    1543           0 :             cx->recoverFromOutOfMemory();
    1544             :     }
    1545             : 
    1546        2059 :     if (!table->add(p, key, res)) {
    1547           0 :         ReportOutOfMemory(cx);
    1548           0 :         return nullptr;
    1549             :     }
    1550             : 
    1551        2059 :     return res;
    1552             : }
    1553             : 
    1554             : void
    1555           4 : ObjectGroupCompartment::replaceAllocationSiteGroup(JSScript* script, jsbytecode* pc,
    1556             :                                                    JSProtoKey kind, ObjectGroup* group)
    1557             : {
    1558           8 :     AllocationSiteKey key(script, script->pcToOffset(pc), kind, group->proto().toObjectOrNull());
    1559             : 
    1560           4 :     AllocationSiteTable::Ptr p = allocationSiteTable->lookup(key);
    1561           4 :     MOZ_RELEASE_ASSERT(p);
    1562           4 :     allocationSiteTable->remove(p);
    1563             :     {
    1564           8 :         AutoEnterOOMUnsafeRegion oomUnsafe;
    1565           4 :         if (!allocationSiteTable->putNew(key, group))
    1566           0 :             oomUnsafe.crash("Inconsistent object table");
    1567             :     }
    1568           4 : }
    1569             : 
    1570             : /* static */ ObjectGroup*
    1571         260 : ObjectGroup::callingAllocationSiteGroup(JSContext* cx, JSProtoKey key, HandleObject proto)
    1572             : {
    1573         260 :     MOZ_ASSERT_IF(proto, key == JSProto_Array);
    1574             : 
    1575             :     jsbytecode* pc;
    1576         520 :     RootedScript script(cx, cx->currentScript(&pc));
    1577         260 :     if (script)
    1578         260 :         return allocationSiteGroup(cx, script, pc, key, proto);
    1579           0 :     if (proto)
    1580           0 :         return defaultNewGroup(cx, GetClassForProtoKey(key), TaggedProto(proto));
    1581           0 :     return defaultNewGroup(cx, key);
    1582             : }
    1583             : 
    1584             : /* static */ bool
    1585           0 : ObjectGroup::setAllocationSiteObjectGroup(JSContext* cx,
    1586             :                                           HandleScript script, jsbytecode* pc,
    1587             :                                           HandleObject obj, bool singleton)
    1588             : {
    1589           0 :     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(obj->getClass());
    1590           0 :     MOZ_ASSERT(key != JSProto_Null);
    1591           0 :     MOZ_ASSERT(singleton == useSingletonForAllocationSite(script, pc, key));
    1592             : 
    1593           0 :     if (singleton) {
    1594           0 :         MOZ_ASSERT(obj->isSingleton());
    1595             : 
    1596             :         /*
    1597             :          * Inference does not account for types of run-once initializer
    1598             :          * objects, as these may not be created until after the script
    1599             :          * has been analyzed.
    1600             :          */
    1601           0 :         TypeScript::Monitor(cx, script, pc, ObjectValue(*obj));
    1602             :     } else {
    1603           0 :         ObjectGroup* group = allocationSiteGroup(cx, script, pc, key);
    1604           0 :         if (!group)
    1605           0 :             return false;
    1606           0 :         obj->setGroup(group);
    1607             :     }
    1608             : 
    1609           0 :     return true;
    1610             : }
    1611             : 
    1612             : /* static */ ArrayObject*
    1613         784 : ObjectGroup::getOrFixupCopyOnWriteObject(JSContext* cx, HandleScript script, jsbytecode* pc)
    1614             : {
    1615             :     // Make sure that the template object for script/pc has a type indicating
    1616             :     // that the object and its copies have copy on write elements.
    1617        1568 :     RootedArrayObject obj(cx, &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>());
    1618         784 :     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
    1619             : 
    1620         784 :     if (obj->group()->fromAllocationSite()) {
    1621          43 :         MOZ_ASSERT(obj->group()->hasAnyFlags(OBJECT_FLAG_COPY_ON_WRITE));
    1622          43 :         return obj;
    1623             :     }
    1624             : 
    1625        1482 :     RootedObjectGroup group(cx, allocationSiteGroup(cx, script, pc, JSProto_Array));
    1626         741 :     if (!group)
    1627           0 :         return nullptr;
    1628             : 
    1629         741 :     group->addFlags(OBJECT_FLAG_COPY_ON_WRITE);
    1630             : 
    1631             :     // Update type information in the initializer object group.
    1632         741 :     MOZ_ASSERT(obj->slotSpan() == 0);
    1633        3191 :     for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
    1634        2450 :         const Value& v = obj->getDenseElement(i);
    1635        2450 :         AddTypePropertyId(cx, group, nullptr, JSID_VOID, v);
    1636             :     }
    1637             : 
    1638         741 :     obj->setGroup(group);
    1639         741 :     return obj;
    1640             : }
    1641             : 
    1642             : /* static */ ArrayObject*
    1643           0 : ObjectGroup::getCopyOnWriteObject(JSScript* script, jsbytecode* pc)
    1644             : {
    1645             :     // getOrFixupCopyOnWriteObject should already have been called for
    1646             :     // script/pc, ensuring that the template object has a group with the
    1647             :     // COPY_ON_WRITE flag. We don't assert this here, due to a corner case
    1648             :     // where this property doesn't hold. See jsop_newarray_copyonwrite in
    1649             :     // IonBuilder.
    1650           0 :     ArrayObject* obj = &script->getObject(GET_UINT32_INDEX(pc))->as<ArrayObject>();
    1651           0 :     MOZ_ASSERT(obj->denseElementsAreCopyOnWrite());
    1652             : 
    1653           0 :     return obj;
    1654             : }
    1655             : 
    1656             : /* static */ bool
    1657           0 : ObjectGroup::findAllocationSite(JSContext* cx, ObjectGroup* group,
    1658             :                                 JSScript** script, uint32_t* offset)
    1659             : {
    1660           0 :     *script = nullptr;
    1661           0 :     *offset = 0;
    1662             : 
    1663             :     const ObjectGroupCompartment::AllocationSiteTable* table =
    1664           0 :         cx->compartment()->objectGroups.allocationSiteTable;
    1665             : 
    1666           0 :     if (!table)
    1667           0 :         return false;
    1668             : 
    1669           0 :     for (ObjectGroupCompartment::AllocationSiteTable::Range r = table->all();
    1670           0 :          !r.empty();
    1671           0 :          r.popFront())
    1672             :     {
    1673           0 :         if (group == r.front().value()) {
    1674           0 :             *script = r.front().key().script;
    1675           0 :             *offset = r.front().key().offset;
    1676           0 :             return true;
    1677             :         }
    1678             :     }
    1679             : 
    1680           0 :     return false;
    1681             : }
    1682             : 
    1683             : /////////////////////////////////////////////////////////////////////
    1684             : // ObjectGroupCompartment
    1685             : /////////////////////////////////////////////////////////////////////
    1686             : 
    1687         315 : ObjectGroupCompartment::ObjectGroupCompartment()
    1688             : {
    1689         315 :     PodZero(this);
    1690         315 : }
    1691             : 
    1692           0 : ObjectGroupCompartment::~ObjectGroupCompartment()
    1693             : {
    1694           0 :     js_delete(defaultNewTable);
    1695           0 :     js_delete(lazyTable);
    1696           0 :     js_delete(arrayObjectTable);
    1697           0 :     js_delete(plainObjectTable);
    1698           0 :     js_delete(allocationSiteTable);
    1699           0 :     stringSplitStringGroup = nullptr;
    1700           0 : }
    1701             : 
    1702             : void
    1703           0 : ObjectGroupCompartment::removeDefaultNewGroup(const Class* clasp, TaggedProto proto,
    1704             :                                               JSObject* associated)
    1705             : {
    1706           0 :     auto p = defaultNewTable->lookup(NewEntry::Lookup(clasp, proto, associated));
    1707           0 :     MOZ_RELEASE_ASSERT(p);
    1708             : 
    1709           0 :     defaultNewTable->remove(p);
    1710           0 :     defaultNewGroupCache.purge();
    1711           0 : }
    1712             : 
    1713             : void
    1714           1 : ObjectGroupCompartment::replaceDefaultNewGroup(const Class* clasp, TaggedProto proto,
    1715             :                                                JSObject* associated, ObjectGroup* group)
    1716             : {
    1717           1 :     NewEntry::Lookup lookup(clasp, proto, associated);
    1718             : 
    1719           1 :     auto p = defaultNewTable->lookup(lookup);
    1720           1 :     MOZ_RELEASE_ASSERT(p);
    1721           1 :     defaultNewTable->remove(p);
    1722           1 :     defaultNewGroupCache.purge();
    1723             :     {
    1724           2 :         AutoEnterOOMUnsafeRegion oomUnsafe;
    1725           1 :         if (!defaultNewTable->putNew(lookup, NewEntry(group, associated)))
    1726           0 :             oomUnsafe.crash("Inconsistent object table");
    1727             :     }
    1728           1 : }
    1729             : 
    1730             : /* static */
    1731             : ObjectGroup*
    1732       43831 : ObjectGroupCompartment::makeGroup(JSContext* cx, const Class* clasp,
    1733             :                                   Handle<TaggedProto> proto,
    1734             :                                   ObjectGroupFlags initialFlags /* = 0 */)
    1735             : {
    1736       43831 :     MOZ_ASSERT_IF(proto.isObject(), cx->isInsideCurrentCompartment(proto.toObject()));
    1737             : 
    1738       43831 :     ObjectGroup* group = Allocate<ObjectGroup>(cx);
    1739       43831 :     if (!group)
    1740           0 :         return nullptr;
    1741       43831 :     new(group) ObjectGroup(clasp, proto, cx->compartment(), initialFlags);
    1742             : 
    1743       43831 :     return group;
    1744             : }
    1745             : 
    1746             : /* static */
    1747             : ObjectGroup*
    1748          69 : ObjectGroupCompartment::getStringSplitStringGroup(JSContext* cx)
    1749             : {
    1750          69 :     ObjectGroupCompartment& groups = cx->compartment()->objectGroups;
    1751             : 
    1752          69 :     ObjectGroup* group = groups.stringSplitStringGroup.get();
    1753          69 :     if (group) {
    1754          57 :         return group;
    1755             :     }
    1756             : 
    1757             :     // The following code is a specialized version of the code
    1758             :     // for ObjectGroup::allocationSiteGroup().
    1759             : 
    1760          12 :     const Class* clasp = GetClassForProtoKey(JSProto_Array);
    1761             : 
    1762          24 :     RootedObject proto(cx);
    1763          12 :     if (!GetBuiltinPrototype(cx, JSProto_Array, &proto))
    1764           0 :         return nullptr;
    1765          24 :     Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
    1766             : 
    1767          12 :     group = makeGroup(cx, clasp, tagged, /* initialFlags = */ 0);
    1768          12 :     if (!group)
    1769           0 :         return nullptr;
    1770             : 
    1771          12 :     if (cx->options().unboxedArrays()) {
    1772             :         PreliminaryObjectArrayWithTemplate* preliminaryObjects =
    1773           0 :             cx->new_<PreliminaryObjectArrayWithTemplate>(nullptr);
    1774           0 :         if (preliminaryObjects)
    1775           0 :             group->setPreliminaryObjects(preliminaryObjects);
    1776             :         else
    1777           0 :             cx->recoverFromOutOfMemory();
    1778             :     }
    1779             : 
    1780          12 :     groups.stringSplitStringGroup.set(group);
    1781          12 :     return group;
    1782             : }
    1783             : 
    1784             : void
    1785           0 : ObjectGroupCompartment::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
    1786             :                                                size_t* allocationSiteTables,
    1787             :                                                size_t* arrayObjectGroupTables,
    1788             :                                                size_t* plainObjectGroupTables,
    1789             :                                                size_t* compartmentTables)
    1790             : {
    1791           0 :     if (allocationSiteTable)
    1792           0 :         *allocationSiteTables += allocationSiteTable->sizeOfIncludingThis(mallocSizeOf);
    1793             : 
    1794           0 :     if (arrayObjectTable)
    1795           0 :         *arrayObjectGroupTables += arrayObjectTable->sizeOfIncludingThis(mallocSizeOf);
    1796             : 
    1797           0 :     if (plainObjectTable) {
    1798           0 :         *plainObjectGroupTables += plainObjectTable->sizeOfIncludingThis(mallocSizeOf);
    1799             : 
    1800           0 :         for (PlainObjectTable::Enum e(*plainObjectTable);
    1801           0 :              !e.empty();
    1802           0 :              e.popFront())
    1803             :         {
    1804           0 :             const PlainObjectKey& key = e.front().key();
    1805           0 :             const PlainObjectEntry& value = e.front().value();
    1806             : 
    1807             :             /* key.ids and values.types have the same length. */
    1808           0 :             *plainObjectGroupTables += mallocSizeOf(key.properties) + mallocSizeOf(value.types);
    1809             :         }
    1810             :     }
    1811             : 
    1812           0 :     if (defaultNewTable)
    1813           0 :         *compartmentTables += defaultNewTable->sizeOfIncludingThis(mallocSizeOf);
    1814             : 
    1815           0 :     if (lazyTable)
    1816           0 :         *compartmentTables += lazyTable->sizeOfIncludingThis(mallocSizeOf);
    1817           0 : }
    1818             : 
    1819             : void
    1820          15 : ObjectGroupCompartment::clearTables()
    1821             : {
    1822          15 :     if (allocationSiteTable && allocationSiteTable->initialized())
    1823           0 :         allocationSiteTable->clear();
    1824          15 :     if (arrayObjectTable && arrayObjectTable->initialized())
    1825           1 :         arrayObjectTable->clear();
    1826          15 :     if (plainObjectTable && plainObjectTable->initialized()) {
    1827         940 :         for (PlainObjectTable::Enum e(*plainObjectTable); !e.empty(); e.popFront()) {
    1828         925 :             const PlainObjectKey& key = e.front().key();
    1829         925 :             PlainObjectEntry& entry = e.front().value();
    1830         925 :             js_free(key.properties);
    1831         925 :             js_free(entry.types);
    1832             :         }
    1833          15 :         plainObjectTable->clear();
    1834             :     }
    1835          15 :     if (defaultNewTable && defaultNewTable->initialized())
    1836          15 :         defaultNewTable->clear();
    1837          15 :     if (lazyTable && lazyTable->initialized())
    1838          15 :         lazyTable->clear();
    1839          15 :     defaultNewGroupCache.purge();
    1840          15 : }
    1841             : 
    1842             : /* static */ bool
    1843           0 : ObjectGroupCompartment::PlainObjectTableSweepPolicy::needsSweep(PlainObjectKey* key,
    1844             :                                                                 PlainObjectEntry* entry)
    1845             : {
    1846           0 :     if (!(JS::GCPolicy<PlainObjectKey>::needsSweep(key) || entry->needsSweep(key->nproperties)))
    1847           0 :         return false;
    1848           0 :     js_free(key->properties);
    1849           0 :     js_free(entry->types);
    1850           0 :     return true;
    1851             : }
    1852             : 
    1853             : void
    1854           0 : ObjectGroupCompartment::sweep(FreeOp* fop)
    1855             : {
    1856             :     /*
    1857             :      * Iterate through the array/object group tables and remove all entries
    1858             :      * referencing collected data. These tables only hold weak references.
    1859             :      */
    1860             : 
    1861           0 :     if (arrayObjectTable)
    1862           0 :         arrayObjectTable->sweep();
    1863           0 :     if (plainObjectTable)
    1864           0 :         plainObjectTable->sweep();
    1865           0 :     if (stringSplitStringGroup) {
    1866           0 :         if (JS::GCPolicy<ReadBarrieredObjectGroup>::needsSweep(&stringSplitStringGroup)) {
    1867           0 :             stringSplitStringGroup = nullptr;
    1868             :         }
    1869             :     }
    1870           0 : }
    1871             : 
    1872             : void
    1873           0 : ObjectGroupCompartment::fixupNewTableAfterMovingGC(NewTable* table)
    1874             : {
    1875             :     /*
    1876             :      * Each entry's hash depends on the object's prototype and we can't tell
    1877             :      * whether that has been moved or not in sweepNewObjectGroupTable().
    1878             :      */
    1879           0 :     if (table && table->initialized()) {
    1880           0 :         for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
    1881           0 :             NewEntry& entry = e.mutableFront();
    1882             : 
    1883           0 :             ObjectGroup* group = entry.group.unbarrieredGet();
    1884           0 :             if (IsForwarded(group)) {
    1885           0 :                 group = Forwarded(group);
    1886           0 :                 entry.group.set(group);
    1887             :             }
    1888           0 :             TaggedProto proto = group->proto();
    1889           0 :             if (proto.isObject() && IsForwarded(proto.toObject())) {
    1890           0 :                 proto = TaggedProto(Forwarded(proto.toObject()));
    1891             :                 // Update the group's proto here so that we are able to lookup
    1892             :                 // entries in this table before all object pointers are updated.
    1893           0 :                 group->proto() = proto;
    1894             :             }
    1895           0 :             if (entry.associated && IsForwarded(entry.associated))
    1896           0 :                 entry.associated = Forwarded(entry.associated);
    1897             :         }
    1898             :     }
    1899           0 : }
    1900             : 
    1901             : #ifdef JSGC_HASH_TABLE_CHECKS
    1902             : 
    1903             : void
    1904           0 : ObjectGroupCompartment::checkNewTableAfterMovingGC(NewTable* table)
    1905             : {
    1906             :     /*
    1907             :      * Assert that nothing points into the nursery or needs to be relocated, and
    1908             :      * that the hash table entries are discoverable.
    1909             :      */
    1910           0 :     if (!table || !table->initialized())
    1911           0 :         return;
    1912             : 
    1913           0 :     for (auto r = table->all(); !r.empty(); r.popFront()) {
    1914           0 :         NewEntry entry = r.front();
    1915           0 :         CheckGCThingAfterMovingGC(entry.group.unbarrieredGet());
    1916           0 :         TaggedProto proto = entry.group.unbarrieredGet()->proto();
    1917           0 :         if (proto.isObject())
    1918           0 :             CheckGCThingAfterMovingGC(proto.toObject());
    1919           0 :         CheckGCThingAfterMovingGC(entry.associated);
    1920             : 
    1921           0 :         auto ptr = table->lookup(NewEntry::Lookup(entry));
    1922           0 :         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &r.front());
    1923             :     }
    1924             : }
    1925             : 
    1926             : #endif // JSGC_HASH_TABLE_CHECKS

Generated by: LCOV version 1.13