tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PropMap.h (43145B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      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_PropMap_h
      8 #define vm_PropMap_h
      9 
     10 #include "gc/Barrier.h"
     11 #include "gc/Cell.h"
     12 #include "gc/Policy.h"
     13 #include "js/TypeDecls.h"
     14 #include "js/UbiNode.h"
     15 #include "js/Utility.h"  // JS::UniqueChars
     16 #include "vm/ObjectFlags.h"
     17 #include "vm/PropertyInfo.h"
     18 #include "vm/PropertyKey.h"
     19 
     20 // [SMDOC] Property Maps
     21 //
     22 // Property maps are used to store information about native object properties.
     23 // Each property map represents an ordered list of (PropertyKey, PropertyInfo)
     24 // tuples.
     25 //
     26 // Each property map can store up to 8 properties (see PropMap::Capacity). To
     27 // store more than eight properties, multiple maps must be linked together with
     28 // the |previous| pointer.
     29 //
     30 // Shapes and Property Maps
     31 // ------------------------
     32 // Native object shapes represent property information as a (PropMap*, length)
     33 // tuple. When there are no properties yet, the shape's map will be nullptr and
     34 // the length is zero.
     35 //
     36 // For example, consider the following objects:
     37 //
     38 //   o1 = {x: 1, y: 2}
     39 //   o2 = {x: 3, y: 4, z: 5}
     40 //
     41 // This is stored as follows:
     42 //
     43 //   +-------------+      +--------------+     +-------------------+
     44 //   | JSObject o1 |      | Shape S1     |     | PropMap M1        |
     45 //   |-------------+      +--------------+     +-------------------+
     46 //   | shape: S1  -+--->  | map: M1     -+--+> | key 0: x (slot 0) |
     47 //   | slot 0: 1   |      | mapLength: 2 |  |  | key 1: y (slot 1) |
     48 //   | slot 1: 2   |      +--------------+  |  | key 2: z (slot 2) |
     49 //   +-------------+                        |  | ...               |
     50 //                                          |  +-------------------+
     51 //                                          |
     52 //   +-------------+      +--------------+  |
     53 //   | JSObject o2 |      | Shape S2     |  |
     54 //   |-------------+      +--------------+  |
     55 //   | shape: S2  -+--->  | map: M1     -+--+
     56 //   | slot 0: 3   |      | mapLength: 3 |
     57 //   | slot 1: 4   |      +--------------+
     58 //   | slot 2: 5   |
     59 //   +-------------+
     60 //
     61 // There's a single map M1 shared by shapes S1 and S2. Shape S1 includes only
     62 // the first two properties and shape S2 includes all three properties.
     63 //
     64 // Class Hierarchy
     65 // ---------------
     66 // Property maps have the following C++ class hierarchy:
     67 //
     68 //   PropMap (abstract)
     69 //    |
     70 //    +-- SharedPropMap (abstract)
     71 //    |      |
     72 //    |      +-- CompactPropMap
     73 //    |      |
     74 //    |      +-- NormalPropMap
     75 //    |
     76 //    +-- DictionaryPropMap
     77 //
     78 // * PropMap: base class. It has a flags word and an array of PropertyKeys.
     79 //
     80 // * SharedPropMap: base class for all shared property maps. See below for more
     81 //                  information on shared maps.
     82 //
     83 // * CompactPropMap: a shared map that stores its information more compactly
     84 //                   than the other maps. It saves four words by not storing a
     85 //                   PropMapTable, previous pointer, and by using a more compact
     86 //                   PropertyInfo type for slot numbers that fit in one byte.
     87 //
     88 // * NormalPropMap: a shared map, used when CompactPropMap can't be used.
     89 //
     90 // * DictionaryPropMap: an unshared map (used by a single object/shape). See
     91 //                      below for more information on dictionary maps.
     92 //
     93 // Secondary hierarchy
     94 // -------------------
     95 // NormalPropMap and DictionaryPropMap store property information in the same
     96 // way. This means property lookups don't have to distinguish between these two
     97 // types. This is represented with a second class hierarchy:
     98 //
     99 //   PropMap (abstract)
    100 //    |
    101 //    +-- CompactPropMap
    102 //    |
    103 //    +-- LinkedPropMap (NormalPropMap or DictionaryPropMap)
    104 //
    105 // Property lookup and property iteration are very performance sensitive and use
    106 // this Compact vs Linked "view" so that they don't have to handle the three map
    107 // types separately.
    108 //
    109 // LinkedPropMap also stores the PropMapTable and a pointer to the |previous|
    110 // map. Compact maps don't have these fields.
    111 //
    112 // To summarize these map types:
    113 //
    114 //   +-------------------+-------------+--------+
    115 //   | Concrete type     | Shared/tree | Linked |
    116 //   +-------------------+-------------+--------+
    117 //   | CompactPropMap    | yes         | no     |
    118 //   | NormalPropMap     | yes         | yes    |
    119 //   | DictionaryPropMap | no          | yes    |
    120 //   +-------------------+-------------+--------+
    121 //
    122 // PropMapTable
    123 // ------------
    124 // Finding the PropertyInfo for a particular PropertyKey requires a linear
    125 // search if the map is small. For larger maps we can create a PropMapTable, a
    126 // hash table that maps from PropertyKey to PropMap + index, to speed up
    127 // property lookups.
    128 //
    129 // To save memory, property map tables can be discarded on GC and recreated when
    130 // needed. AutoKeepPropMapTables can be used to avoid discarding tables in a
    131 // particular zone. Methods to access a PropMapTable take either an
    132 // AutoCheckCannotGC or AutoKeepPropMapTables argument, to help ensure tables
    133 // are not purged while we're using them.
    134 //
    135 // Shared Property Maps
    136 // --------------------
    137 // Shared property maps can be shared per-Zone by objects with the same property
    138 // keys, flags, and slot numbers. To make this work, shared maps form a tree:
    139 //
    140 // - Each Zone has a table that maps from first PropertyKey + PropertyInfo to
    141 //   a SharedPropMap that begins with that property. This is used to lookup the
    142 //   the map to use when adding the first property.
    143 //   See ShapeZone::initialPropMaps.
    144 //
    145 // - When adding a property other than the first one, the property is stored in
    146 //   the next entry of the same map when possible. If the map is full or the
    147 //   next entry already stores a different property, a child map is created and
    148 //   linked to the parent map.
    149 //
    150 // For example, imagine we want to create these objects:
    151 //
    152 //   o1 = {x: 1, y: 2, z: 3}
    153 //   o2 = {x: 1, y: 2, foo: 4}
    154 //
    155 // This will result in the following maps being created:
    156 //
    157 //     +---------------------+    +---------------------+
    158 //     | SharedPropMap M1    |    | SharedPropMap M2    |
    159 //     +---------------------+    +---------------------+
    160 //     | Child M2 (index 1) -+--> | Parent M1 (index 1) |
    161 //     +---------------------+    +---------------------+
    162 //     | 0: x                |    | 0: x                |
    163 //     | 1: y                |    | 1: y                |
    164 //     | 2: z                |    | 2: foo              |
    165 //     | ...                 |    | ...                 |
    166 //     +---------------------+    +---------------------+
    167 //
    168 // M1 is the map used for initial property "x". Properties "y" and "z" can be
    169 // stored inline. When later adding "foo" following "y", the map has to be
    170 // forked: a child map M2 is created and M1 remembers this transition at
    171 // property index 1 so that M2 will be used the next time properties "x", "y",
    172 // and "foo" are added to an object.
    173 //
    174 // Shared maps contain a TreeData struct that stores the parent and children
    175 // links for the SharedPropMap tree. The parent link is a tagged pointer that
    176 // stores both the parent map and the property index of the last used property
    177 // in the parent map before the branch. The children are stored similarly: the
    178 // parent map can store a single child map and index, or a set of children.
    179 // See SharedChildrenPtr.
    180 //
    181 // Looking up a child map can then be done based on the index of the last
    182 // property in the parent map and the new property's key and flags. So for the
    183 // example above, the lookup key for M1 => M2 is (index 1, "foo", <flags>).
    184 //
    185 // Note: shared maps can have both a |previous| map and a |parent| map. They are
    186 // equal when the previous map was full, but can be different maps when
    187 // branching in the middle of a map like in the example above: M2 has parent M1
    188 // but does not have a |previous| map (because it only has three properties).
    189 //
    190 // Dictionary Property Maps
    191 // ------------------------
    192 // Certain operations can't be implemented (efficiently) for shared property
    193 // maps, for example changing or deleting a property other than the last one.
    194 // When this happens the map is copied as a DictionaryPropMap.
    195 //
    196 // Dictionary maps are unshared so can be mutated in place (after generating a
    197 // new shape for the object).
    198 //
    199 // Unlike shared maps, dictionary maps can have holes between two property keys
    200 // after removing a property. When there are more holes than properties, the
    201 // map is compacted. See DictionaryPropMap::maybeCompact.
    202 
    203 namespace js {
    204 
    205 enum class IntegrityLevel;
    206 
    207 class DictionaryPropMap;
    208 class SharedPropMap;
    209 class LinkedPropMap;
    210 class CompactPropMap;
    211 class NormalPropMap;
    212 
    213 class JS_PUBLIC_API GenericPrinter;
    214 class JSONPrinter;
    215 
    216 // Template class for storing a PropMap* and a property index as tagged pointer.
    217 template <typename T>
    218 class MapAndIndex {
    219  uintptr_t data_ = 0;
    220 
    221  static constexpr uintptr_t IndexMask = 0b111;
    222 
    223 public:
    224  MapAndIndex() = default;
    225 
    226  MapAndIndex(const T* map, uint32_t index) : data_(uintptr_t(map) | index) {
    227    MOZ_ASSERT((uintptr_t(map) & IndexMask) == 0);
    228    MOZ_ASSERT(index <= IndexMask);
    229  }
    230  explicit MapAndIndex(uintptr_t data) : data_(data) {}
    231 
    232  void setNone() { data_ = 0; }
    233 
    234  bool isNone() const { return data_ == 0; }
    235 
    236  uintptr_t raw() const { return data_; }
    237  T* maybeMap() const { return reinterpret_cast<T*>(data_ & ~IndexMask); }
    238 
    239  uint32_t index() const {
    240    MOZ_ASSERT(!isNone());
    241    return data_ & IndexMask;
    242  }
    243  T* map() const {
    244    MOZ_ASSERT(!isNone());
    245    return maybeMap();
    246  }
    247 
    248  inline PropertyInfo propertyInfo() const;
    249 
    250  bool operator==(const MapAndIndex<T>& other) const {
    251    return data_ == other.data_;
    252  }
    253  bool operator!=(const MapAndIndex<T>& other) const {
    254    return !operator==(other);
    255  }
    256 } JS_HAZ_GC_POINTER;
    257 using PropMapAndIndex = MapAndIndex<PropMap>;
    258 using SharedPropMapAndIndex = MapAndIndex<SharedPropMap>;
    259 
    260 struct SharedChildrenHasher;
    261 using SharedChildrenSet =
    262    HashSet<SharedPropMapAndIndex, SharedChildrenHasher, SystemAllocPolicy>;
    263 
    264 // Children of shared maps. This is either:
    265 //
    266 // - None (no children)
    267 // - SingleMapAndIndex (one child map, including the property index of the last
    268 //   property before the branch)
    269 // - SharedChildrenSet (multiple children)
    270 //
    271 // Because SingleMapAndIndex use all bits, this relies on the HasChildrenSet
    272 // flag in the map to distinguish the latter two cases.
    273 class SharedChildrenPtr {
    274  uintptr_t data_ = 0;
    275 
    276 public:
    277  bool isNone() const { return data_ == 0; }
    278  void setNone() { data_ = 0; }
    279 
    280  void setSingleChild(SharedPropMapAndIndex child) { data_ = child.raw(); }
    281  void setChildrenSet(SharedChildrenSet* set) { data_ = uintptr_t(set); }
    282 
    283  SharedPropMapAndIndex toSingleChild() const {
    284    MOZ_ASSERT(!isNone());
    285    return SharedPropMapAndIndex(data_);
    286  }
    287 
    288  SharedChildrenSet* toChildrenSet() const {
    289    MOZ_ASSERT(!isNone());
    290    return reinterpret_cast<SharedChildrenSet*>(data_);
    291  }
    292 } JS_HAZ_GC_POINTER;
    293 
    294 // Ensures no property map tables are purged in the current zone.
    295 class MOZ_RAII AutoKeepPropMapTables {
    296  JSContext* cx_;
    297  bool prev_;
    298 
    299 public:
    300  void operator=(const AutoKeepPropMapTables&) = delete;
    301  AutoKeepPropMapTables(const AutoKeepPropMapTables&) = delete;
    302  explicit inline AutoKeepPropMapTables(JSContext* cx);
    303  inline ~AutoKeepPropMapTables();
    304 };
    305 
    306 // Hash table to optimize property lookups on larger maps. This maps from
    307 // PropertyKey to PropMapAndIndex.
    308 class PropMapTable {
    309  struct Hasher {
    310    using Key = PropMapAndIndex;
    311    using Lookup = PropertyKey;
    312    static MOZ_ALWAYS_INLINE HashNumber hash(PropertyKey key);
    313    static MOZ_ALWAYS_INLINE bool match(PropMapAndIndex, PropertyKey key);
    314  };
    315 
    316  // Small lookup cache. This has a hit rate of 30-60% on most workloads and is
    317  // a lot faster than the full HashSet lookup.
    318  struct CacheEntry {
    319    PropertyKey key;
    320    PropMapAndIndex result;
    321  };
    322  static constexpr uint32_t NumCacheEntries = 2;
    323  CacheEntry cacheEntries_[NumCacheEntries];
    324 
    325  using Set = HashSet<PropMapAndIndex, Hasher, SystemAllocPolicy>;
    326  Set set_;
    327 
    328  void setCacheEntry(PropertyKey key, PropMapAndIndex entry) {
    329    for (uint32_t i = 0; i < NumCacheEntries; i++) {
    330      if (cacheEntries_[i].key == key) {
    331        cacheEntries_[i].result = entry;
    332        return;
    333      }
    334    }
    335  }
    336  bool lookupInCache(PropertyKey key, PropMapAndIndex* result) const {
    337    for (uint32_t i = 0; i < NumCacheEntries; i++) {
    338      if (cacheEntries_[i].key == key) {
    339        *result = cacheEntries_[i].result;
    340 #ifdef DEBUG
    341        auto p = lookupRaw(key);
    342        MOZ_ASSERT(*result == (p ? *p : PropMapAndIndex()));
    343 #endif
    344        return true;
    345      }
    346    }
    347    return false;
    348  }
    349  void addToCache(PropertyKey key, Set::Ptr p) {
    350    for (uint32_t i = NumCacheEntries - 1; i > 0; i--) {
    351      cacheEntries_[i] = cacheEntries_[i - 1];
    352      MOZ_ASSERT(cacheEntries_[i].key != key);
    353    }
    354    cacheEntries_[0].key = key;
    355    cacheEntries_[0].result = p ? *p : PropMapAndIndex();
    356  }
    357 
    358 public:
    359  using Ptr = Set::Ptr;
    360 
    361  PropMapTable() = default;
    362  ~PropMapTable() = default;
    363 
    364  uint32_t entryCount() const { return set_.count(); }
    365 
    366  // This counts the PropMapTable object itself (which must be heap-allocated)
    367  // and its HashSet.
    368  size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
    369    return mallocSizeOf(this) + set_.shallowSizeOfExcludingThis(mallocSizeOf);
    370  }
    371 
    372  // init() is fallible and reports OOM to the context.
    373  bool init(JSContext* cx, LinkedPropMap* map);
    374 
    375  MOZ_ALWAYS_INLINE PropMap* lookup(PropMap* map, uint32_t mapLength,
    376                                    PropertyKey key, uint32_t* index);
    377 
    378  Set::Ptr lookupRaw(PropertyKey key) const { return set_.lookup(key); }
    379 #ifdef DEBUG
    380  Set::Ptr readonlyThreadsafeLookup(PropertyKey key) const {
    381    return set_.readonlyThreadsafeLookup(key);
    382  }
    383 #endif
    384 
    385  bool add(JSContext* cx, PropertyKey key, PropMapAndIndex entry) {
    386    if (!set_.putNew(key, entry)) {
    387      ReportOutOfMemory(cx);
    388      return false;
    389    }
    390    setCacheEntry(key, entry);
    391    return true;
    392  }
    393 
    394  void purgeCache() {
    395    for (uint32_t i = 0; i < NumCacheEntries; i++) {
    396      cacheEntries_[i] = CacheEntry();
    397    }
    398  }
    399 
    400  void remove(Ptr ptr) {
    401    set_.remove(ptr);
    402    purgeCache();
    403  }
    404 
    405  void replaceEntry(Ptr ptr, PropertyKey key, PropMapAndIndex newEntry) {
    406    MOZ_ASSERT(*ptr != newEntry);
    407    set_.replaceKey(ptr, key, newEntry);
    408    setCacheEntry(key, newEntry);
    409  }
    410 
    411  void trace(JSTracer* trc);
    412 #ifdef JSGC_HASH_TABLE_CHECKS
    413  void checkAfterMovingGC(JS::Zone* zone);
    414 #endif
    415 };
    416 
    417 class PropMap : public gc::TenuredCellWithFlags {
    418 public:
    419  // Number of properties that can be stored in each map. This must be small
    420  // enough so that every index fits in a tagged PropMap* pointer (MapAndIndex).
    421  static constexpr size_t Capacity = 8;
    422 
    423 protected:
    424  static_assert(gc::CellFlagBitsReservedForGC == 3,
    425                "PropMap must reserve enough bits for Cell");
    426 
    427  enum Flags {
    428    // Set if this is a CompactPropMap.
    429    IsCompactFlag = 1 << 3,
    430 
    431    // Set if this map has a non-null previous map pointer. Never set for
    432    // compact maps because they don't have a previous field.
    433    HasPrevFlag = 1 << 4,
    434 
    435    // Set if this is a DictionaryPropMap.
    436    IsDictionaryFlag = 1 << 5,
    437 
    438    // Set if this map can have a table. Never set for compact maps. Always set
    439    // for dictionary maps.
    440    CanHaveTableFlag = 1 << 6,
    441 
    442    // If set, this SharedPropMap has a SharedChildrenSet. Else it either has no
    443    // children or a single child. See SharedChildrenPtr. Never set for
    444    // dictionary maps.
    445    HasChildrenSetFlag = 1 << 7,
    446 
    447    // If set, this SharedPropMap was once converted to dictionary mode. This is
    448    // only used for heuristics. Never set for dictionary maps.
    449    HadDictionaryConversionFlag = 1 << 8,
    450 
    451    // For SharedPropMap this stores the number of previous maps, clamped to
    452    // NumPreviousMapsMax. This is used for heuristics.
    453    NumPreviousMapsMax = 0x7f,
    454    NumPreviousMapsShift = 9,
    455    NumPreviousMapsMask = NumPreviousMapsMax << NumPreviousMapsShift,
    456  };
    457 
    458  template <typename KnownF, typename UnknownF>
    459  static void forEachPropMapFlag(uintptr_t flags, KnownF known,
    460                                 UnknownF unknown);
    461 
    462  // Flags word, stored in the cell header. Note that this hides the
    463  // Cell::flags() method.
    464  uintptr_t flags() const { return headerFlagsField(); }
    465 
    466 private:
    467  GCPtr<PropertyKey> keys_[Capacity];
    468 
    469 protected:
    470  PropMap() = default;
    471 
    472  void initKey(uint32_t index, PropertyKey key) {
    473    MOZ_ASSERT(index < Capacity);
    474    keys_[index].init(key);
    475  }
    476  void setKey(uint32_t index, PropertyKey key) {
    477    MOZ_ASSERT(index < Capacity);
    478    keys_[index] = key;
    479  }
    480 
    481 public:
    482  bool isCompact() const { return flags() & IsCompactFlag; }
    483  bool isLinked() const { return !isCompact(); }
    484  bool isDictionary() const { return flags() & IsDictionaryFlag; }
    485  bool isShared() const { return !isDictionary(); }
    486  bool isNormal() const { return isShared() && !isCompact(); }
    487 
    488  bool hasPrevious() const { return flags() & HasPrevFlag; }
    489  bool canHaveTable() const { return flags() & CanHaveTableFlag; }
    490 
    491  inline CompactPropMap* asCompact();
    492  inline const CompactPropMap* asCompact() const;
    493 
    494  inline LinkedPropMap* asLinked();
    495  inline const LinkedPropMap* asLinked() const;
    496 
    497  inline NormalPropMap* asNormal();
    498  inline const NormalPropMap* asNormal() const;
    499 
    500  inline SharedPropMap* asShared();
    501  inline const SharedPropMap* asShared() const;
    502 
    503  inline DictionaryPropMap* asDictionary();
    504  inline const DictionaryPropMap* asDictionary() const;
    505 
    506  bool hasKey(uint32_t index) const {
    507    MOZ_ASSERT(index < Capacity);
    508    return !keys_[index].isVoid();
    509  }
    510  PropertyKey getKey(uint32_t index) const {
    511    MOZ_ASSERT(index < Capacity);
    512    return keys_[index];
    513  }
    514 
    515  uint32_t approximateEntryCount() const;
    516 
    517 #if defined(DEBUG) || defined(JS_JITSPEW)
    518  void dump() const;
    519  void dump(js::GenericPrinter& out) const;
    520  void dump(js::JSONPrinter& json) const;
    521 
    522  void dumpFields(js::JSONPrinter& json) const;
    523  void dumpFieldsAt(js::JSONPrinter& json, uint32_t index) const;
    524  void dumpDescriptorStringContentAt(js::GenericPrinter& out,
    525                                     uint32_t index) const;
    526  JS::UniqueChars getPropertyNameAt(uint32_t index) const;
    527 #endif
    528 
    529 #ifdef DEBUG
    530  void checkConsistency(NativeObject* obj) const;
    531 #endif
    532 
    533  void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf,
    534                              size_t* children, size_t* tables) const;
    535 
    536  inline PropertyInfo getPropertyInfo(uint32_t index) const;
    537 
    538  PropertyInfoWithKey getPropertyInfoWithKey(uint32_t index) const {
    539    return PropertyInfoWithKey(getPropertyInfo(index), getKey(index));
    540  }
    541 
    542  MOZ_ALWAYS_INLINE PropMap* lookupLinear(uint32_t mapLength, PropertyKey key,
    543                                          uint32_t* index);
    544 
    545  MOZ_ALWAYS_INLINE PropMap* lookupPure(uint32_t mapLength, PropertyKey key,
    546                                        uint32_t* index);
    547 
    548  MOZ_ALWAYS_INLINE PropMap* lookup(JSContext* cx, uint32_t mapLength,
    549                                    PropertyKey key, uint32_t* index);
    550 
    551  static inline bool lookupForRemove(JSContext* cx, PropMap* map,
    552                                     uint32_t mapLength, PropertyKey key,
    553                                     const AutoKeepPropMapTables& keep,
    554                                     PropMap** propMap, uint32_t* propIndex,
    555                                     PropMapTable** table,
    556                                     PropMapTable::Ptr* ptr);
    557 
    558  static const JS::TraceKind TraceKind = JS::TraceKind::PropMap;
    559 
    560  void traceChildren(JSTracer* trc);
    561 };
    562 
    563 class SharedPropMap : public PropMap {
    564  friend class PropMap;
    565 
    566 protected:
    567  // Shared maps are stored in a tree structure. Each shared map has a TreeData
    568  // struct linking the map to its parent and children. Initial maps (the ones
    569  // stored in ShapeZone's initialPropMaps table) don't have a parent.
    570  struct TreeData {
    571    SharedChildrenPtr children;
    572    SharedPropMapAndIndex parent;
    573 
    574    void setParent(SharedPropMap* map, uint32_t index) {
    575      parent = SharedPropMapAndIndex(map, index);
    576    }
    577  };
    578 
    579 private:
    580  static SharedPropMap* create(JSContext* cx, Handle<SharedPropMap*> prev,
    581                               HandleId id, PropertyInfo prop);
    582  static SharedPropMap* createInitial(JSContext* cx, HandleId id,
    583                                      PropertyInfo prop);
    584  static SharedPropMap* clone(JSContext* cx, Handle<SharedPropMap*> map,
    585                              uint32_t length);
    586 
    587  inline void initProperty(uint32_t index, PropertyKey key, PropertyInfo prop);
    588 
    589  static bool addPropertyInternal(JSContext* cx,
    590                                  MutableHandle<SharedPropMap*> map,
    591                                  uint32_t* mapLength, HandleId id,
    592                                  PropertyInfo prop);
    593 
    594  bool addChild(JSContext* cx, SharedPropMapAndIndex child, HandleId id,
    595                PropertyInfo prop);
    596  SharedPropMap* lookupChild(uint32_t length, HandleId id, PropertyInfo prop);
    597 
    598 protected:
    599  void initNumPreviousMaps(uint32_t value) {
    600    MOZ_ASSERT((flags() >> NumPreviousMapsShift) == 0);
    601    // Clamp to NumPreviousMapsMax. This is okay because this value is only used
    602    // for heuristics.
    603    if (value > NumPreviousMapsMax) {
    604      value = NumPreviousMapsMax;
    605    }
    606    setHeaderFlagBits(value << NumPreviousMapsShift);
    607  }
    608 
    609  bool hasChildrenSet() const { return flags() & HasChildrenSetFlag; }
    610  void setHasChildrenSet() { setHeaderFlagBits(HasChildrenSetFlag); }
    611  void clearHasChildrenSet() { clearHeaderFlagBits(HasChildrenSetFlag); }
    612 
    613  void setHadDictionaryConversion() {
    614    setHeaderFlagBits(HadDictionaryConversionFlag);
    615  }
    616 
    617 public:
    618  // Heuristics used when adding a property via NativeObject::addProperty and
    619  // friends:
    620  //
    621  // * If numPreviousMaps >= NumPrevMapsForAddConsiderDictionary, consider
    622  //   converting the object to a dictionary object based on other heuristics.
    623  //
    624  // * If numPreviousMaps >= NumPrevMapsForAddAlwaysDictionary, always convert
    625  //   the object to a dictionary object.
    626  static constexpr size_t NumPrevMapsConsiderDictionary = 32;
    627  static constexpr size_t NumPrevMapsAlwaysDictionary = 100;
    628 
    629  static_assert(NumPrevMapsConsiderDictionary < NumPreviousMapsMax);
    630  static_assert(NumPrevMapsAlwaysDictionary < NumPreviousMapsMax);
    631 
    632  // The number of properties that can definitely be added to an object without
    633  // triggering dictionary mode conversion in NativeObject::addProperty.
    634  static constexpr size_t MaxPropsForNonDictionary =
    635      NumPrevMapsConsiderDictionary * Capacity;
    636 
    637  bool isDictionary() const = delete;
    638  bool isShared() const = delete;
    639  SharedPropMap* asShared() = delete;
    640  const SharedPropMap* asShared() const = delete;
    641 
    642  bool hadDictionaryConversion() const {
    643    return flags() & HadDictionaryConversionFlag;
    644  }
    645 
    646  uint32_t numPreviousMaps() const {
    647    uint32_t val = (flags() & NumPreviousMapsMask) >> NumPreviousMapsShift;
    648    MOZ_ASSERT_IF(hasPrevious(), val > 0);
    649    return val;
    650  }
    651 
    652  MOZ_ALWAYS_INLINE bool shouldConvertToDictionaryForAdd() const;
    653 
    654  void fixupAfterMovingGC();
    655  inline void sweep(JS::GCContext* gcx);
    656  inline void finalize(JS::GCContext* gcx);
    657 
    658  static inline void getPrevious(MutableHandle<SharedPropMap*> map,
    659                                 uint32_t* mapLength);
    660 
    661  bool matchProperty(uint32_t index, PropertyKey key, PropertyInfo prop) const {
    662    return getKey(index) == key && getPropertyInfo(index) == prop;
    663  }
    664 
    665  inline TreeData& treeDataRef();
    666  inline const TreeData& treeDataRef() const;
    667 
    668  void removeChild(JS::GCContext* gcx, SharedPropMap* child);
    669 
    670  uint32_t lastUsedSlot(uint32_t mapLength) const {
    671    return getPropertyInfo(mapLength - 1).maybeSlot();
    672  }
    673 
    674  // Number of slots required for objects with this map/mapLength.
    675  static uint32_t slotSpan(const JSClass* clasp, const SharedPropMap* map,
    676                           uint32_t mapLength) {
    677    MOZ_ASSERT(clasp->isNativeObject());
    678    uint32_t numReserved = JSCLASS_RESERVED_SLOTS(clasp);
    679    if (!map) {
    680      MOZ_ASSERT(mapLength == 0);
    681      return numReserved;
    682    }
    683    uint32_t lastSlot = map->lastUsedSlot(mapLength);
    684    if (lastSlot == SHAPE_INVALID_SLOT) {
    685      // The object only has custom data properties.
    686      return numReserved;
    687    }
    688    // Some builtin objects store properties in reserved slots. Make sure the
    689    // slot span >= numReserved. See addPropertyInReservedSlot.
    690    return std::max(lastSlot + 1, numReserved);
    691  }
    692 
    693  static uint32_t indexOfNextProperty(uint32_t index) {
    694    MOZ_ASSERT(index < PropMap::Capacity);
    695    return (index + 1) % PropMap::Capacity;
    696  }
    697 
    698  // Add a new property to this map. Returns the new map/mapLength, slot number,
    699  // and object flags.
    700  static bool addProperty(JSContext* cx, const JSClass* clasp,
    701                          MutableHandle<SharedPropMap*> map,
    702                          uint32_t* mapLength, HandleId id, PropertyFlags flags,
    703                          ObjectFlags* objectFlags, uint32_t* slot);
    704 
    705  // Like addProperty, but for when the slot number is a reserved slot. A few
    706  // builtin objects use this for initial properties.
    707  static bool addPropertyInReservedSlot(JSContext* cx, const JSClass* clasp,
    708                                        MutableHandle<SharedPropMap*> map,
    709                                        uint32_t* mapLength, HandleId id,
    710                                        PropertyFlags flags, uint32_t slot,
    711                                        ObjectFlags* objectFlags);
    712 
    713  // Like addProperty, but for when the caller already knows the slot number to
    714  // use (or wants to assert this exact slot number is used).
    715  static bool addPropertyWithKnownSlot(JSContext* cx, const JSClass* clasp,
    716                                       MutableHandle<SharedPropMap*> map,
    717                                       uint32_t* mapLength, HandleId id,
    718                                       PropertyFlags flags, uint32_t slot,
    719                                       ObjectFlags* objectFlags);
    720 
    721  // Like addProperty, but for adding a custom data property.
    722  static bool addCustomDataProperty(JSContext* cx, const JSClass* clasp,
    723                                    MutableHandle<SharedPropMap*> map,
    724                                    uint32_t* mapLength, HandleId id,
    725                                    PropertyFlags flags,
    726                                    ObjectFlags* objectFlags);
    727 
    728  // Freeze or seal all properties by creating a new shared map. Returns the new
    729  // map and object flags.
    730  static bool freezeOrSealProperties(JSContext* cx, IntegrityLevel level,
    731                                     const JSClass* clasp,
    732                                     MutableHandle<SharedPropMap*> map,
    733                                     uint32_t mapLength,
    734                                     ObjectFlags* objectFlags);
    735 
    736  // Create a new dictionary map as copy of this map.
    737  static DictionaryPropMap* toDictionaryMap(JSContext* cx,
    738                                            Handle<SharedPropMap*> map,
    739                                            uint32_t length);
    740 
    741 #if defined(DEBUG) || defined(JS_JITSPEW)
    742  void dumpOwnFields(js::JSONPrinter& json) const;
    743 #endif
    744 };
    745 
    746 class CompactPropMap final : public SharedPropMap {
    747  CompactPropertyInfo propInfos_[Capacity];
    748  TreeData treeData_;
    749 
    750  friend class PropMap;
    751  friend class SharedPropMap;
    752  friend class DictionaryPropMap;
    753  friend class js::gc::CellAllocator;
    754 
    755  CompactPropMap(JS::Handle<PropertyKey> key, PropertyInfo prop) {
    756    setHeaderFlagBits(IsCompactFlag);
    757    initProperty(0, key, prop);
    758  }
    759 
    760  CompactPropMap(JS::Handle<CompactPropMap*> orig, uint32_t length) {
    761    setHeaderFlagBits(IsCompactFlag);
    762    for (uint32_t i = 0; i < length; i++) {
    763      initKey(i, orig->getKey(i));
    764      propInfos_[i] = orig->propInfos_[i];
    765    }
    766  }
    767 
    768  void initProperty(uint32_t index, PropertyKey key, PropertyInfo prop) {
    769    MOZ_ASSERT(!hasKey(index));
    770    initKey(index, key);
    771    propInfos_[index] = CompactPropertyInfo(prop);
    772  }
    773 
    774  TreeData& treeDataRef() { return treeData_; }
    775  const TreeData& treeDataRef() const { return treeData_; }
    776 
    777 public:
    778  bool isDictionary() const = delete;
    779  bool isShared() const = delete;
    780  bool isCompact() const = delete;
    781  bool isNormal() const = delete;
    782  bool isLinked() const = delete;
    783  CompactPropMap* asCompact() = delete;
    784  const CompactPropMap* asCompact() const = delete;
    785 
    786  PropertyInfo getPropertyInfo(uint32_t index) const {
    787    MOZ_ASSERT(hasKey(index));
    788    return PropertyInfo(propInfos_[index]);
    789  }
    790 };
    791 
    792 // Layout shared by NormalPropMap and DictionaryPropMap.
    793 class LinkedPropMap final : public PropMap {
    794  friend class PropMap;
    795  friend class SharedPropMap;
    796  friend class NormalPropMap;
    797  friend class DictionaryPropMap;
    798 
    799  struct Data {
    800    GCPtr<PropMap*> previous;
    801    PropMapTable* table = nullptr;
    802    PropertyInfo propInfos[Capacity];
    803 
    804    explicit Data(PropMap* prev) : previous(prev) {}
    805  };
    806  Data data_;
    807 
    808  bool createTable(JSContext* cx);
    809  void handOffTableTo(LinkedPropMap* next);
    810 
    811 public:
    812  bool isCompact() const = delete;
    813  bool isLinked() const = delete;
    814  LinkedPropMap* asLinked() = delete;
    815  const LinkedPropMap* asLinked() const = delete;
    816 
    817  PropMap* previous() const { return data_.previous; }
    818 
    819  bool hasTable() const { return data_.table != nullptr; }
    820 
    821  PropMapTable* maybeTable(JS::AutoCheckCannotGC& nogc) const {
    822    return data_.table;
    823  }
    824  PropMapTable* ensureTable(JSContext* cx, const JS::AutoCheckCannotGC& nogc) {
    825    if (!data_.table && MOZ_UNLIKELY(!createTable(cx))) {
    826      return nullptr;
    827    }
    828    return data_.table;
    829  }
    830  PropMapTable* ensureTable(JSContext* cx, const AutoKeepPropMapTables& keep) {
    831    if (!data_.table && MOZ_UNLIKELY(!createTable(cx))) {
    832      return nullptr;
    833    }
    834    return data_.table;
    835  }
    836 
    837  void purgeTable(JS::GCContext* gcx);
    838 
    839  void purgeTableCache() {
    840    if (data_.table) {
    841      data_.table->purgeCache();
    842    }
    843  }
    844 
    845 #ifdef DEBUG
    846  bool canSkipMarkingTable();
    847 #endif
    848 
    849  PropertyInfo getPropertyInfo(uint32_t index) const {
    850    MOZ_ASSERT(hasKey(index));
    851    return data_.propInfos[index];
    852  }
    853 
    854 #if defined(DEBUG) || defined(JS_JITSPEW)
    855  void dumpOwnFields(js::JSONPrinter& json) const;
    856 #endif
    857 };
    858 
    859 class NormalPropMap final : public SharedPropMap {
    860  friend class PropMap;
    861  friend class SharedPropMap;
    862  friend class DictionaryPropMap;
    863  friend class js::gc::CellAllocator;
    864 
    865  LinkedPropMap::Data linkedData_;
    866  TreeData treeData_;
    867 
    868  NormalPropMap(JS::Handle<SharedPropMap*> prev, PropertyKey key,
    869                PropertyInfo prop)
    870      : linkedData_(prev) {
    871    if (prev) {
    872      setHeaderFlagBits(HasPrevFlag);
    873      initNumPreviousMaps(prev->numPreviousMaps() + 1);
    874      if (prev->hasPrevious()) {
    875        setHeaderFlagBits(CanHaveTableFlag);
    876      }
    877    }
    878    initProperty(0, key, prop);
    879  }
    880 
    881  NormalPropMap(JS::Handle<NormalPropMap*> orig, uint32_t length)
    882      : linkedData_(orig->previous()) {
    883    if (orig->hasPrevious()) {
    884      setHeaderFlagBits(HasPrevFlag);
    885    }
    886    if (orig->canHaveTable()) {
    887      setHeaderFlagBits(CanHaveTableFlag);
    888    }
    889    initNumPreviousMaps(orig->numPreviousMaps());
    890    for (uint32_t i = 0; i < length; i++) {
    891      initProperty(i, orig->getKey(i), orig->getPropertyInfo(i));
    892    }
    893  }
    894 
    895  void initProperty(uint32_t index, PropertyKey key, PropertyInfo prop) {
    896    MOZ_ASSERT(!hasKey(index));
    897    initKey(index, key);
    898    linkedData_.propInfos[index] = prop;
    899  }
    900 
    901  bool isDictionary() const = delete;
    902  bool isShared() const = delete;
    903  bool isCompact() const = delete;
    904  bool isNormal() const = delete;
    905  bool isLinked() const = delete;
    906  NormalPropMap* asNormal() = delete;
    907  const NormalPropMap* asNormal() const = delete;
    908 
    909  SharedPropMap* previous() const {
    910    return static_cast<SharedPropMap*>(linkedData_.previous.get());
    911  }
    912 
    913  TreeData& treeDataRef() { return treeData_; }
    914  const TreeData& treeDataRef() const { return treeData_; }
    915 
    916  static void staticAsserts() {
    917    static_assert(offsetof(NormalPropMap, linkedData_) ==
    918                  offsetof(LinkedPropMap, data_));
    919  }
    920 };
    921 
    922 class DictionaryPropMap final : public PropMap {
    923  friend class PropMap;
    924  friend class SharedPropMap;
    925  friend class js::gc::CellAllocator;
    926 
    927  LinkedPropMap::Data linkedData_;
    928 
    929  // SHAPE_INVALID_SLOT or head of slot freelist in owning dictionary-mode
    930  // object.
    931  uint32_t freeList_ = SHAPE_INVALID_SLOT;
    932 
    933  // Number of holes for removed properties in this and previous maps. Used by
    934  // compacting heuristics.
    935  uint32_t holeCount_ = 0;
    936 
    937  // Empty Dictionary prop map used during reshape.
    938  explicit DictionaryPropMap(std::nullptr_t) : linkedData_(nullptr) {
    939    setHeaderFlagBits(IsDictionaryFlag | CanHaveTableFlag);
    940  }
    941 
    942  DictionaryPropMap(JS::Handle<DictionaryPropMap*> prev, PropertyKey key,
    943                    PropertyInfo prop)
    944      : linkedData_(prev) {
    945    setHeaderFlagBits(IsDictionaryFlag | CanHaveTableFlag |
    946                      (prev ? HasPrevFlag : 0));
    947    initProperty(0, key, prop);
    948  }
    949 
    950  DictionaryPropMap(JS::Handle<NormalPropMap*> orig, uint32_t length)
    951      : linkedData_(nullptr) {
    952    setHeaderFlagBits(IsDictionaryFlag | CanHaveTableFlag);
    953    for (uint32_t i = 0; i < length; i++) {
    954      initProperty(i, orig->getKey(i), orig->getPropertyInfo(i));
    955    }
    956  }
    957 
    958  DictionaryPropMap(JS::Handle<CompactPropMap*> orig, uint32_t length)
    959      : linkedData_(nullptr) {
    960    setHeaderFlagBits(IsDictionaryFlag | CanHaveTableFlag);
    961    for (uint32_t i = 0; i < length; i++) {
    962      initProperty(i, orig->getKey(i), orig->getPropertyInfo(i));
    963    }
    964  }
    965 
    966  void initProperty(uint32_t index, PropertyKey key, PropertyInfo prop) {
    967    MOZ_ASSERT(!hasKey(index));
    968    initKey(index, key);
    969    linkedData_.propInfos[index] = prop;
    970  }
    971 
    972  void initPrevious(DictionaryPropMap* prev) {
    973    MOZ_ASSERT(prev);
    974    linkedData_.previous.init(prev);
    975    setHeaderFlagBits(HasPrevFlag);
    976  }
    977  void clearPrevious() {
    978    linkedData_.previous = nullptr;
    979    clearHeaderFlagBits(HasPrevFlag);
    980  }
    981 
    982  void clearProperty(uint32_t index) { setKey(index, PropertyKey::Void()); }
    983 
    984  static void skipTrailingHoles(MutableHandle<DictionaryPropMap*> map,
    985                                uint32_t* mapLength);
    986 
    987  void handOffLastMapStateTo(DictionaryPropMap* newLast);
    988 
    989  void incHoleCount() { holeCount_++; }
    990  void decHoleCount() {
    991    MOZ_ASSERT(holeCount_ > 0);
    992    holeCount_--;
    993  }
    994  static void maybeCompact(JSContext* cx, MutableHandle<DictionaryPropMap*> map,
    995                           uint32_t* mapLength);
    996 
    997 public:
    998  bool isDictionary() const = delete;
    999  bool isShared() const = delete;
   1000  bool isCompact() const = delete;
   1001  bool isNormal() const = delete;
   1002  bool isLinked() const = delete;
   1003  DictionaryPropMap* asDictionary() = delete;
   1004  const DictionaryPropMap* asDictionary() const = delete;
   1005 
   1006  void fixupAfterMovingGC() {}
   1007  inline void finalize(JS::GCContext* gcx);
   1008 
   1009  DictionaryPropMap* previous() const {
   1010    return static_cast<DictionaryPropMap*>(linkedData_.previous.get());
   1011  }
   1012 
   1013  uint32_t freeList() const { return freeList_; }
   1014  void setFreeList(uint32_t slot) { freeList_ = slot; }
   1015 
   1016  PropertyInfo getPropertyInfo(uint32_t index) const {
   1017    MOZ_ASSERT(hasKey(index));
   1018    return linkedData_.propInfos[index];
   1019  }
   1020 
   1021  // Create an empty prop map for dictionary mode teleporting
   1022  static DictionaryPropMap* createEmpty(JSContext* cx);
   1023 
   1024  // Add a new property to this map. Returns the new map/mapLength and object
   1025  // flags. The caller is responsible for generating a new dictionary shape.
   1026  static bool addProperty(JSContext* cx, const JSClass* clasp,
   1027                          MutableHandle<DictionaryPropMap*> map,
   1028                          uint32_t* mapLength, HandleId id, PropertyFlags flags,
   1029                          uint32_t slot, ObjectFlags* objectFlags);
   1030 
   1031  // Remove the property referenced by the table pointer. Returns the new
   1032  // map/mapLength. The caller is responsible for generating a new dictionary
   1033  // shape.
   1034  static void removeProperty(JSContext* cx,
   1035                             MutableHandle<DictionaryPropMap*> map,
   1036                             uint32_t* mapLength, PropMapTable* table,
   1037                             PropMapTable::Ptr& ptr);
   1038 
   1039  // Turn all sparse elements into dense elements. The caller is responsible
   1040  // for checking all sparse elements are plain data properties and must
   1041  // generate a new shape for the object.
   1042  static void densifyElements(JSContext* cx,
   1043                              MutableHandle<DictionaryPropMap*> map,
   1044                              uint32_t* mapLength, NativeObject* obj);
   1045 
   1046  // Freeze or seal all properties in this map. Returns the new object flags.
   1047  // The caller is responsible for generating a new shape for the object.
   1048  void freezeOrSealProperties(JSContext* cx, IntegrityLevel level,
   1049                              const JSClass* clasp, uint32_t mapLength,
   1050                              ObjectFlags* objectFlags);
   1051 
   1052  // Change a property's slot number and/or flags and return the new object
   1053  // flags. The caller is responsible for generating a new shape.
   1054  void changeProperty(JSContext* cx, const JSClass* clasp, uint32_t index,
   1055                      PropertyFlags flags, uint32_t slot,
   1056                      ObjectFlags* objectFlags);
   1057 
   1058  // Like changeProperty, but doesn't change the slot number.
   1059  void changePropertyFlags(JSContext* cx, const JSClass* clasp, uint32_t index,
   1060                           PropertyFlags flags, ObjectFlags* objectFlags) {
   1061    uint32_t slot = getPropertyInfo(index).maybeSlot();
   1062    changeProperty(cx, clasp, index, flags, slot, objectFlags);
   1063  }
   1064 
   1065  static void staticAsserts() {
   1066    static_assert(offsetof(DictionaryPropMap, linkedData_) ==
   1067                  offsetof(LinkedPropMap, data_));
   1068  }
   1069 
   1070 #if defined(DEBUG) || defined(JS_JITSPEW)
   1071  void dumpOwnFields(js::JSONPrinter& json) const;
   1072 #endif
   1073 };
   1074 
   1075 inline CompactPropMap* PropMap::asCompact() {
   1076  MOZ_ASSERT(isCompact());
   1077  return static_cast<CompactPropMap*>(this);
   1078 }
   1079 inline const CompactPropMap* PropMap::asCompact() const {
   1080  MOZ_ASSERT(isCompact());
   1081  return static_cast<const CompactPropMap*>(this);
   1082 }
   1083 inline LinkedPropMap* PropMap::asLinked() {
   1084  MOZ_ASSERT(isLinked());
   1085  return static_cast<LinkedPropMap*>(this);
   1086 }
   1087 inline const LinkedPropMap* PropMap::asLinked() const {
   1088  MOZ_ASSERT(isLinked());
   1089  return static_cast<const LinkedPropMap*>(this);
   1090 }
   1091 inline NormalPropMap* PropMap::asNormal() {
   1092  MOZ_ASSERT(isNormal());
   1093  return static_cast<NormalPropMap*>(this);
   1094 }
   1095 inline const NormalPropMap* PropMap::asNormal() const {
   1096  MOZ_ASSERT(isNormal());
   1097  return static_cast<const NormalPropMap*>(this);
   1098 }
   1099 inline SharedPropMap* PropMap::asShared() {
   1100  MOZ_ASSERT(isShared());
   1101  return static_cast<SharedPropMap*>(this);
   1102 }
   1103 inline const SharedPropMap* PropMap::asShared() const {
   1104  MOZ_ASSERT(isShared());
   1105  return static_cast<const SharedPropMap*>(this);
   1106 }
   1107 inline DictionaryPropMap* PropMap::asDictionary() {
   1108  MOZ_ASSERT(isDictionary());
   1109  return static_cast<DictionaryPropMap*>(this);
   1110 }
   1111 inline const DictionaryPropMap* PropMap::asDictionary() const {
   1112  MOZ_ASSERT(isDictionary());
   1113  return static_cast<const DictionaryPropMap*>(this);
   1114 }
   1115 
   1116 inline PropertyInfo PropMap::getPropertyInfo(uint32_t index) const {
   1117  return isCompact() ? asCompact()->getPropertyInfo(index)
   1118                     : asLinked()->getPropertyInfo(index);
   1119 }
   1120 
   1121 inline SharedPropMap::TreeData& SharedPropMap::treeDataRef() {
   1122  return isCompact() ? asCompact()->treeDataRef() : asNormal()->treeDataRef();
   1123 }
   1124 
   1125 inline const SharedPropMap::TreeData& SharedPropMap::treeDataRef() const {
   1126  return isCompact() ? asCompact()->treeDataRef() : asNormal()->treeDataRef();
   1127 }
   1128 
   1129 inline void SharedPropMap::initProperty(uint32_t index, PropertyKey key,
   1130                                        PropertyInfo prop) {
   1131  if (isCompact()) {
   1132    asCompact()->initProperty(index, key, prop);
   1133  } else {
   1134    asNormal()->initProperty(index, key, prop);
   1135  }
   1136 }
   1137 
   1138 template <typename T>
   1139 inline PropertyInfo MapAndIndex<T>::propertyInfo() const {
   1140  MOZ_ASSERT(!isNone());
   1141  return map()->getPropertyInfo(index());
   1142 }
   1143 
   1144 MOZ_ALWAYS_INLINE HashNumber PropMapTable::Hasher::hash(PropertyKey key) {
   1145  return HashPropertyKey(key);
   1146 }
   1147 MOZ_ALWAYS_INLINE bool PropMapTable::Hasher::match(PropMapAndIndex entry,
   1148                                                   PropertyKey key) {
   1149  MOZ_ASSERT(entry.map()->hasKey(entry.index()));
   1150  return entry.map()->getKey(entry.index()) == key;
   1151 }
   1152 
   1153 // Hash policy for SharedPropMap children.
   1154 struct SharedChildrenHasher {
   1155  using Key = SharedPropMapAndIndex;
   1156 
   1157  struct Lookup {
   1158    PropertyKey key;
   1159    PropertyInfo prop;
   1160    uint8_t index;
   1161 
   1162    Lookup(PropertyKey key, PropertyInfo prop, uint8_t index)
   1163        : key(key), prop(prop), index(index) {}
   1164    Lookup(PropertyInfoWithKey prop, uint8_t index)
   1165        : key(prop.key()), prop(prop), index(index) {}
   1166  };
   1167 
   1168  static HashNumber hash(const Lookup& l) {
   1169    HashNumber hash = HashPropertyKey(l.key);
   1170    return mozilla::AddToHash(hash, l.prop.toRaw(), l.index);
   1171  }
   1172  static bool match(SharedPropMapAndIndex k, const Lookup& l) {
   1173    SharedPropMap* map = k.map();
   1174    uint32_t index = k.index();
   1175    uint32_t newIndex = SharedPropMap::indexOfNextProperty(index);
   1176    return index == l.index && map->matchProperty(newIndex, l.key, l.prop);
   1177  }
   1178 };
   1179 
   1180 // An iterator that walks a shared prop map in property definition order
   1181 class MOZ_RAII SharedPropMapIter {
   1182 public:
   1183  // Iterate over all properties of propMap.
   1184  SharedPropMapIter(JSContext* cx, SharedPropMapAndIndex propMap);
   1185 
   1186  // Iterate over all properties after (but not including) startAfter.
   1187  SharedPropMapIter(JSContext* cx, SharedPropMapAndIndex startAfter,
   1188                    SharedPropMapAndIndex end);
   1189 
   1190  PropertyKey key() const {
   1191    MOZ_ASSERT(!done());
   1192    return maps_[mapIdx_]->getKey(propIdx_);
   1193  }
   1194  PropertyInfo prop() const {
   1195    MOZ_ASSERT(!done());
   1196    return maps_[mapIdx_]->getPropertyInfo(propIdx_);
   1197  }
   1198 
   1199  bool done() const { return mapIdx_ == 0 && propIdx_ > endIdx_; }
   1200  void next() {
   1201    MOZ_ASSERT(!done());
   1202    if (++propIdx_ == PropMap::Capacity && mapIdx_ > 0) {
   1203      propIdx_ = 0;
   1204      mapIdx_--;
   1205    }
   1206  }
   1207 
   1208 private:
   1209  SharedPropMapIter(JSContext* cx, mozilla::Maybe<SharedPropMapAndIndex> start,
   1210                    SharedPropMapAndIndex end);
   1211 
   1212  JS::RootedVector<SharedPropMap*> maps_;
   1213  uint32_t mapIdx_;
   1214  uint32_t propIdx_;
   1215  uint32_t endIdx_;
   1216 };
   1217 
   1218 }  // namespace js
   1219 
   1220 // JS::ubi::Nodes can point to PropMaps; they're js::gc::Cell instances
   1221 // with no associated compartment.
   1222 namespace JS {
   1223 namespace ubi {
   1224 
   1225 template <>
   1226 class Concrete<js::PropMap> : TracerConcrete<js::PropMap> {
   1227 protected:
   1228  explicit Concrete(js::PropMap* ptr) : TracerConcrete<js::PropMap>(ptr) {}
   1229 
   1230 public:
   1231  static void construct(void* storage, js::PropMap* ptr) {
   1232    new (storage) Concrete(ptr);
   1233  }
   1234 
   1235  Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
   1236 
   1237  const char16_t* typeName() const override { return concreteTypeName; }
   1238  static const char16_t concreteTypeName[];
   1239 };
   1240 
   1241 }  // namespace ubi
   1242 }  // namespace JS
   1243 
   1244 #endif  // vm_PropMap_h