LCOV - code coverage report
Current view: top level - js/src/vm - Shape.h (source / functions) Hit Total Coverage
Test: output.info Lines: 444 511 86.9 %
Date: 2017-07-14 16:53:18 Functions: 174 203 85.7 %
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             : #ifndef vm_Shape_h
       8             : #define vm_Shape_h
       9             : 
      10             : #include "mozilla/Attributes.h"
      11             : #include "mozilla/GuardObjects.h"
      12             : #include "mozilla/MathAlgorithms.h"
      13             : #include "mozilla/Maybe.h"
      14             : #include "mozilla/MemoryReporting.h"
      15             : #include "mozilla/TemplateLib.h"
      16             : 
      17             : #include "jsapi.h"
      18             : #include "jsatom.h"
      19             : #include "jsfriendapi.h"
      20             : #include "jstypes.h"
      21             : #include "NamespaceImports.h"
      22             : 
      23             : #include "gc/Barrier.h"
      24             : #include "gc/Heap.h"
      25             : #include "gc/Marking.h"
      26             : #include "gc/Rooting.h"
      27             : #include "js/HashTable.h"
      28             : #include "js/MemoryMetrics.h"
      29             : #include "js/RootingAPI.h"
      30             : #include "js/UbiNode.h"
      31             : #include "vm/ObjectGroup.h"
      32             : #include "vm/String.h"
      33             : #include "vm/Symbol.h"
      34             : 
      35             : #ifdef _MSC_VER
      36             : #pragma warning(push)
      37             : #pragma warning(disable:4800)
      38             : #pragma warning(push)
      39             : #pragma warning(disable:4100) /* Silence unreferenced formal parameter warnings */
      40             : #endif
      41             : 
      42             : /*
      43             :  * In isolation, a Shape represents a property that exists in one or more
      44             :  * objects; it has an id, flags, etc. (But it doesn't represent the property's
      45             :  * value.)  However, Shapes are always stored in linked linear sequence of
      46             :  * Shapes, called "shape lineages". Each shape lineage represents the layout of
      47             :  * an entire object.
      48             :  *
      49             :  * Every JSObject has a pointer, |shape_|, accessible via lastProperty(), to
      50             :  * the last Shape in a shape lineage, which identifies the property most
      51             :  * recently added to the object.  This pointer permits fast object layout
      52             :  * tests. The shape lineage order also dictates the enumeration order for the
      53             :  * object; ECMA requires no particular order but this implementation has
      54             :  * promised and delivered property definition order.
      55             :  *
      56             :  * Shape lineages occur in two kinds of data structure.
      57             :  *
      58             :  * 1. N-ary property trees. Each path from a non-root node to the root node in
      59             :  *    a property tree is a shape lineage. Property trees permit full (or
      60             :  *    partial) sharing of Shapes between objects that have fully (or partly)
      61             :  *    identical layouts. The root is an EmptyShape whose identity is determined
      62             :  *    by the object's class, compartment and prototype. These Shapes are shared
      63             :  *    and immutable.
      64             :  *
      65             :  * 2. Dictionary mode lists. Shapes in such lists are said to be "in
      66             :  *    dictionary mode", as are objects that point to such Shapes. These Shapes
      67             :  *    are unshared, private to a single object, and immutable except for their
      68             :  *    links in the dictionary list.
      69             :  *
      70             :  * All shape lineages are bi-directionally linked, via the |parent| and
      71             :  * |kids|/|listp| members.
      72             :  *
      73             :  * Shape lineages start out life in the property tree. They can be converted
      74             :  * (by copying) to dictionary mode lists in the following circumstances.
      75             :  *
      76             :  * 1. The shape lineage's size reaches MAX_HEIGHT. This reasonable limit avoids
      77             :  *    potential worst cases involving shape lineage mutations.
      78             :  *
      79             :  * 2. A property represented by a non-last Shape in a shape lineage is removed
      80             :  *    from an object. (In the last Shape case, obj->shape_ can be easily
      81             :  *    adjusted to point to obj->shape_->parent.)  We originally tried lazy
      82             :  *    forking of the property tree, but this blows up for delete/add
      83             :  *    repetitions.
      84             :  *
      85             :  * 3. A property represented by a non-last Shape in a shape lineage has its
      86             :  *    attributes modified.
      87             :  *
      88             :  * To find the Shape for a particular property of an object initially requires
      89             :  * a linear search. But if the number of searches starting at any particular
      90             :  * Shape in the property tree exceeds LINEAR_SEARCHES_MAX and the Shape's
      91             :  * lineage has (excluding the EmptyShape) at least MIN_ENTRIES, we create an
      92             :  * auxiliary hash table -- the ShapeTable -- that allows faster lookup.
      93             :  * Furthermore, a ShapeTable is always created for dictionary mode lists,
      94             :  * and it is attached to the last Shape in the lineage. Shape tables for
      95             :  * property tree Shapes never change, but shape tables for dictionary mode
      96             :  * Shapes can grow and shrink.
      97             :  *
      98             :  * To save memory, shape tables can be discarded on GC and recreated when
      99             :  * needed. AutoKeepShapeTables can be used to avoid discarding shape tables
     100             :  * for a particular zone. Methods operating on ShapeTables take either an
     101             :  * AutoCheckCannotGC or AutoKeepShapeTables argument, to help ensure tables
     102             :  * are not purged while we're using them.
     103             :  *
     104             :  * There used to be a long, math-heavy comment here explaining why property
     105             :  * trees are more space-efficient than alternatives.  This was removed in bug
     106             :  * 631138; see that bug for the full details.
     107             :  *
     108             :  * For getters/setters, an AccessorShape is allocated. This is a slightly fatter
     109             :  * type with extra fields for the getter/setter data.
     110             :  *
     111             :  * Because many Shapes have similar data, there is actually a secondary type
     112             :  * called a BaseShape that holds some of a Shape's data.  Many shapes can share
     113             :  * a single BaseShape.
     114             :  */
     115             : 
     116             : MOZ_ALWAYS_INLINE size_t
     117     3824753 : JSSLOT_FREE(const js::Class* clasp)
     118             : {
     119             :     // Proxy classes have reserved slots, but proxies manage their own slot
     120             :     // layout.
     121     3824753 :     MOZ_ASSERT(!clasp->isProxy());
     122     3824761 :     return JSCLASS_RESERVED_SLOTS(clasp);
     123             : }
     124             : 
     125             : namespace js {
     126             : 
     127             : class Shape;
     128             : struct StackShape;
     129             : 
     130             : struct ShapeHasher : public DefaultHasher<Shape*> {
     131             :     typedef Shape* Key;
     132             :     typedef StackShape Lookup;
     133             : 
     134             :     static MOZ_ALWAYS_INLINE HashNumber hash(const Lookup& l);
     135             :     static MOZ_ALWAYS_INLINE bool match(Key k, const Lookup& l);
     136             : };
     137             : 
     138             : typedef HashSet<Shape*, ShapeHasher, SystemAllocPolicy> KidsHash;
     139             : 
     140             : class KidsPointer {
     141             :   private:
     142             :     enum {
     143             :         SHAPE = 0,
     144             :         HASH  = 1,
     145             :         TAG   = 1
     146             :     };
     147             : 
     148             :     uintptr_t w;
     149             : 
     150             :   public:
     151      269003 :     bool isNull() const { return !w; }
     152      106036 :     void setNull() { w = 0; }
     153             : 
     154      225742 :     bool isShape() const { return (w & TAG) == SHAPE && !isNull(); }
     155       67125 :     Shape* toShape() const {
     156       67125 :         MOZ_ASSERT(isShape());
     157       67125 :         return reinterpret_cast<Shape*>(w & ~uintptr_t(TAG));
     158             :     }
     159       62163 :     void setShape(Shape* shape) {
     160       62163 :         MOZ_ASSERT(shape);
     161       62163 :         MOZ_ASSERT((reinterpret_cast<uintptr_t>(static_cast<Shape*>(shape)) & TAG) == 0);
     162       62163 :         w = reinterpret_cast<uintptr_t>(static_cast<Shape*>(shape)) | SHAPE;
     163       62163 :     }
     164             : 
     165      117637 :     bool isHash() const { return (w & TAG) == HASH; }
     166       30277 :     KidsHash* toHash() const {
     167       30277 :         MOZ_ASSERT(isHash());
     168       30277 :         return reinterpret_cast<KidsHash*>(w & ~uintptr_t(TAG));
     169             :     }
     170        2095 :     void setHash(KidsHash* hash) {
     171        2095 :         MOZ_ASSERT(hash);
     172        2095 :         MOZ_ASSERT((reinterpret_cast<uintptr_t>(hash) & TAG) == 0);
     173        2095 :         w = reinterpret_cast<uintptr_t>(hash) | HASH;
     174        2095 :     }
     175             : 
     176             : #ifdef DEBUG
     177             :     void checkConsistency(Shape* aKid) const;
     178             : #endif
     179             : };
     180             : 
     181             : class PropertyTree
     182             : {
     183             :     friend class ::JSFunction;
     184             : 
     185             : #ifdef DEBUG
     186             :     JS::Zone* zone_;
     187             : #endif
     188             : 
     189             :     bool insertChild(JSContext* cx, Shape* parent, Shape* child);
     190             : 
     191             :     PropertyTree();
     192             : 
     193             :   public:
     194             :     /*
     195             :      * Use a lower limit for objects that are accessed using SETELEM (o[x] = y).
     196             :      * These objects are likely used as hashmaps and dictionary mode is more
     197             :      * efficient in this case.
     198             :      */
     199             :     enum {
     200             :         MAX_HEIGHT = 512,
     201             :         MAX_HEIGHT_WITH_ELEMENTS_ACCESS = 128
     202             :     };
     203             : 
     204          31 :     explicit PropertyTree(JS::Zone* zone)
     205             : #ifdef DEBUG
     206          31 :       : zone_(zone)
     207             : #endif
     208             :     {
     209          31 :     }
     210             : 
     211             :     MOZ_ALWAYS_INLINE Shape* inlinedGetChild(JSContext* cx, Shape* parent,
     212             :                                              JS::Handle<StackShape> child);
     213             :     Shape* getChild(JSContext* cx, Shape* parent, JS::Handle<StackShape> child);
     214             : };
     215             : 
     216             : class TenuringTracer;
     217             : 
     218             : typedef JSGetterOp GetterOp;
     219             : typedef JSSetterOp SetterOp;
     220             : 
     221             : /* Limit on the number of slotful properties in an object. */
     222             : static const uint32_t SHAPE_INVALID_SLOT = JS_BIT(24) - 1;
     223             : static const uint32_t SHAPE_MAXIMUM_SLOT = JS_BIT(24) - 2;
     224             : 
     225             : enum class MaybeAdding { Adding = true, NotAdding = false };
     226             : 
     227             : class AutoKeepShapeTables;
     228             : 
     229             : /*
     230             :  * Shapes use multiplicative hashing, but specialized to
     231             :  * minimize footprint.
     232             :  */
     233             : class ShapeTable {
     234             :   public:
     235             :     friend class NativeObject;
     236             :     friend class BaseShape;
     237             :     static const uint32_t MIN_ENTRIES   = 11;
     238             : 
     239             :     class Entry {
     240             :         // js::Shape pointer tag bit indicating a collision.
     241             :         static const uintptr_t SHAPE_COLLISION = 1;
     242             :         static Shape* const SHAPE_REMOVED; // = SHAPE_COLLISION
     243             : 
     244             :         Shape* shape_;
     245             : 
     246             :         Entry() = delete;
     247             :         Entry(const Entry&) = delete;
     248             :         Entry& operator=(const Entry&) = delete;
     249             : 
     250             :       public:
     251      537179 :         bool isFree() const { return shape_ == nullptr; }
     252      257621 :         bool isRemoved() const { return shape_ == SHAPE_REMOVED; }
     253       10531 :         bool isLive() const { return !isFree() && !isRemoved(); }
     254      320657 :         bool hadCollision() const { return uintptr_t(shape_) & SHAPE_COLLISION; }
     255             : 
     256         242 :         void setFree() { shape_ = nullptr; }
     257         139 :         void setRemoved() { shape_ = SHAPE_REMOVED; }
     258             : 
     259      611171 :         Shape* shape() const {
     260      611171 :             return reinterpret_cast<Shape*>(uintptr_t(shape_) & ~SHAPE_COLLISION);
     261             :         }
     262             : 
     263        2564 :         void setShape(Shape* shape) {
     264        2564 :             MOZ_ASSERT(isFree());
     265        2564 :             MOZ_ASSERT(shape);
     266        2564 :             MOZ_ASSERT(shape != SHAPE_REMOVED);
     267        2564 :             shape_ = shape;
     268        2564 :             MOZ_ASSERT(!hadCollision());
     269        2564 :         }
     270             : 
     271       22914 :         void flagCollision() {
     272       22914 :             shape_ = reinterpret_cast<Shape*>(uintptr_t(shape_) | SHAPE_COLLISION);
     273       22914 :         }
     274       71769 :         void setPreservingCollision(Shape* shape) {
     275       71769 :             shape_ = reinterpret_cast<Shape*>(uintptr_t(shape) | uintptr_t(hadCollision()));
     276       71769 :         }
     277             :     };
     278             : 
     279             :   private:
     280             :     static const uint32_t HASH_BITS     = mozilla::tl::BitSize<HashNumber>::value;
     281             : 
     282             :     // This value is low because it's common for a ShapeTable to be created
     283             :     // with an entryCount of zero.
     284             :     static const uint32_t MIN_SIZE_LOG2 = 2;
     285             :     static const uint32_t MIN_SIZE      = JS_BIT(MIN_SIZE_LOG2);
     286             : 
     287             :     uint32_t        hashShift_;         /* multiplicative hash shift */
     288             : 
     289             :     uint32_t        entryCount_;        /* number of entries in table */
     290             :     uint32_t        removedCount_;      /* removed entry sentinels in table */
     291             : 
     292             :     uint32_t        freeList_;          /* SHAPE_INVALID_SLOT or head of slot
     293             :                                            freelist in owning dictionary-mode
     294             :                                            object */
     295             : 
     296             :     Entry*          entries_;          /* table of ptrs to shared tree nodes */
     297             : 
     298             :     template<MaybeAdding Adding>
     299             :     MOZ_ALWAYS_INLINE Entry& searchUnchecked(jsid id);
     300             : 
     301             :   public:
     302        2716 :     explicit ShapeTable(uint32_t nentries)
     303        2716 :       : hashShift_(HASH_BITS - MIN_SIZE_LOG2),
     304             :         entryCount_(nentries),
     305             :         removedCount_(0),
     306             :         freeList_(SHAPE_INVALID_SLOT),
     307        2716 :         entries_(nullptr)
     308             :     {
     309             :         /* NB: entries is set by init, which must be called. */
     310        2716 :     }
     311             : 
     312           0 :     ~ShapeTable() {
     313           0 :         js_free(entries_);
     314           0 :     }
     315             : 
     316       18283 :     uint32_t entryCount() const { return entryCount_; }
     317             : 
     318        1989 :     uint32_t freeList() const { return freeList_; }
     319          54 :     void setFreeList(uint32_t slot) { freeList_ = slot; }
     320             : 
     321             :     /*
     322             :      * This counts the ShapeTable object itself (which must be
     323             :      * heap-allocated) and its |entries| array.
     324             :      */
     325           0 :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     326           0 :         return mallocSizeOf(this) + mallocSizeOf(entries_);
     327             :     }
     328             : 
     329             :     // init() is fallible and reports OOM to the context.
     330             :     bool init(JSContext* cx, Shape* lastProp);
     331             : 
     332             :     // change() is fallible but does not report OOM.
     333             :     bool change(JSContext* cx, int log2Delta);
     334             : 
     335             :     template<MaybeAdding Adding>
     336             :     MOZ_ALWAYS_INLINE Entry& search(jsid id, const AutoKeepShapeTables&);
     337             : 
     338             :     template<MaybeAdding Adding>
     339             :     MOZ_ALWAYS_INLINE Entry& search(jsid id, const JS::AutoCheckCannotGC&);
     340             : 
     341             :     void trace(JSTracer* trc);
     342             : #ifdef JSGC_HASH_TABLE_CHECKS
     343             :     void checkAfterMovingGC();
     344             : #endif
     345             : 
     346             :   private:
     347      521520 :     Entry& getEntry(uint32_t i) const {
     348      521520 :         MOZ_ASSERT(i < capacity());
     349      521520 :         return entries_[i];
     350             :     }
     351         381 :     void decEntryCount() {
     352         381 :         MOZ_ASSERT(entryCount_ > 0);
     353         381 :         entryCount_--;
     354         381 :     }
     355        2665 :     void incEntryCount() {
     356        2665 :         entryCount_++;
     357        2665 :         MOZ_ASSERT(entryCount_ + removedCount_ <= capacity());
     358        2665 :     }
     359         139 :     void incRemovedCount() {
     360         139 :         removedCount_++;
     361         139 :         MOZ_ASSERT(entryCount_ + removedCount_ <= capacity());
     362         139 :     }
     363             : 
     364             :     /* By definition, hashShift = HASH_BITS - log2(capacity). */
     365      532984 :     uint32_t capacity() const { return JS_BIT(HASH_BITS - hashShift_); }
     366             : 
     367             :     /* Whether we need to grow.  We want to do this if the load factor is >= 0.75 */
     368        5436 :     bool needsToGrow() const {
     369        5436 :         uint32_t size = capacity();
     370        5436 :         return entryCount_ + removedCount_ >= size - (size >> 2);
     371             :     }
     372             : 
     373             :     /*
     374             :      * Try to grow the table.  On failure, reports out of memory on cx
     375             :      * and returns false.  This will make any extant pointers into the
     376             :      * table invalid.  Don't call this unless needsToGrow() is true.
     377             :      */
     378             :     bool grow(JSContext* cx);
     379             : };
     380             : 
     381             : // Ensures no shape tables are purged in the current zone.
     382             : class MOZ_RAII AutoKeepShapeTables
     383             : {
     384             :     JSContext* cx_;
     385             :     bool prev_;
     386             : 
     387             :     AutoKeepShapeTables(const AutoKeepShapeTables&) = delete;
     388             :     void operator=(const AutoKeepShapeTables&) = delete;
     389             : 
     390             :   public:
     391             :     explicit inline AutoKeepShapeTables(JSContext* cx);
     392             :     inline ~AutoKeepShapeTables();
     393             : };
     394             : 
     395             : /*
     396             :  * Use the reserved attribute bit to mean shadowability.
     397             :  */
     398             : #define JSPROP_SHADOWABLE       JSPROP_INTERNAL_USE_BIT
     399             : 
     400             : /*
     401             :  * Shapes encode information about both a property lineage *and* a particular
     402             :  * property. This information is split across the Shape and the BaseShape
     403             :  * at shape->base(). Both Shape and BaseShape can be either owned or unowned
     404             :  * by, respectively, the Object or Shape referring to them.
     405             :  *
     406             :  * Owned Shapes are used in dictionary objects, and form a doubly linked list
     407             :  * whose entries are all owned by that dictionary. Unowned Shapes are all in
     408             :  * the property tree.
     409             :  *
     410             :  * Owned BaseShapes are used for shapes which have shape tables, including the
     411             :  * last properties in all dictionaries. Unowned BaseShapes compactly store
     412             :  * information common to many shapes. In a given zone there is a single
     413             :  * BaseShape for each combination of BaseShape information. This information is
     414             :  * cloned in owned BaseShapes so that information can be quickly looked up for a
     415             :  * given object or shape without regard to whether the base shape is owned or
     416             :  * not.
     417             :  *
     418             :  * All combinations of owned/unowned Shapes/BaseShapes are possible:
     419             :  *
     420             :  * Owned Shape, Owned BaseShape:
     421             :  *
     422             :  *     Last property in a dictionary object. The BaseShape is transferred from
     423             :  *     property to property as the object's last property changes.
     424             :  *
     425             :  * Owned Shape, Unowned BaseShape:
     426             :  *
     427             :  *     Property in a dictionary object other than the last one.
     428             :  *
     429             :  * Unowned Shape, Owned BaseShape:
     430             :  *
     431             :  *     Property in the property tree which has a shape table.
     432             :  *
     433             :  * Unowned Shape, Unowned BaseShape:
     434             :  *
     435             :  *     Property in the property tree which does not have a shape table.
     436             :  *
     437             :  * BaseShapes additionally encode some information about the referring object
     438             :  * itself. This includes the object's class and various flags that may be set
     439             :  * for the object. Except for the class, this information is mutable and may
     440             :  * change when the object has an established property lineage. On such changes
     441             :  * the entire property lineage is not updated, but rather only the last property
     442             :  * (and its base shape). This works because only the object's last property is
     443             :  * used to query information about the object. Care must be taken to call
     444             :  * JSObject::canRemoveLastProperty when unwinding an object to an earlier
     445             :  * property, however.
     446             :  */
     447             : 
     448             : class AccessorShape;
     449             : class Shape;
     450             : class UnownedBaseShape;
     451             : struct StackBaseShape;
     452             : 
     453             : class BaseShape : public gc::TenuredCell
     454             : {
     455             :   public:
     456             :     friend class Shape;
     457             :     friend struct StackBaseShape;
     458             :     friend struct StackShape;
     459             :     friend void gc::MergeCompartments(JSCompartment* source, JSCompartment* target);
     460             : 
     461             :     enum Flag {
     462             :         /* Owned by the referring shape. */
     463             :         OWNED_SHAPE        = 0x1,
     464             : 
     465             :         /* (0x2 and 0x4 are unused) */
     466             : 
     467             :         /*
     468             :          * Flags set which describe the referring object. Once set these cannot
     469             :          * be unset (except during object densification of sparse indexes), and
     470             :          * are transferred from shape to shape as the object's last property
     471             :          * changes.
     472             :          *
     473             :          * If you add a new flag here, please add appropriate code to
     474             :          * JSObject::dump to dump it as part of object representation.
     475             :          */
     476             : 
     477             :         DELEGATE            =    0x8,
     478             :         NOT_EXTENSIBLE      =   0x10,
     479             :         INDEXED             =   0x20,
     480             :         HAS_INTERESTING_SYMBOL = 0x40,
     481             :         HAD_ELEMENTS_ACCESS =   0x80,
     482             :         WATCHED             =  0x100,
     483             :         ITERATED_SINGLETON  =  0x200,
     484             :         NEW_GROUP_UNKNOWN   =  0x400,
     485             :         UNCACHEABLE_PROTO   =  0x800,
     486             :         IMMUTABLE_PROTOTYPE = 0x1000,
     487             : 
     488             :         // See JSObject::isQualifiedVarObj().
     489             :         QUALIFIED_VAROBJ    = 0x2000,
     490             : 
     491             :         // 0x4000 is unused.
     492             : 
     493             :         // For a function used as an interpreted constructor, whether a 'new'
     494             :         // type had constructor information cleared.
     495             :         NEW_SCRIPT_CLEARED  = 0x8000,
     496             : 
     497             :         OBJECT_FLAG_MASK    = 0xfff8
     498             :     };
     499             : 
     500             :   private:
     501             :     const Class*        clasp_;        /* Class of referring object. */
     502             :     uint32_t            flags;          /* Vector of above flags. */
     503             :     uint32_t            slotSpan_;      /* Object slot span for BaseShapes at
     504             :                                          * dictionary last properties. */
     505             : 
     506             :     /* For owned BaseShapes, the canonical unowned BaseShape. */
     507             :     GCPtrUnownedBaseShape unowned_;
     508             : 
     509             :     /* For owned BaseShapes, the shape's shape table. */
     510             :     ShapeTable*      table_;
     511             : 
     512             : #if JS_BITS_PER_WORD == 32
     513             :     // Ensure sizeof(BaseShape) is a multiple of gc::CellAlignBytes.
     514             :     uint32_t padding_;
     515             : #endif
     516             : 
     517             :     BaseShape(const BaseShape& base) = delete;
     518             :     BaseShape& operator=(const BaseShape& other) = delete;
     519             : 
     520             :   public:
     521             :     void finalize(FreeOp* fop);
     522             : 
     523             :     explicit inline BaseShape(const StackBaseShape& base);
     524             : 
     525             :     /* Not defined: BaseShapes must not be stack allocated. */
     526             :     ~BaseShape();
     527             : 
     528           0 :     const Class* clasp() const { return clasp_; }
     529             : 
     530     1432501 :     bool isOwned() const { return !!(flags & OWNED_SHAPE); }
     531             : 
     532             :     static void copyFromUnowned(BaseShape& dest, UnownedBaseShape& src);
     533             :     inline void adoptUnowned(UnownedBaseShape* other);
     534             : 
     535        2716 :     void setOwned(UnownedBaseShape* unowned) {
     536        2716 :         flags |= OWNED_SHAPE;
     537        2716 :         unowned_ = unowned;
     538        2716 :     }
     539             : 
     540      197911 :     uint32_t getObjectFlags() const { return flags & OBJECT_FLAG_MASK; }
     541             : 
     542      600679 :     bool hasTable() const { MOZ_ASSERT_IF(table_, isOwned()); return table_ != nullptr; }
     543        2716 :     void setTable(ShapeTable* table) { MOZ_ASSERT(isOwned()); table_ = table; }
     544             : 
     545        9677 :     ShapeTable* maybeTable(const AutoKeepShapeTables&) const {
     546        9677 :         MOZ_ASSERT_IF(table_, isOwned());
     547        9677 :         return table_;
     548             :     }
     549      751838 :     ShapeTable* maybeTable(const JS::AutoCheckCannotGC&) const {
     550      751838 :         MOZ_ASSERT_IF(table_, isOwned());
     551      751838 :         return table_;
     552             :     }
     553           0 :     void maybePurgeTable() {
     554           0 :         if (table_ && table_->freeList() == SHAPE_INVALID_SLOT) {
     555           0 :             js_delete(table_);
     556           0 :             table_ = nullptr;
     557             :         }
     558           0 :     }
     559             : 
     560      206885 :     uint32_t slotSpan() const { MOZ_ASSERT(isOwned()); return slotSpan_; }
     561       10235 :     void setSlotSpan(uint32_t slotSpan) { MOZ_ASSERT(isOwned()); slotSpan_ = slotSpan; }
     562             : 
     563             :     /*
     564             :      * Lookup base shapes from the zone's baseShapes table, adding if not
     565             :      * already found.
     566             :      */
     567             :     static UnownedBaseShape* getUnowned(JSContext* cx, StackBaseShape& base);
     568             : 
     569             :     /* Get the canonical base shape. */
     570             :     inline UnownedBaseShape* unowned();
     571             : 
     572             :     /* Get the canonical base shape for an owned one. */
     573             :     inline UnownedBaseShape* baseUnowned();
     574             : 
     575             :     /* Get the canonical base shape for an unowned one (i.e. identity). */
     576             :     inline UnownedBaseShape* toUnowned();
     577             : 
     578             :     /* Check that an owned base shape is consistent with its unowned base. */
     579             :     void assertConsistency();
     580             : 
     581             :     /* For JIT usage */
     582             :     static inline size_t offsetOfFlags() { return offsetof(BaseShape, flags); }
     583             : 
     584             :     static const JS::TraceKind TraceKind = JS::TraceKind::BaseShape;
     585             : 
     586             :     void traceChildren(JSTracer* trc);
     587             :     void traceChildrenSkipShapeTable(JSTracer* trc);
     588             : 
     589             : #ifdef DEBUG
     590             :     bool canSkipMarkingShapeTable(Shape* lastShape);
     591             : #endif
     592             : 
     593             :   private:
     594             :     static void staticAsserts() {
     595             :         JS_STATIC_ASSERT(offsetof(BaseShape, clasp_) == offsetof(js::shadow::BaseShape, clasp_));
     596             :         static_assert(sizeof(BaseShape) % gc::CellAlignBytes == 0,
     597             :                       "Things inheriting from gc::Cell must have a size that's "
     598             :                       "a multiple of gc::CellAlignBytes");
     599             :     }
     600             : 
     601             :     void traceShapeTable(JSTracer* trc);
     602             : };
     603             : 
     604             : class UnownedBaseShape : public BaseShape {};
     605             : 
     606             : UnownedBaseShape*
     607      367831 : BaseShape::unowned()
     608             : {
     609      367831 :     return isOwned() ? baseUnowned() : toUnowned();
     610             : }
     611             : 
     612             : UnownedBaseShape*
     613      325863 : BaseShape::toUnowned()
     614             : {
     615      325863 :     MOZ_ASSERT(!isOwned() && !unowned_);
     616      325863 :     return static_cast<UnownedBaseShape*>(this);
     617             : }
     618             : 
     619             : UnownedBaseShape*
     620       66315 : BaseShape::baseUnowned()
     621             : {
     622       66315 :     MOZ_ASSERT(isOwned() && unowned_);
     623       66315 :     return unowned_;
     624             : }
     625             : 
     626             : /* Entries for the per-zone baseShapes set of unowned base shapes. */
     627             : struct StackBaseShape : public DefaultHasher<ReadBarriered<UnownedBaseShape*>>
     628             : {
     629             :     uint32_t flags;
     630             :     const Class* clasp;
     631             : 
     632        1064 :     explicit StackBaseShape(BaseShape* base)
     633        2128 :       : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
     634        2128 :         clasp(base->clasp_)
     635        1064 :     {}
     636             : 
     637             :     inline StackBaseShape(JSContext* cx, const Class* clasp, uint32_t objectFlags);
     638             :     explicit inline StackBaseShape(Shape* shape);
     639             : 
     640             :     struct Lookup
     641             :     {
     642             :         uint32_t flags;
     643             :         const Class* clasp;
     644             : 
     645       17134 :         MOZ_IMPLICIT Lookup(const StackBaseShape& base)
     646       17134 :           : flags(base.flags), clasp(base.clasp)
     647       17134 :         {}
     648             : 
     649           0 :         MOZ_IMPLICIT Lookup(UnownedBaseShape* base)
     650           0 :           : flags(base->getObjectFlags()), clasp(base->clasp())
     651             :         {
     652           0 :             MOZ_ASSERT(!base->isOwned());
     653           0 :         }
     654             : 
     655             :         explicit Lookup(const ReadBarriered<UnownedBaseShape*>& base)
     656             :           : flags(base.unbarrieredGet()->getObjectFlags()), clasp(base.unbarrieredGet()->clasp())
     657             :         {
     658             :             MOZ_ASSERT(!base.unbarrieredGet()->isOwned());
     659             :         }
     660             :     };
     661             : 
     662       17135 :     static HashNumber hash(const Lookup& lookup) {
     663       17135 :         HashNumber hash = lookup.flags;
     664       17135 :         hash = mozilla::RotateLeft(hash, 4) ^ (uintptr_t(lookup.clasp) >> 3);
     665       17135 :         return hash;
     666             :     }
     667       13117 :     static inline bool match(const ReadBarriered<UnownedBaseShape*>& key, const Lookup& lookup) {
     668       26216 :         return key.unbarrieredGet()->flags == lookup.flags &&
     669       26216 :                key.unbarrieredGet()->clasp_ == lookup.clasp;
     670             :     }
     671             : };
     672             : 
     673             : static MOZ_ALWAYS_INLINE js::HashNumber
     674      365741 : HashId(jsid id)
     675             : {
     676             :     // HashGeneric alone would work, but bits of atom and symbol addresses
     677             :     // could then be recovered from the hash code. See bug 1330769.
     678      365741 :     if (MOZ_LIKELY(JSID_IS_ATOM(id)))
     679      356781 :         return JSID_TO_ATOM(id)->hash();
     680        8961 :     if (JSID_IS_SYMBOL(id))
     681        8776 :         return JSID_TO_SYMBOL(id)->hash();
     682         185 :     return mozilla::HashGeneric(JSID_BITS(id));
     683             : }
     684             : 
     685             : template <>
     686             : struct DefaultHasher<jsid>
     687             : {
     688             :     typedef jsid Lookup;
     689        3981 :     static HashNumber hash(jsid id) {
     690        3981 :         return HashId(id);
     691             :     }
     692          21 :     static bool match(jsid id1, jsid id2) {
     693          21 :         return id1 == id2;
     694             :     }
     695             : };
     696             : 
     697             : using BaseShapeSet = JS::WeakCache<JS::GCHashSet<ReadBarriered<UnownedBaseShape*>,
     698             :                                                  StackBaseShape,
     699             :                                                  SystemAllocPolicy>>;
     700             : 
     701             : class Shape : public gc::TenuredCell
     702             : {
     703             :     friend class ::JSObject;
     704             :     friend class ::JSFunction;
     705             :     friend class NativeObject;
     706             :     friend class PropertyTree;
     707             :     friend class TenuringTracer;
     708             :     friend struct StackBaseShape;
     709             :     friend struct StackShape;
     710             :     friend class JS::ubi::Concrete<Shape>;
     711             :     friend class js::gc::RelocationOverlay;
     712             : 
     713             :   protected:
     714             :     GCPtrBaseShape base_;
     715             :     PreBarrieredId propid_;
     716             : 
     717             :     enum SlotInfo : uint32_t
     718             :     {
     719             :         /* Number of fixed slots in objects with this shape. */
     720             :         // FIXED_SLOTS_MAX is the biggest count of fixed slots a Shape can store
     721             :         FIXED_SLOTS_MAX        = 0x1f,
     722             :         FIXED_SLOTS_SHIFT      = 27,
     723             :         FIXED_SLOTS_MASK       = uint32_t(FIXED_SLOTS_MAX << FIXED_SLOTS_SHIFT),
     724             : 
     725             :         /*
     726             :          * numLinearSearches starts at zero and is incremented initially on
     727             :          * search() calls. Once numLinearSearches reaches LINEAR_SEARCHES_MAX,
     728             :          * the table is created on the next search() call. The table can also
     729             :          * be created when hashifying for dictionary mode.
     730             :          */
     731             :         LINEAR_SEARCHES_MAX    = 0x7,
     732             :         LINEAR_SEARCHES_SHIFT  = 24,
     733             :         LINEAR_SEARCHES_MASK   = LINEAR_SEARCHES_MAX << LINEAR_SEARCHES_SHIFT,
     734             : 
     735             :         /*
     736             :          * Mask to get the index in object slots for shapes which hasSlot().
     737             :          * For !hasSlot() shapes in the property tree with a parent, stores the
     738             :          * parent's slot index (which may be invalid), and invalid for all
     739             :          * other shapes.
     740             :          */
     741             :         SLOT_MASK              = JS_BIT(24) - 1
     742             :     };
     743             : 
     744             :     uint32_t            slotInfo;       /* mask of above info */
     745             :     uint8_t             attrs;          /* attributes, see jsapi.h JSPROP_* */
     746             :     uint8_t             flags;          /* flags, see below for defines */
     747             : 
     748             :     GCPtrShape   parent;          /* parent node, reverse for..in order */
     749             :     /* kids is valid when !inDictionary(), listp is valid when inDictionary(). */
     750             :     union {
     751             :         KidsPointer kids;         /* null, single child, or a tagged ptr
     752             :                                      to many-kids data structure */
     753             :         GCPtrShape* listp;        /* dictionary list starting at shape_
     754             :                                      has a double-indirect back pointer,
     755             :                                      either to the next shape's parent if not
     756             :                                      last, else to obj->shape_ */
     757             :     };
     758             : 
     759             :     template<MaybeAdding Adding = MaybeAdding::NotAdding>
     760             :     static MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, Shape* start, jsid id);
     761             : 
     762             :     template<MaybeAdding Adding = MaybeAdding::NotAdding>
     763             :     static inline MOZ_MUST_USE bool search(JSContext* cx, Shape* start, jsid id,
     764             :                                            const AutoKeepShapeTables&,
     765             :                                            Shape** pshape, ShapeTable::Entry** pentry);
     766             : 
     767             :     static inline Shape* searchNoHashify(Shape* start, jsid id);
     768             : 
     769             :     void removeFromDictionary(NativeObject* obj);
     770             :     void insertIntoDictionary(GCPtrShape* dictp);
     771             : 
     772             :     inline void initDictionaryShape(const StackShape& child, uint32_t nfixed,
     773             :                                     GCPtrShape* dictp);
     774             : 
     775             :     /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
     776             :     static Shape* replaceLastProperty(JSContext* cx, StackBaseShape& base,
     777             :                                       TaggedProto proto, HandleShape shape);
     778             : 
     779             :     /*
     780             :      * This function is thread safe if every shape in the lineage of |shape|
     781             :      * is thread local, which is the case when we clone the entire shape
     782             :      * lineage in preparation for converting an object to dictionary mode.
     783             :      */
     784             :     static bool hashify(JSContext* cx, Shape* shape);
     785             :     void handoffTableTo(Shape* newShape);
     786             : 
     787       94909 :     void setParent(Shape* p) {
     788       94909 :         MOZ_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
     789             :                       p->maybeSlot() <= maybeSlot());
     790       94909 :         MOZ_ASSERT_IF(p && !inDictionary(),
     791             :                       hasSlot() == (p->maybeSlot() != maybeSlot()));
     792       94909 :         parent = p;
     793       94909 :     }
     794             : 
     795        2716 :     bool ensureOwnBaseShape(JSContext* cx) {
     796        2716 :         if (base()->isOwned())
     797           0 :             return true;
     798        2716 :         return makeOwnBaseShape(cx);
     799             :     }
     800             : 
     801             :     bool makeOwnBaseShape(JSContext* cx);
     802             : 
     803             :     MOZ_ALWAYS_INLINE MOZ_MUST_USE bool maybeCreateTableForLookup(JSContext* cx);
     804             : 
     805             :   public:
     806      600680 :     bool hasTable() const { return base()->hasTable(); }
     807             : 
     808        9677 :     ShapeTable* maybeTable(const AutoKeepShapeTables& keep) const {
     809        9677 :         return base()->maybeTable(keep);
     810             :     }
     811      751428 :     ShapeTable* maybeTable(const JS::AutoCheckCannotGC& check) const {
     812      751428 :         return base()->maybeTable(check);
     813             :     }
     814             : 
     815             :     template <typename T>
     816       11134 :     MOZ_MUST_USE ShapeTable* ensureTableForDictionary(JSContext* cx, const T& nogc) {
     817       11134 :         MOZ_ASSERT(inDictionary());
     818       11134 :         if (ShapeTable* table = maybeTable(nogc))
     819       11134 :             return table;
     820           0 :         if (!hashify(cx, this))
     821           0 :             return nullptr;
     822           0 :         ShapeTable* table = maybeTable(nogc);
     823           0 :         MOZ_ASSERT(table);
     824           0 :         return table;
     825             :     }
     826             : 
     827           0 :     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
     828             :                                 JS::ShapeInfo* info) const
     829             :     {
     830           0 :         JS::AutoCheckCannotGC nogc;
     831           0 :         if (ShapeTable* table = maybeTable(nogc)) {
     832           0 :             if (inDictionary())
     833           0 :                 info->shapesMallocHeapDictTables += table->sizeOfIncludingThis(mallocSizeOf);
     834             :             else
     835           0 :                 info->shapesMallocHeapTreeTables += table->sizeOfIncludingThis(mallocSizeOf);
     836             :         }
     837             : 
     838           0 :         if (!inDictionary() && kids.isHash())
     839           0 :             info->shapesMallocHeapTreeKids += kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
     840           0 :     }
     841             : 
     842      774848 :     bool isAccessorShape() const {
     843      774848 :         MOZ_ASSERT_IF(flags & ACCESSOR_SHAPE, getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
     844      774848 :         return flags & ACCESSOR_SHAPE;
     845             :     }
     846      131302 :     AccessorShape& asAccessorShape() const {
     847      131302 :         MOZ_ASSERT(isAccessorShape());
     848      131302 :         return *(AccessorShape*)this;
     849             :     }
     850             : 
     851       30229 :     const GCPtrShape& previous() const { return parent; }
     852             : 
     853             :     template <AllowGC allowGC>
     854             :     class Range {
     855             :       protected:
     856             :         friend class Shape;
     857             : 
     858             :         typename MaybeRooted<Shape*, allowGC>::RootType cursor;
     859             : 
     860             :       public:
     861             :         Range(JSContext* cx, Shape* shape) : cursor(cx, shape) {
     862             :             JS_STATIC_ASSERT(allowGC == CanGC);
     863             :         }
     864             : 
     865      266451 :         explicit Range(Shape* shape) : cursor((JSContext*) nullptr, shape) {
     866             :             JS_STATIC_ASSERT(allowGC == NoGC);
     867      266451 :         }
     868             : 
     869     5194801 :         bool empty() const {
     870     5194801 :             return !cursor || cursor->isEmptyShape();
     871             :         }
     872             : 
     873       90695 :         Shape& front() const {
     874       90695 :             MOZ_ASSERT(!empty());
     875       90695 :             return *cursor;
     876             :         }
     877             : 
     878     2418924 :         void popFront() {
     879     2418924 :             MOZ_ASSERT(!empty());
     880     2418896 :             cursor = cursor->parent;
     881     2418900 :         }
     882             :     };
     883             : 
     884     3418562 :     const Class* getObjectClass() const {
     885     3418562 :         return base()->clasp_;
     886             :     }
     887             : 
     888             :     static Shape* setObjectFlags(JSContext* cx,
     889             :                                  BaseShape::Flag flag, TaggedProto proto, Shape* last);
     890             : 
     891      182649 :     uint32_t getObjectFlags() const { return base()->getObjectFlags(); }
     892      687424 :     bool hasAllObjectFlags(BaseShape::Flag flags) const {
     893      687424 :         MOZ_ASSERT(flags);
     894      687424 :         MOZ_ASSERT(!(flags & ~BaseShape::OBJECT_FLAG_MASK));
     895      687424 :         return (base()->flags & flags) == flags;
     896             :     }
     897             : 
     898             :   protected:
     899             :     /*
     900             :      * Implementation-private bits stored in shape->flags. See public: enum {}
     901             :      * flags further below, which were allocated FCFS over time, so interleave
     902             :      * with these bits.
     903             :      */
     904             :     enum {
     905             :         /* Property stored in per-object dictionary, not shared property tree. */
     906             :         IN_DICTIONARY   = 0x01,
     907             : 
     908             :         /*
     909             :          * Slotful property was stored to more than once. This is used as a
     910             :          * hint for type inference.
     911             :          */
     912             :         OVERWRITTEN     = 0x02,
     913             : 
     914             :         /*
     915             :          * This shape is an AccessorShape, a fat Shape that can store
     916             :          * getter/setter information.
     917             :          */
     918             :         ACCESSOR_SHAPE  = 0x04,
     919             : 
     920             :         /* Flags used to speed up isBigEnoughForAShapeTable(). */
     921             :         HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x08,
     922             :         CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE = 0x10,
     923             :     };
     924             : 
     925             :     /* Get a shape identical to this one, without parent/kids information. */
     926             :     inline Shape(const StackShape& other, uint32_t nfixed);
     927             : 
     928             :     /* Used by EmptyShape (see jsscopeinlines.h). */
     929             :     inline Shape(UnownedBaseShape* base, uint32_t nfixed);
     930             : 
     931             :     /* Copy constructor disabled, to avoid misuse of the above form. */
     932             :     Shape(const Shape& other) = delete;
     933             : 
     934             :     /* Allocate a new shape based on the given StackShape. */
     935             :     static inline Shape* new_(JSContext* cx, Handle<StackShape> other, uint32_t nfixed);
     936             : 
     937             :     /*
     938             :      * Whether this shape has a valid slot value. This may be true even if
     939             :      * !hasSlot() (see SlotInfo comment above), and may be false even if
     940             :      * hasSlot() if the shape is being constructed and has not had a slot
     941             :      * assigned yet. After construction, hasSlot() implies !hasMissingSlot().
     942             :      */
     943     3577894 :     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
     944             : 
     945             :   public:
     946     6938430 :     bool inDictionary() const {
     947     6938430 :         return (flags & IN_DICTIONARY) != 0;
     948             :     }
     949             : 
     950             :     inline GetterOp getter() const;
     951      253104 :     bool hasDefaultGetter() const { return !getter(); }
     952         732 :     GetterOp getterOp() const { MOZ_ASSERT(!hasGetterValue()); return getter(); }
     953             :     inline JSObject* getterObject() const;
     954       24319 :     bool hasGetterObject() const { return hasGetterValue() && getterObject(); }
     955             : 
     956             :     // Per ES5, decode null getterObj as the undefined value, which encodes as null.
     957        7824 :     Value getterValue() const {
     958        7824 :         MOZ_ASSERT(hasGetterValue());
     959        7824 :         if (JSObject* getterObj = getterObject())
     960        7824 :             return ObjectValue(*getterObj);
     961           0 :         return UndefinedValue();
     962             :     }
     963             : 
     964          54 :     Value getterOrUndefined() const {
     965          54 :         return hasGetterValue() ? getterValue() : UndefinedValue();
     966             :     }
     967             : 
     968             :     inline SetterOp setter() const;
     969      116499 :     bool hasDefaultSetter() const { return !setter(); }
     970          72 :     SetterOp setterOp() const { MOZ_ASSERT(!hasSetterValue()); return setter(); }
     971             :     inline JSObject* setterObject() const;
     972       21223 :     bool hasSetterObject() const { return hasSetterValue() && setterObject(); }
     973             : 
     974             :     // Per ES5, decode null setterObj as the undefined value, which encodes as null.
     975          10 :     Value setterValue() const {
     976          10 :         MOZ_ASSERT(hasSetterValue());
     977          10 :         if (JSObject* setterObj = setterObject())
     978          10 :             return ObjectValue(*setterObj);
     979           0 :         return UndefinedValue();
     980             :     }
     981             : 
     982           0 :     Value setterOrUndefined() const {
     983           0 :         return hasSetterValue() ? setterValue() : UndefinedValue();
     984             :     }
     985             : 
     986        2309 :     void setOverwritten() {
     987        2309 :         flags |= OVERWRITTEN;
     988        2309 :     }
     989         960 :     bool hadOverwrite() const {
     990         960 :         return flags & OVERWRITTEN;
     991             :     }
     992             : 
     993             :     void update(GetterOp getter, SetterOp setter, uint8_t attrs);
     994             : 
     995        2095 :     bool matches(const Shape* other) const {
     996        3150 :         return propid_.get() == other->propid_.get() &&
     997        1055 :                matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs, other->flags,
     998        2095 :                                     other->getter(), other->setter());
     999             :     }
    1000             : 
    1001             :     inline bool matches(const StackShape& other) const;
    1002             : 
    1003       88030 :     bool matchesParamsAfterId(BaseShape* base, uint32_t aslot, unsigned aattrs, unsigned aflags,
    1004             :                               GetterOp rawGetter, SetterOp rawSetter) const
    1005             :     {
    1006      173899 :         return base->unowned() == this->base()->unowned() &&
    1007      171551 :                maybeSlot() == aslot &&
    1008      170929 :                attrs == aattrs &&
    1009      258269 :                getter() == rawGetter &&
    1010      173022 :                setter() == rawSetter;
    1011             :     }
    1012             : 
    1013     6170879 :     BaseShape* base() const { return base_.get(); }
    1014             : 
    1015      755954 :     bool hasSlot() const {
    1016      755954 :         return (attrs & JSPROP_SHARED) == 0;
    1017             :     }
    1018      292938 :     uint32_t slot() const { MOZ_ASSERT(hasSlot() && !hasMissingSlot()); return maybeSlot(); }
    1019     6427674 :     uint32_t maybeSlot() const {
    1020     6427674 :         return slotInfo & SLOT_MASK;
    1021             :     }
    1022             : 
    1023     6097016 :     bool isEmptyShape() const {
    1024     6097016 :         MOZ_ASSERT_IF(JSID_IS_EMPTY(propid_), hasMissingSlot());
    1025     6097021 :         return JSID_IS_EMPTY(propid_);
    1026             :     }
    1027             : 
    1028     2935113 :     uint32_t slotSpan(const Class* clasp) const {
    1029     2935113 :         MOZ_ASSERT(!inDictionary());
    1030             :         // Proxy classes have reserved slots, but proxies manage their own slot
    1031             :         // layout. This means all non-native object shapes have nfixed == 0 and
    1032             :         // slotSpan == 0.
    1033     2935145 :         uint32_t free = clasp->isProxy() ? 0 : JSSLOT_FREE(clasp);
    1034     2935151 :         return hasMissingSlot() ? free : Max(free, maybeSlot() + 1);
    1035             :     }
    1036             : 
    1037     2934894 :     uint32_t slotSpan() const {
    1038     2934894 :         return slotSpan(getObjectClass());
    1039             :     }
    1040             : 
    1041         924 :     void setSlot(uint32_t slot) {
    1042         924 :         MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
    1043         924 :         slotInfo = slotInfo & ~Shape::SLOT_MASK;
    1044         924 :         slotInfo = slotInfo | slot;
    1045         924 :     }
    1046             : 
    1047      679078 :     uint32_t numFixedSlots() const {
    1048      679078 :         return slotInfo >> FIXED_SLOTS_SHIFT;
    1049             :     }
    1050             : 
    1051           0 :     void setNumFixedSlots(uint32_t nfixed) {
    1052           0 :         MOZ_ASSERT(nfixed < FIXED_SLOTS_MAX);
    1053           0 :         slotInfo = slotInfo & ~FIXED_SLOTS_MASK;
    1054           0 :         slotInfo = slotInfo | (nfixed << FIXED_SLOTS_SHIFT);
    1055           0 :     }
    1056             : 
    1057      374060 :     uint32_t numLinearSearches() const {
    1058      374060 :         return (slotInfo & LINEAR_SEARCHES_MASK) >> LINEAR_SEARCHES_SHIFT;
    1059             :     }
    1060             : 
    1061      109451 :     void incrementNumLinearSearches() {
    1062      109451 :         uint32_t count = numLinearSearches();
    1063      109451 :         MOZ_ASSERT(count < LINEAR_SEARCHES_MAX);
    1064      109451 :         slotInfo = slotInfo & ~LINEAR_SEARCHES_MASK;
    1065      109451 :         slotInfo = slotInfo | ((count + 1) << LINEAR_SEARCHES_SHIFT);
    1066      109451 :     }
    1067             : 
    1068      784448 :     const PreBarrieredId& propid() const {
    1069      784448 :         MOZ_ASSERT(!isEmptyShape());
    1070      784436 :         MOZ_ASSERT(!JSID_IS_VOID(propid_));
    1071      784425 :         return propid_;
    1072             :     }
    1073     6666544 :     PreBarrieredId& propidRef() { MOZ_ASSERT(!JSID_IS_VOID(propid_)); return propid_; }
    1074      321370 :     jsid propidRaw() const {
    1075             :         // Return the actual jsid, not an internal reference.
    1076      321370 :         return propid();
    1077             :     }
    1078             : 
    1079       13332 :     uint8_t attributes() const { return attrs; }
    1080       18311 :     bool configurable() const { return (attrs & JSPROP_PERMANENT) == 0; }
    1081       12617 :     bool enumerable() const { return (attrs & JSPROP_ENUMERATE) != 0; }
    1082      142361 :     bool writable() const {
    1083      142361 :         return (attrs & JSPROP_READONLY) == 0;
    1084             :     }
    1085       90937 :     bool hasGetterValue() const { return attrs & JSPROP_GETTER; }
    1086       46999 :     bool hasSetterValue() const { return attrs & JSPROP_SETTER; }
    1087             : 
    1088       42502 :     bool isDataDescriptor() const {
    1089       42502 :         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
    1090             :     }
    1091         449 :     bool isAccessorDescriptor() const {
    1092         449 :         return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
    1093             :     }
    1094             : 
    1095           0 :     bool hasShadowable() const { return attrs & JSPROP_SHADOWABLE; }
    1096             : 
    1097      117048 :     uint32_t entryCount() {
    1098      234097 :         JS::AutoCheckCannotGC nogc;
    1099      117049 :         if (ShapeTable* table = maybeTable(nogc))
    1100       17628 :             return table->entryCount();
    1101       99421 :         uint32_t count = 0;
    1102     2021724 :         for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront())
    1103     1922303 :             ++count;
    1104       99421 :         return count;
    1105             :     }
    1106             : 
    1107             :   private:
    1108      155170 :     bool isBigEnoughForAShapeTableSlow() {
    1109      155170 :         uint32_t count = 0;
    1110      560663 :         for (Shape::Range<NoGC> r(this); !r.empty(); r.popFront()) {
    1111      407262 :             ++count;
    1112      407262 :             if (count >= ShapeTable::MIN_ENTRIES)
    1113        1769 :                 return true;
    1114             :         }
    1115      153401 :         return false;
    1116             :     }
    1117        5311 :     void clearCachedBigEnoughForShapeTable() {
    1118        5311 :         flags &= ~(HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE | CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE);
    1119        5311 :     }
    1120             : 
    1121             :   public:
    1122      155170 :     bool isBigEnoughForAShapeTable() {
    1123      155170 :         MOZ_ASSERT(!hasTable());
    1124             : 
    1125             :         // isBigEnoughForAShapeTableSlow is pretty inefficient so we only call
    1126             :         // it once and cache the result.
    1127             : 
    1128      155170 :         if (flags & HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE) {
    1129      150809 :             bool res = flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
    1130      150809 :             MOZ_ASSERT(res == isBigEnoughForAShapeTableSlow());
    1131      150809 :             return res;
    1132             :         }
    1133             : 
    1134        4361 :         MOZ_ASSERT(!(flags & CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE));
    1135             : 
    1136        4361 :         bool res = isBigEnoughForAShapeTableSlow();
    1137        4361 :         if (res)
    1138        1746 :             flags |= CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
    1139        4361 :         flags |= HAS_CACHED_BIG_ENOUGH_FOR_SHAPE_TABLE;
    1140        4361 :         return res;
    1141             :     }
    1142             : 
    1143             : #ifdef DEBUG
    1144             :     void dump(FILE* fp) const;
    1145             :     void dumpSubtree(int level, FILE* fp) const;
    1146             : #endif
    1147             : 
    1148             :     void sweep();
    1149             :     void finalize(FreeOp* fop);
    1150             :     void removeChild(Shape* child);
    1151             : 
    1152             :     static const JS::TraceKind TraceKind = JS::TraceKind::Shape;
    1153             : 
    1154             :     void traceChildren(JSTracer* trc);
    1155             : 
    1156             :     MOZ_ALWAYS_INLINE Shape* search(JSContext* cx, jsid id);
    1157             :     MOZ_ALWAYS_INLINE Shape* searchLinear(jsid id);
    1158             : 
    1159             :     void fixupAfterMovingGC();
    1160             :     void fixupGetterSetterForBarrier(JSTracer* trc);
    1161             :     void updateBaseShapeAfterMovingGC();
    1162             : 
    1163             :     /* For JIT usage */
    1164             :     static inline size_t offsetOfBase() { return offsetof(Shape, base_); }
    1165           0 :     static inline size_t offsetOfSlotInfo() { return offsetof(Shape, slotInfo); }
    1166           0 :     static inline uint32_t fixedSlotsMask() { return FIXED_SLOTS_MASK; }
    1167             : 
    1168             :   private:
    1169             :     void fixupDictionaryShapeAfterMovingGC();
    1170             :     void fixupShapeTreeAfterMovingGC();
    1171             : 
    1172             :     static void staticAsserts() {
    1173             :         JS_STATIC_ASSERT(offsetof(Shape, base_) == offsetof(js::shadow::Shape, base));
    1174             :         JS_STATIC_ASSERT(offsetof(Shape, slotInfo) == offsetof(js::shadow::Shape, slotInfo));
    1175             :         JS_STATIC_ASSERT(FIXED_SLOTS_SHIFT == js::shadow::Shape::FIXED_SLOTS_SHIFT);
    1176             :     }
    1177             : };
    1178             : 
    1179             : /* Fat Shape used for accessor properties. */
    1180             : class AccessorShape : public Shape
    1181             : {
    1182             :     friend class Shape;
    1183             :     friend class NativeObject;
    1184             : 
    1185             :     union {
    1186             :         GetterOp rawGetter;     /* getter hook for shape */
    1187             :         JSObject* getterObj;    /* user-defined callable "get" object or
    1188             :                                    null if shape->hasGetterValue() */
    1189             :     };
    1190             :     union {
    1191             :         SetterOp rawSetter;     /* setter hook for shape */
    1192             :         JSObject* setterObj;    /* user-defined callable "set" object or
    1193             :                                    null if shape->hasSetterValue() */
    1194             :     };
    1195             : 
    1196             :   public:
    1197             :     /* Get a shape identical to this one, without parent/kids information. */
    1198             :     inline AccessorShape(const StackShape& other, uint32_t nfixed);
    1199             : };
    1200             : 
    1201             : inline
    1202       12781 : StackBaseShape::StackBaseShape(Shape* shape)
    1203       12781 :   : flags(shape->getObjectFlags()),
    1204       25562 :     clasp(shape->getObjectClass())
    1205       12781 : {}
    1206             : 
    1207      139630 : class MOZ_RAII AutoRooterGetterSetter
    1208             : {
    1209       19963 :     class Inner final : private JS::CustomAutoRooter
    1210             :     {
    1211             :       public:
    1212             :         inline Inner(JSContext* cx, uint8_t attrs, GetterOp* pgetter_, SetterOp* psetter_);
    1213             : 
    1214             :       private:
    1215             :         virtual void trace(JSTracer* trc);
    1216             : 
    1217             :         uint8_t attrs;
    1218             :         GetterOp* pgetter;
    1219             :         SetterOp* psetter;
    1220             :     };
    1221             : 
    1222             :   public:
    1223             :     inline AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
    1224             :                                   GetterOp* pgetter, SetterOp* psetter
    1225             :                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
    1226             :     inline AutoRooterGetterSetter(JSContext* cx, uint8_t attrs,
    1227             :                                   JSNative* pgetter, JSNative* psetter
    1228             :                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
    1229             : 
    1230             :   private:
    1231             :     mozilla::Maybe<Inner> inner;
    1232             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
    1233             : };
    1234             : 
    1235             : struct EmptyShape : public js::Shape
    1236             : {
    1237        5252 :     EmptyShape(UnownedBaseShape* base, uint32_t nfixed)
    1238        5252 :       : js::Shape(base, nfixed)
    1239        5252 :     { }
    1240             : 
    1241             :     static Shape* new_(JSContext* cx, Handle<UnownedBaseShape*> base, uint32_t nfixed);
    1242             : 
    1243             :     /*
    1244             :      * Lookup an initial shape matching the given parameters, creating an empty
    1245             :      * shape if none was found.
    1246             :      */
    1247             :     static Shape* getInitialShape(JSContext* cx, const Class* clasp,
    1248             :                                   TaggedProto proto, size_t nfixed, uint32_t objectFlags = 0);
    1249             :     static Shape* getInitialShape(JSContext* cx, const Class* clasp,
    1250             :                                   TaggedProto proto, gc::AllocKind kind, uint32_t objectFlags = 0);
    1251             : 
    1252             :     /*
    1253             :      * Reinsert an alternate initial shape, to be returned by future
    1254             :      * getInitialShape calls, until the new shape becomes unreachable in a GC
    1255             :      * and the table entry is purged.
    1256             :      */
    1257             :     static void insertInitialShape(JSContext* cx, HandleShape shape, HandleObject proto);
    1258             : 
    1259             :     /*
    1260             :      * Some object subclasses are allocated with a built-in set of properties.
    1261             :      * The first time such an object is created, these built-in properties must
    1262             :      * be set manually, to compute an initial shape.  Afterward, that initial
    1263             :      * shape can be reused for newly-created objects that use the subclass's
    1264             :      * standard prototype.  This method should be used in a post-allocation
    1265             :      * init method, to ensure that objects of such subclasses compute and cache
    1266             :      * the initial shape, if it hasn't already been computed.
    1267             :      */
    1268             :     template<class ObjectSubclass>
    1269             :     static inline bool
    1270             :     ensureInitialCustomShape(JSContext* cx, Handle<ObjectSubclass*> obj);
    1271             : };
    1272             : 
    1273             : // InitialShapeProto stores either:
    1274             : //
    1275             : // * A TaggedProto (or ReadBarriered<TaggedProto>).
    1276             : //
    1277             : // * A JSProtoKey. This is used instead of the TaggedProto if the proto is one
    1278             : //   of the global's builtin prototypes. For instance, if the proto is the
    1279             : //   initial Object.prototype, we use key_ = JSProto_Object, proto_ = nullptr.
    1280             : //
    1281             : // Using the JSProtoKey here is an optimization that lets us share more shapes
    1282             : // across compartments within a zone.
    1283             : template <typename PtrType>
    1284      230000 : class InitialShapeProto
    1285             : {
    1286             :     template <typename T> friend class InitialShapeProto;
    1287             : 
    1288             :     JSProtoKey key_;
    1289             :     PtrType proto_;
    1290             : 
    1291             :   public:
    1292             :     InitialShapeProto()
    1293             :       : key_(JSProto_LIMIT), proto_()
    1294             :     {}
    1295             : 
    1296           0 :     InitialShapeProto(JSProtoKey key, TaggedProto proto)
    1297           0 :       : key_(key), proto_(proto)
    1298           0 :     {}
    1299             : 
    1300             :     template <typename T>
    1301       11215 :     explicit InitialShapeProto(const InitialShapeProto<T>& other)
    1302       11215 :       : key_(other.key()), proto_(other.proto_)
    1303       11215 :     {}
    1304             : 
    1305      152630 :     explicit InitialShapeProto(TaggedProto proto)
    1306      152630 :       : key_(JSProto_LIMIT), proto_(proto)
    1307      152630 :     {}
    1308        6876 :     explicit InitialShapeProto(JSProtoKey key)
    1309        6876 :       : key_(key), proto_(nullptr)
    1310             :     {
    1311        6876 :         MOZ_ASSERT(key < JSProto_LIMIT);
    1312        6876 :     }
    1313             : 
    1314      445027 :     JSProtoKey key() const {
    1315      445027 :         return key_;
    1316             :     }
    1317      433647 :     const PtrType& proto() const {
    1318      433647 :         return proto_;
    1319             :     }
    1320           0 :     void setProto(TaggedProto proto) {
    1321           0 :         proto_ = proto;
    1322           0 :     }
    1323             : 
    1324           0 :     bool operator==(const InitialShapeProto& other) const {
    1325           0 :         return key_ == other.key_ && proto_ == other.proto_;
    1326             :     }
    1327             : };
    1328             : 
    1329             : template <>
    1330             : struct MovableCellHasher<InitialShapeProto<ReadBarriered<TaggedProto>>>
    1331             : {
    1332             :     using Key = InitialShapeProto<ReadBarriered<TaggedProto>>;
    1333             :     using Lookup = InitialShapeProto<TaggedProto>;
    1334             : 
    1335             :     static bool hasHash(const Lookup& l) {
    1336             :         return MovableCellHasher<TaggedProto>::hasHash(l.proto());
    1337             :     }
    1338             :     static bool ensureHash(const Lookup& l) {
    1339             :         return MovableCellHasher<TaggedProto>::ensureHash(l.proto());
    1340             :     }
    1341      159507 :     static HashNumber hash(const Lookup& l) {
    1342      159507 :         return HashNumber(l.key()) ^ MovableCellHasher<TaggedProto>::hash(l.proto());
    1343             :     }
    1344      137163 :     static bool match(const Key& k, const Lookup& l) {
    1345      274239 :         return k.key() == l.key() &&
    1346      274239 :                MovableCellHasher<TaggedProto>::match(k.proto().unbarrieredGet(), l.proto());
    1347             :     }
    1348             : };
    1349             : 
    1350             : /*
    1351             :  * Entries for the per-zone initialShapes set indexing initial shapes for
    1352             :  * objects in the zone and the associated types.
    1353             :  */
    1354       59279 : struct InitialShapeEntry
    1355             : {
    1356             :     /*
    1357             :      * Initial shape to give to the object. This is an empty shape, except for
    1358             :      * certain classes (e.g. String, RegExp) which may add certain baked-in
    1359             :      * properties.
    1360             :      */
    1361             :     ReadBarriered<Shape*> shape;
    1362             : 
    1363             :     /*
    1364             :      * Matching prototype for the entry. The shape of an object determines its
    1365             :      * prototype, but the prototype cannot be determined from the shape itself.
    1366             :      */
    1367             :     using ShapeProto = InitialShapeProto<ReadBarriered<TaggedProto>>;
    1368             :     ShapeProto proto;
    1369             : 
    1370             :     /* State used to determine a match on an initial shape. */
    1371             :     struct Lookup {
    1372             :         using ShapeProto = InitialShapeProto<TaggedProto>;
    1373             :         const Class* clasp;
    1374             :         ShapeProto proto;
    1375             :         uint32_t nfixed;
    1376             :         uint32_t baseFlags;
    1377             : 
    1378      159507 :         Lookup(const Class* clasp, ShapeProto proto, uint32_t nfixed, uint32_t baseFlags)
    1379      159507 :           : clasp(clasp), proto(proto), nfixed(nfixed), baseFlags(baseFlags)
    1380      159506 :         {}
    1381             : 
    1382             :         explicit Lookup(const InitialShapeEntry& entry)
    1383             :           : proto(entry.proto.key(),
    1384             :                   entry.proto.proto().unbarrieredGet())
    1385             :         {
    1386             :             const Shape* shape = entry.shape.unbarrieredGet();
    1387             :             clasp = shape->getObjectClass();
    1388             :             nfixed = shape->numFixedSlots();
    1389             :             baseFlags = shape->getObjectFlags();
    1390             :         }
    1391             :     };
    1392             : 
    1393             :     inline InitialShapeEntry();
    1394             :     inline InitialShapeEntry(Shape* shape, const Lookup::ShapeProto& proto);
    1395             : 
    1396      159508 :     static HashNumber hash(const Lookup& lookup) {
    1397      319012 :         return (mozilla::RotateLeft(uintptr_t(lookup.clasp) >> 3, 4) ^
    1398      159507 :                 MovableCellHasher<ShapeProto>::hash(lookup.proto)) +
    1399      159504 :                lookup.nfixed;
    1400             :     }
    1401      159981 :     static inline bool match(const InitialShapeEntry& key, const Lookup& lookup) {
    1402      159981 :         const Shape* shape = key.shape.unbarrieredGet();
    1403      159981 :         return lookup.clasp == shape->getObjectClass()
    1404      159032 :             && lookup.nfixed == shape->numFixedSlots()
    1405      156460 :             && lookup.baseFlags == shape->getObjectFlags()
    1406      297144 :             && MovableCellHasher<ShapeProto>::match(key.proto, lookup.proto);
    1407             :     }
    1408           0 :     static void rekey(InitialShapeEntry& k, const InitialShapeEntry& newKey) {
    1409           0 :         k = newKey;
    1410           0 :     }
    1411             : 
    1412           0 :     bool needsSweep() {
    1413           0 :         Shape* ushape = shape.unbarrieredGet();
    1414           0 :         TaggedProto uproto = proto.proto().unbarrieredGet();
    1415           0 :         JSObject* protoObj = uproto.raw();
    1416           0 :         return (gc::IsAboutToBeFinalizedUnbarriered(&ushape) ||
    1417           0 :                 (uproto.isObject() && gc::IsAboutToBeFinalizedUnbarriered(&protoObj)));
    1418             :     }
    1419             : 
    1420           0 :     bool operator==(const InitialShapeEntry& other) const {
    1421           0 :         return shape == other.shape && proto == other.proto;
    1422             :     }
    1423             : };
    1424             : 
    1425             : using InitialShapeSet = JS::WeakCache<JS::GCHashSet<InitialShapeEntry,
    1426             :                                                     InitialShapeEntry,
    1427             :                                                     SystemAllocPolicy>>;
    1428             : 
    1429             : struct StackShape
    1430             : {
    1431             :     /* For performance, StackShape only roots when absolutely necessary. */
    1432             :     UnownedBaseShape* base;
    1433             :     jsid propid;
    1434             :     GetterOp rawGetter;
    1435             :     SetterOp rawSetter;
    1436             :     uint32_t slot_;
    1437             :     uint8_t attrs;
    1438             :     uint8_t flags;
    1439             : 
    1440      146543 :     explicit StackShape(UnownedBaseShape* base, jsid propid, uint32_t slot,
    1441             :                         unsigned attrs, unsigned flags)
    1442      146543 :       : base(base),
    1443             :         propid(propid),
    1444             :         rawGetter(nullptr),
    1445             :         rawSetter(nullptr),
    1446             :         slot_(slot),
    1447             :         attrs(uint8_t(attrs)),
    1448      146543 :         flags(uint8_t(flags))
    1449             :     {
    1450      146543 :         MOZ_ASSERT(base);
    1451      146543 :         MOZ_ASSERT(!JSID_IS_VOID(propid));
    1452      146542 :         MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
    1453      146542 :         MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
    1454      146542 :     }
    1455             : 
    1456       38850 :     explicit StackShape(Shape* shape)
    1457       38850 :       : base(shape->base()->unowned()),
    1458       38850 :         propid(shape->propidRef()),
    1459       38850 :         rawGetter(shape->getter()),
    1460       38849 :         rawSetter(shape->setter()),
    1461       38850 :         slot_(shape->maybeSlot()),
    1462       38850 :         attrs(shape->attrs),
    1463      233099 :         flags(shape->flags)
    1464       38850 :     {}
    1465             : 
    1466      117460 :     void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
    1467      117460 :         if (rawGetter || rawSetter || (attrs & (JSPROP_GETTER|JSPROP_SETTER)))
    1468       16530 :             flags |= Shape::ACCESSOR_SHAPE;
    1469             :         else
    1470      100930 :             flags &= ~Shape::ACCESSOR_SHAPE;
    1471             : 
    1472      117460 :         this->rawGetter = rawGetter;
    1473      117460 :         this->rawSetter = rawSetter;
    1474      117460 :     }
    1475             : 
    1476      122090 :     bool hasSlot() const { return (attrs & JSPROP_SHARED) == 0; }
    1477      104275 :     bool hasMissingSlot() const { return maybeSlot() == SHAPE_INVALID_SLOT; }
    1478             : 
    1479        1965 :     uint32_t slot() const { MOZ_ASSERT(hasSlot() && !hasMissingSlot()); return slot_; }
    1480      200127 :     uint32_t maybeSlot() const { return slot_; }
    1481             : 
    1482      117332 :     void setSlot(uint32_t slot) {
    1483      117332 :         MOZ_ASSERT(slot <= SHAPE_INVALID_SLOT);
    1484      117332 :         slot_ = slot;
    1485      117332 :     }
    1486             : 
    1487      343001 :     bool isAccessorShape() const {
    1488      343001 :         return flags & Shape::ACCESSOR_SHAPE;
    1489             :     }
    1490             : 
    1491       47465 :     HashNumber hash() const {
    1492       47465 :         HashNumber hash = uintptr_t(base);
    1493             : 
    1494             :         /* Accumulate from least to most random so the low bits are most random. */
    1495       47465 :         hash = mozilla::RotateLeft(hash, 4) ^ attrs;
    1496       47465 :         hash = mozilla::RotateLeft(hash, 4) ^ slot_;
    1497       47465 :         hash = mozilla::RotateLeft(hash, 4) ^ HashId(propid);
    1498       47466 :         hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawGetter);
    1499       47466 :         hash = mozilla::RotateLeft(hash, 4) ^ uintptr_t(rawSetter);
    1500       47467 :         return hash;
    1501             :     }
    1502             : 
    1503             :     // Traceable implementation.
    1504             :     static void trace(StackShape* stackShape, JSTracer* trc) { stackShape->trace(trc); }
    1505             :     void trace(JSTracer* trc);
    1506             : };
    1507             : 
    1508             : template <typename Wrapper>
    1509      416555 : class WrappedPtrOperations<StackShape, Wrapper>
    1510             : {
    1511      377091 :     const StackShape& ss() const { return static_cast<const Wrapper*>(this)->get(); }
    1512             : 
    1513             :   public:
    1514      120125 :     bool hasSlot() const { return ss().hasSlot(); }
    1515      102310 :     bool hasMissingSlot() const { return ss().hasMissingSlot(); }
    1516        1965 :     uint32_t slot() const { return ss().slot(); }
    1517             :     uint32_t maybeSlot() const { return ss().maybeSlot(); }
    1518             :     uint32_t slotSpan() const { return ss().slotSpan(); }
    1519      147860 :     bool isAccessorShape() const { return ss().isAccessorShape(); }
    1520        4830 :     uint8_t attrs() const { return ss().attrs; }
    1521             : };
    1522             : 
    1523             : template <typename Wrapper>
    1524      268339 : class MutableWrappedPtrOperations<StackShape, Wrapper>
    1525             :   : public WrappedPtrOperations<StackShape, Wrapper>
    1526             : {
    1527      239128 :     StackShape& ss() { return static_cast<Wrapper*>(this)->get(); }
    1528             : 
    1529             :   public:
    1530      117460 :     void updateGetterSetter(GetterOp rawGetter, SetterOp rawSetter) {
    1531      117460 :         ss().updateGetterSetter(rawGetter, rawSetter);
    1532      117460 :     }
    1533      117332 :     void setSlot(uint32_t slot) { ss().setSlot(slot); }
    1534        1895 :     void setBase(UnownedBaseShape* base) { ss().base = base; }
    1535        2415 :     void setAttrs(uint8_t attrs) { ss().attrs = attrs; }
    1536             : };
    1537             : 
    1538             : inline
    1539       95856 : Shape::Shape(const StackShape& other, uint32_t nfixed)
    1540      191711 :   : base_(other.base),
    1541             :     propid_(other.propid),
    1542       95855 :     slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
    1543       95855 :     attrs(other.attrs),
    1544       95855 :     flags(other.flags),
    1545      575132 :     parent(nullptr)
    1546             : {
    1547             : #ifdef DEBUG
    1548       95855 :     gc::AllocKind allocKind = getAllocKind();
    1549       95856 :     MOZ_ASSERT_IF(other.isAccessorShape(), allocKind == gc::AllocKind::ACCESSOR_SHAPE);
    1550       95856 :     MOZ_ASSERT_IF(allocKind == gc::AllocKind::SHAPE, !other.isAccessorShape());
    1551             : #endif
    1552             : 
    1553       95856 :     MOZ_ASSERT_IF(!isEmptyShape(), AtomIsMarked(zone(), propid()));
    1554             : 
    1555       95854 :     MOZ_ASSERT_IF(attrs & (JSPROP_GETTER | JSPROP_SETTER), attrs & JSPROP_SHARED);
    1556       95854 :     kids.setNull();
    1557       95854 : }
    1558             : 
    1559             : // This class is used to add a post barrier on the AccessorShape's getter/setter
    1560             : // objects. It updates the pointers and the shape's entry in the parent's
    1561             : // KidsHash table.
    1562        3350 : class ShapeGetterSetterRef : public gc::BufferableRef
    1563             : {
    1564             :     AccessorShape* shape_;
    1565             : 
    1566             :   public:
    1567        3350 :     explicit ShapeGetterSetterRef(AccessorShape* shape) : shape_(shape) {}
    1568        3129 :     void trace(JSTracer* trc) override { shape_->fixupGetterSetterForBarrier(trc); }
    1569             : };
    1570             : 
    1571             : static inline void
    1572       19563 : GetterSetterWriteBarrierPost(AccessorShape* shape)
    1573             : {
    1574       19563 :     MOZ_ASSERT(shape);
    1575       19563 :     if (shape->hasGetterObject()) {
    1576       18312 :         gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(shape->getterObject())->storeBuffer();
    1577       18312 :         if (sb) {
    1578        3301 :             sb->putGeneric(ShapeGetterSetterRef(shape));
    1579        3301 :             return;
    1580             :         }
    1581             :     }
    1582       16262 :     if (shape->hasSetterObject()) {
    1583        7022 :         gc::StoreBuffer* sb = reinterpret_cast<gc::Cell*>(shape->setterObject())->storeBuffer();
    1584        7022 :         if (sb) {
    1585          49 :             sb->putGeneric(ShapeGetterSetterRef(shape));
    1586          49 :             return;
    1587             :         }
    1588             :     }
    1589             : }
    1590             : 
    1591             : inline
    1592       19446 : AccessorShape::AccessorShape(const StackShape& other, uint32_t nfixed)
    1593             :   : Shape(other, nfixed),
    1594       19446 :     rawGetter(other.rawGetter),
    1595       38892 :     rawSetter(other.rawSetter)
    1596             : {
    1597       19446 :     MOZ_ASSERT(getAllocKind() == gc::AllocKind::ACCESSOR_SHAPE);
    1598       19446 :     GetterSetterWriteBarrierPost(this);
    1599       19446 : }
    1600             : 
    1601             : inline
    1602       10182 : Shape::Shape(UnownedBaseShape* base, uint32_t nfixed)
    1603             :   : base_(base),
    1604             :     propid_(JSID_EMPTY),
    1605       10182 :     slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
    1606             :     attrs(JSPROP_SHARED),
    1607             :     flags(0),
    1608       20364 :     parent(nullptr)
    1609             : {
    1610       10182 :     MOZ_ASSERT(base);
    1611       10182 :     kids.setNull();
    1612       10182 : }
    1613             : 
    1614             : inline GetterOp
    1615      379895 : Shape::getter() const
    1616             : {
    1617      379895 :     return isAccessorShape() ? asAccessorShape().rawGetter : nullptr;
    1618             : }
    1619             : 
    1620             : inline SetterOp
    1621      242332 : Shape::setter() const
    1622             : {
    1623      242332 :     return isAccessorShape() ? asAccessorShape().rawSetter : nullptr;
    1624             : }
    1625             : 
    1626             : inline JSObject*
    1627       46853 : Shape::getterObject() const
    1628             : {
    1629       46853 :     MOZ_ASSERT(hasGetterValue());
    1630       46853 :     return asAccessorShape().getterObj;
    1631             : }
    1632             : 
    1633             : inline JSObject*
    1634       24544 : Shape::setterObject() const
    1635             : {
    1636       24544 :     MOZ_ASSERT(hasSetterValue());
    1637       24544 :     return asAccessorShape().setterObj;
    1638             : }
    1639             : 
    1640             : inline void
    1641       23258 : Shape::initDictionaryShape(const StackShape& child, uint32_t nfixed, GCPtrShape* dictp)
    1642             : {
    1643       23258 :     if (child.isAccessorShape())
    1644        3511 :         new (this) AccessorShape(child, nfixed);
    1645             :     else
    1646       19747 :         new (this) Shape(child, nfixed);
    1647       23258 :     this->flags |= IN_DICTIONARY;
    1648             : 
    1649       23258 :     this->listp = nullptr;
    1650       23258 :     if (dictp)
    1651       22311 :         insertIntoDictionary(dictp);
    1652       23258 : }
    1653             : 
    1654             : inline Shape*
    1655      407249 : Shape::searchLinear(jsid id)
    1656             : {
    1657     6936573 :     for (Shape* shape = this; shape; ) {
    1658     6617547 :         if (shape->propidRef() == id)
    1659       88260 :             return shape;
    1660     6529358 :         shape = shape->parent;
    1661             :     }
    1662             : 
    1663      319026 :     return nullptr;
    1664             : }
    1665             : 
    1666             : inline bool
    1667       78626 : Shape::matches(const StackShape& other) const
    1668             : {
    1669      156212 :     return propid_.get() == other.propid &&
    1670       77586 :            matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags,
    1671      156212 :                                 other.rawGetter, other.rawSetter);
    1672             : }
    1673             : 
    1674             : Shape*
    1675             : ReshapeForAllocKind(JSContext* cx, Shape* shape, TaggedProto proto,
    1676             :                     gc::AllocKind allocKind);
    1677             : 
    1678             : } // namespace js
    1679             : 
    1680             : #ifdef _MSC_VER
    1681             : #pragma warning(pop)
    1682             : #pragma warning(pop)
    1683             : #endif
    1684             : 
    1685             : // JS::ubi::Nodes can point to Shapes and BaseShapes; they're js::gc::Cell
    1686             : // instances that occupy a compartment.
    1687             : namespace JS {
    1688             : namespace ubi {
    1689             : 
    1690             : template<>
    1691             : class Concrete<js::Shape> : TracerConcrete<js::Shape> {
    1692             :   protected:
    1693           0 :     explicit Concrete(js::Shape *ptr) : TracerConcrete<js::Shape>(ptr) { }
    1694             : 
    1695             :   public:
    1696           0 :     static void construct(void *storage, js::Shape *ptr) { new (storage) Concrete(ptr); }
    1697             : 
    1698             :     Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
    1699             : 
    1700           0 :     const char16_t* typeName() const override { return concreteTypeName; }
    1701             :     static const char16_t concreteTypeName[];
    1702             : };
    1703             : 
    1704             : template<>
    1705             : class Concrete<js::BaseShape> : TracerConcrete<js::BaseShape> {
    1706             :   protected:
    1707           0 :     explicit Concrete(js::BaseShape *ptr) : TracerConcrete<js::BaseShape>(ptr) { }
    1708             : 
    1709             :   public:
    1710           0 :     static void construct(void *storage, js::BaseShape *ptr) { new (storage) Concrete(ptr); }
    1711             : 
    1712             :     Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
    1713             : 
    1714           0 :     const char16_t* typeName() const override { return concreteTypeName; }
    1715             :     static const char16_t concreteTypeName[];
    1716             : };
    1717             : 
    1718             : } // namespace ubi
    1719             : } // namespace JS
    1720             : 
    1721             : #endif /* vm_Shape_h */

Generated by: LCOV version 1.13