Commit 9cc8fd31 authored by Jon Coppeard's avatar Jon Coppeard
Browse files

Bug 1802106 - Part 4: Add a trace method to OrderedHaspMap/Set r=jandem

Finally, this adds the trace method and uses it for Map/SetObject.

OrderedHashTable calls a method on Ops to split the entry into key/key+value as
appropriate.

Depends on D162832

Differential Revision: https://phabricator.services.mozilla.com/D162833
parent 96d2c848
Loading
Loading
Loading
Loading
+2 −23
Original line number Diff line number Diff line
@@ -513,28 +513,9 @@ const JSPropertySpec MapObject::staticProperties[] = {
  return NativeDefineDataProperty(cx, nativeProto, iteratorId, entriesFn, 0);
}

template <class MutableRange>
static void TraceKey(MutableRange& r, const PreBarriered<HashableValue>& key,
                     JSTracer* trc) {
  HashableValue newKey = key;
  newKey.trace(trc);

  // Check whether the key was moved by the GC. This is a bitwise comparison.
  if (newKey != key.get()) {
    // Update the key stored in the table. The hash function must take account
    // of the fact that the thing being hashed may have been moved by GC. This
    // is only an issue for BigInt as for other types the hash function only
    // uses the bits of the Value.
    r.rekeyFront(newKey);
  }
}

void MapObject::trace(JSTracer* trc, JSObject* obj) {
  if (ValueMap* map = obj->as<MapObject>().getTableUnchecked()) {
    for (auto r = map->mutableAll(); !r.empty(); r.popFront()) {
      TraceKey(r, r.front().key, trc);
      TraceEdge(trc, &r.front().value, "value");
    }
    map->trace(trc);
  }
}

@@ -1426,9 +1407,7 @@ SetObject* SetObject::create(JSContext* cx,
void SetObject::trace(JSTracer* trc, JSObject* obj) {
  SetObject* setobj = static_cast<SetObject*>(obj);
  if (ValueSet* set = setobj->getData()) {
    for (auto r = set->mutableAll(); !r.empty(); r.popFront()) {
      TraceKey(r, r.front(), trc);
    }
    set->trace(trc);
  }
}

+82 −42
Original line number Diff line number Diff line
@@ -44,8 +44,12 @@

#include <utility>

#include "gc/Barrier.h"
#include "js/GCPolicyAPI.h"
#include "js/HashTable.h"

class JSTracer;

namespace js {

namespace detail {
@@ -501,48 +505,9 @@ class OrderedHashTable {
      return this->ht->data[this->i].element;
    }

    /*
     * Change the key of the front entry.
     *
     * This calls Ops::hash on both the current key and the new key.
     * Ops::hash on the current key must return the same hash code as
     * when the entry was added to the table.
     */
    void rekeyFront(const Key& k) {
      MOZ_ASSERT(this->valid());
      OrderedHashTable* ht = this->ht;
      uint32_t i = this->i;

      Data& entry = ht->data[i];
      HashNumber oldHash =
          ht->prepareHash(Ops::getKey(entry.element)) >> ht->hashShift;
      HashNumber newHash = ht->prepareHash(k) >> ht->hashShift;
      Ops::setKey(entry.element, k);
      if (newHash != oldHash) {
        // Remove this entry from its old hash chain. (If this crashes
        // reading nullptr, it would mean we did not find this entry on
        // the hash chain where we expected it. That probably means the
        // key's hash code changed since it was inserted, breaking the
        // hash code invariant.)
        Data** ep = &ht->hashTable[oldHash];
        while (*ep != &entry) {
          ep = &(*ep)->chain;
        }
        *ep = entry.chain;

        // Add it to the new hash chain. We could just insert it at the
        // beginning of the chain. Instead, we do a bit of work to
        // preserve the invariant that hash chains always go in reverse
        // insertion order (descending memory order). No code currently
        // depends on this invariant, so it's fine to kill it if
        // needed.
        ep = &ht->hashTable[newHash];
        while (*ep && *ep > &entry) {
          ep = &(*ep)->chain;
        }
        entry.chain = *ep;
        *ep = &entry;
      }
      this->ht->rekey(&this->ht->data[this->i], k);
    }
  };

@@ -554,6 +519,31 @@ class OrderedHashTable {
  }
  MutableRange mutableAll() { return MutableRange(this, &ranges); }

  void trace(JSTracer* trc) {
    for (uint32_t i = 0; i < dataLength; i++) {
      if (!Ops::isEmpty(Ops::getKey(data[i].element))) {
        Ops::trace(trc, this, i, data[i].element);
      }
    }
  }

  // For use by the implementation of Ops::trace.
  template <typename Key>
  void traceKey(JSTracer* trc, uint32_t index, Key& key) {
    MOZ_ASSERT(index < dataLength);
    using MutableKey = std::remove_const_t<Key>;
    using UnbarrieredKey = typename RemoveBarrier<MutableKey>::Type;
    UnbarrieredKey newKey = key;
    JS::GCPolicy<UnbarrieredKey>::trace(trc, &newKey, "OrderedHashMap key");
    if (newKey != key) {
      rekey(&data[index], newKey);
    }
  }
  template <typename Value>
  void traceValue(JSTracer* trc, Value& value) {
    JS::GCPolicy<Value>::trace(trc, &value, "OrderedHashMap value");
  }

  /*
   * Allocate a new Range, possibly in nursery memory. The buffer must be
   * large enough to hold a Range object.
@@ -809,6 +799,40 @@ class OrderedHashTable {
    return true;
  }

  // Change the key of the front entry.
  //
  // This calls Ops::hash on both the current key and the new key. Ops::hash on
  // the current key must return the same hash code as when the entry was added
  // to the table.
  void rekey(Data* entry, const Key& k) {
    HashNumber oldHash = prepareHash(Ops::getKey(entry->element)) >> hashShift;
    HashNumber newHash = prepareHash(k) >> hashShift;
    Ops::setKey(entry->element, k);
    if (newHash != oldHash) {
      // Remove this entry from its old hash chain. (If this crashes reading
      // nullptr, it would mean we did not find this entry on the hash chain
      // where we expected it. That probably means the key's hash code changed
      // since it was inserted, breaking the hash code invariant.)
      Data** ep = &hashTable[oldHash];
      while (*ep != entry) {
        ep = &(*ep)->chain;
      }
      *ep = entry->chain;

      // Add it to the new hash chain. We could just insert it at the beginning
      // of the chain. Instead, we do a bit of work to preserve the invariant
      // that hash chains always go in reverse insertion order (descending
      // memory order). No code currently depends on this invariant, so it's
      // fine to kill it if needed.
      ep = &hashTable[newHash];
      while (*ep && *ep > entry) {
        ep = &(*ep)->chain;
      }
      entry->chain = *ep;
      *ep = entry;
    }
  }

  // Not copyable.
  OrderedHashTable& operator=(const OrderedHashTable&) = delete;
  OrderedHashTable(const OrderedHashTable&) = delete;
@@ -847,6 +871,9 @@ class OrderedHashMap {
  };

 private:
  struct MapOps;
  using Impl = detail::OrderedHashTable<Entry, MapOps, AllocPolicy>;

  struct MapOps : OrderedHashPolicy {
    using KeyType = Key;
    static void makeEmpty(Entry* e) {
@@ -858,9 +885,13 @@ class OrderedHashMap {
    }
    static const Key& getKey(const Entry& e) { return e.key; }
    static void setKey(Entry& e, const Key& k) { const_cast<Key&>(e.key) = k; }
    static void trace(JSTracer* trc, Impl* table, uint32_t index,
                      Entry& entry) {
      table->traceKey(trc, index, entry.key);
      table->traceValue(trc, entry.value);
    }
  };

  typedef detail::OrderedHashTable<Entry, MapOps, AllocPolicy> Impl;
  Impl impl;

 public:
@@ -905,6 +936,8 @@ class OrderedHashMap {

  void destroyNurseryRanges() { impl.destroyNurseryRanges(); }

  void trace(JSTracer* trc) { impl.trace(trc); }

  static size_t offsetOfEntryKey() { return Entry::offsetOfKey(); }
  static size_t offsetOfImplDataLength() { return Impl::offsetOfDataLength(); }
  static size_t offsetOfImplData() { return Impl::offsetOfData(); }
@@ -939,13 +972,18 @@ class OrderedHashMap {
template <class T, class OrderedHashPolicy, class AllocPolicy>
class OrderedHashSet {
 private:
  struct SetOps;
  using Impl = detail::OrderedHashTable<T, SetOps, AllocPolicy>;

  struct SetOps : OrderedHashPolicy {
    using KeyType = const T;
    static const T& getKey(const T& v) { return v; }
    static void setKey(const T& e, const T& v) { const_cast<T&>(e) = v; }
    static void trace(JSTracer* trc, Impl* table, uint32_t index, T& entry) {
      table->traceKey(trc, index, entry);
    }
  };

  typedef detail::OrderedHashTable<T, SetOps, AllocPolicy> Impl;
  Impl impl;

 public:
@@ -986,6 +1024,8 @@ class OrderedHashSet {

  void destroyNurseryRanges() { impl.destroyNurseryRanges(); }

  void trace(JSTracer* trc) { impl.trace(trc); }

  static size_t offsetOfEntryKey() { return 0; }
  static size_t offsetOfImplDataLength() { return Impl::offsetOfDataLength(); }
  static size_t offsetOfImplData() { return Impl::offsetOfData(); }