tor-browser

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

PropMap-inl.h (7507B)


      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_inl_h
      8 #define vm_PropMap_inl_h
      9 
     10 #include "vm/PropMap.h"
     11 
     12 #include "gc/Cell.h"
     13 #include "gc/Zone.h"
     14 #include "vm/JSContext.h"
     15 
     16 #include "gc/GCContext-inl.h"
     17 
     18 namespace js {
     19 
     20 inline AutoKeepPropMapTables::AutoKeepPropMapTables(JSContext* cx)
     21    : cx_(cx), prev_(cx->zone()->keepPropMapTables()) {
     22  cx->zone()->setKeepPropMapTables(true);
     23 }
     24 
     25 inline AutoKeepPropMapTables::~AutoKeepPropMapTables() {
     26  cx_->zone()->setKeepPropMapTables(prev_);
     27 }
     28 
     29 // static
     30 MOZ_ALWAYS_INLINE PropMap* PropMap::lookupLinear(uint32_t mapLength,
     31                                                 PropertyKey key,
     32                                                 uint32_t* index) {
     33  MOZ_ASSERT(mapLength > 0);
     34  MOZ_ASSERT(mapLength <= Capacity);
     35 
     36  // This function is very hot, so we use a macro to manually unroll the lookups
     37  // below. Some compilers are able to unroll the equivalent loops, but they're
     38  // not very consistent about this. The code below results in reasonable code
     39  // with all compilers we tested.
     40 
     41  static_assert(PropMap::Capacity == 8,
     42                "Code below needs to change when capacity changes");
     43 
     44 #define LOOKUP_KEY(idx)                        \
     45  if (mapLength > idx && getKey(idx) == key) { \
     46    *index = idx;                              \
     47    return this;                               \
     48  }
     49  LOOKUP_KEY(0);
     50  LOOKUP_KEY(1);
     51  LOOKUP_KEY(2);
     52  LOOKUP_KEY(3);
     53  LOOKUP_KEY(4);
     54  LOOKUP_KEY(5);
     55  LOOKUP_KEY(6);
     56  LOOKUP_KEY(7);
     57 #undef LOOKUP_KEY
     58 
     59  PropMap* map = this;
     60  while (map->hasPrevious()) {
     61    map = map->asLinked()->previous();
     62 #define LOOKUP_KEY(idx)          \
     63  if (map->getKey(idx) == key) { \
     64    *index = idx;                \
     65    return map;                  \
     66  }
     67    LOOKUP_KEY(0);
     68    LOOKUP_KEY(1);
     69    LOOKUP_KEY(2);
     70    LOOKUP_KEY(3);
     71    LOOKUP_KEY(4);
     72    LOOKUP_KEY(5);
     73    LOOKUP_KEY(6);
     74    LOOKUP_KEY(7);
     75 #undef LOOKUP_INDEX
     76  }
     77 
     78  return nullptr;
     79 }
     80 
     81 MOZ_ALWAYS_INLINE PropMap* PropMapTable::lookup(PropMap* map,
     82                                                uint32_t mapLength,
     83                                                PropertyKey key,
     84                                                uint32_t* index) {
     85  JS::AutoCheckCannotGC nogc;
     86  MOZ_ASSERT(map->asLinked()->maybeTable(nogc) == this);
     87 
     88  PropMapAndIndex entry;
     89  if (lookupInCache(key, &entry)) {
     90    if (entry.isNone()) {
     91      return nullptr;
     92    }
     93  } else {
     94    auto p = lookupRaw(key);
     95    addToCache(key, p);
     96    if (!p) {
     97      return nullptr;
     98    }
     99    entry = *p;
    100  }
    101 
    102  // For the last map, only properties in [0, mapLength) are part of the object.
    103  if (entry.map() == map && entry.index() >= mapLength) {
    104    return nullptr;
    105  }
    106 
    107  *index = entry.index();
    108  return entry.map();
    109 }
    110 
    111 // static
    112 MOZ_ALWAYS_INLINE PropMap* PropMap::lookupPure(uint32_t mapLength,
    113                                               PropertyKey key,
    114                                               uint32_t* index) {
    115  if (canHaveTable()) {
    116    JS::AutoCheckCannotGC nogc;
    117    if (PropMapTable* table = asLinked()->maybeTable(nogc)) {
    118      return table->lookup(this, mapLength, key, index);
    119    }
    120  }
    121 
    122  return lookupLinear(mapLength, key, index);
    123 }
    124 
    125 // static
    126 MOZ_ALWAYS_INLINE PropMap* PropMap::lookup(JSContext* cx, uint32_t mapLength,
    127                                           PropertyKey key, uint32_t* index) {
    128  if (canHaveTable()) {
    129    JS::AutoCheckCannotGC nogc;
    130    if (PropMapTable* table = asLinked()->ensureTable(cx, nogc);
    131        MOZ_LIKELY(table)) {
    132      return table->lookup(this, mapLength, key, index);
    133    }
    134    // OOM. Do a linear lookup.
    135    cx->recoverFromOutOfMemory();
    136  }
    137 
    138  return lookupLinear(mapLength, key, index);
    139 }
    140 
    141 // static
    142 inline void SharedPropMap::getPrevious(MutableHandle<SharedPropMap*> map,
    143                                       uint32_t* mapLength) {
    144  // Update the map/mapLength pointers to "remove" the last property. In most
    145  // cases we can simply decrement *mapLength, but if *mapLength is 1 we have to
    146  // either start at the previous map or set map/mapLength to nullptr/zero
    147  // (if there is just one property).
    148 
    149  MOZ_ASSERT(map);
    150  MOZ_ASSERT(*mapLength > 0);
    151 
    152  if (*mapLength > 1) {
    153    *mapLength -= 1;
    154    return;
    155  }
    156 
    157  if (map->hasPrevious()) {
    158    map.set(map->asNormal()->previous());
    159    *mapLength = PropMap::Capacity;
    160    return;
    161  }
    162 
    163  map.set(nullptr);
    164  *mapLength = 0;
    165 }
    166 
    167 // static
    168 inline bool PropMap::lookupForRemove(JSContext* cx, PropMap* map,
    169                                     uint32_t mapLength, PropertyKey key,
    170                                     const AutoKeepPropMapTables& keep,
    171                                     PropMap** propMap, uint32_t* propIndex,
    172                                     PropMapTable** table,
    173                                     PropMapTable::Ptr* ptr) {
    174  if (map->isDictionary()) {
    175    *table = map->asLinked()->ensureTable(cx, keep);
    176    if (!*table) {
    177      return false;
    178    }
    179    *ptr = (*table)->lookupRaw(key);
    180    *propMap = *ptr ? (*ptr)->map() : nullptr;
    181    *propIndex = *ptr ? (*ptr)->index() : 0;
    182    return true;
    183  }
    184 
    185  *table = nullptr;
    186  *propMap = map->lookup(cx, mapLength, key, propIndex);
    187  return true;
    188 }
    189 
    190 MOZ_ALWAYS_INLINE bool SharedPropMap::shouldConvertToDictionaryForAdd() const {
    191  if (MOZ_LIKELY(numPreviousMaps() < NumPrevMapsConsiderDictionary)) {
    192    return false;
    193  }
    194  if (numPreviousMaps() >= NumPrevMapsAlwaysDictionary) {
    195    return true;
    196  }
    197 
    198  // More heuristics: if one of the last two maps has had a dictionary
    199  // conversion before, or is branchy (indicated by parent != previous), convert
    200  // to dictionary.
    201  const SharedPropMap* curMap = this;
    202  for (size_t i = 0; i < 2; i++) {
    203    if (curMap->hadDictionaryConversion()) {
    204      return true;
    205    }
    206    if (curMap->treeDataRef().parent.map() != curMap->asNormal()->previous()) {
    207      return true;
    208    }
    209    curMap = curMap->asNormal()->previous();
    210  }
    211  return false;
    212 }
    213 
    214 inline void SharedPropMap::sweep(JS::GCContext* gcx) {
    215  // We detach the child from the parent if the parent is reachable.
    216  //
    217  // This test depends on PropMap arenas not being freed until after we finish
    218  // incrementally sweeping them. If that were not the case the parent pointer
    219  // could point to a marked cell that had been deallocated and then
    220  // reallocated, since allocating a cell in a zone that is being marked will
    221  // set the mark bit for that cell.
    222 
    223  MOZ_ASSERT(zone()->isGCSweeping());
    224  MOZ_ASSERT_IF(hasPrevious(), asLinked()->previous()->zone() == zone());
    225 
    226  SharedPropMapAndIndex parent = treeDataRef().parent;
    227  if (!parent.isNone() && TenuredThingIsMarkedAny(parent.map())) {
    228    parent.map()->removeChild(gcx, this);
    229  }
    230 }
    231 
    232 inline void SharedPropMap::finalize(JS::GCContext* gcx) {
    233  if (canHaveTable() && asLinked()->hasTable()) {
    234    asLinked()->purgeTable(gcx);
    235  }
    236  if (hasChildrenSet()) {
    237    SharedChildrenPtr& childrenRef = treeDataRef().children;
    238    gcx->delete_(this, childrenRef.toChildrenSet(), MemoryUse::PropMapChildren);
    239    childrenRef.setNone();
    240  }
    241 }
    242 
    243 inline void DictionaryPropMap::finalize(JS::GCContext* gcx) {
    244  if (asLinked()->hasTable()) {
    245    asLinked()->purgeTable(gcx);
    246  }
    247 }
    248 
    249 }  // namespace js
    250 
    251 #endif /* vm_PropMap_inl_h */