Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  WeakMap.h

  Sprache: C
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef gc_WeakMap_h
#define gc_WeakMap_h

#include "mozilla/Atomics.h"
#include "mozilla/Maybe.h"

#include "ds/SlimLinkedList.h"
#include "gc/AllocKind.h"
#include "gc/Barrier.h"
#include "gc/Cell.h"
#include "gc/Marking.h"
#include "gc/Tracer.h"
#include "gc/ZoneAllocator.h"
#include "js/GCVector.h"
#include "js/HashTable.h"
#include "js/HeapAPI.h"
#include "vm/JSObject.h"

namespace JS {
class Zone;
}

namespace js {

class GCMarker;
class WeakMapBase;
struct WeakMapTracer;

template <typename T>
struct WeakMapKeyHasher;

extern void DumpWeakMapLog(JSRuntime* rt);

namespace gc {

// Ensure a Symbol read out of a weak map is marked black in |zone|'s atom
// marking bitmap before it can escape to script.
void MarkSymbolForWeakMapReadBarrier(JS::Zone* zone, JS::Symbol* sym);

#if defined(JS_GC_ZEAL) || defined(DEBUG)
// Check whether a weak map entry is marked correctly.
bool CheckWeakMapEntryMarking(const WeakMapBase* map, Cell* key, Cell* value);
#endif

template <typename PtrT>
struct MightBeInNursery {
  using T = std::remove_pointer_t<PtrT>;
  static_assert(std::is_base_of_v<Cell, T>);
  static_assert(!std::is_same_v<Cell, T> && !std::is_same_v<TenuredCell, T>);

#define CAN_NURSERY_ALLOC_KIND_OR(_1, _2, Type, _3, _4, canNurseryAlloc, _5) \
  std::is_base_of_v<Type, T> ? canNurseryAlloc:

  // FOR_EACH_ALLOCKIND doesn't cover every possible type: make sure
  // to default to `true` for unknown types.
  static constexpr bool value =
      FOR_EACH_ALLOCKIND(CAN_NURSERY_ALLOC_KIND_OR) true;
#undef CAN_NURSERY_ALLOC_KIND_OR
};
template <>
struct MightBeInNursery<JS::Value> {
  static constexpr bool value = true;
};

}  // namespace gc

// A subclass template of js::HashMap whose keys and values may be
// garbage-collected. When a key is collected, the table entry disappears,
// dropping its reference to the value.
//
// More precisely:
//
//     A WeakMap entry is live if and only if both the WeakMap and the entry's
//     key are live. An entry holds a strong reference to its value.
//
// You must call this table's 'trace' method when its owning object is reached
// by the garbage collection tracer. Once a table is known to be live, the
// implementation takes care of the special weak marking (ie, marking through
// the implicit edges stored in the map) and of removing (sweeping) table
// entries when collection is complete.

// WeakMaps are marked with an incremental linear-time algorithm that handles
// all orderings of map and key marking. The basic algorithm is:
//
// At first while marking, do nothing special when marking WeakMap keys (there
// is no straightforward way to know whether a particular object is being used
// as a key in some weakmap.) When a WeakMap is marked, scan through it to mark
// all entries with live keys, and collect all unmarked keys into a "weak keys"
// table.
//
// At some point, everything reachable has been marked. At this point, enter
// "weak marking mode". In this mode, whenever any object is marked, look it up
// in the weak keys table to see if it is the key for any WeakMap entry and if
// so, mark the value. When entering weak marking mode, scan the weak key table
// to find all keys that have been marked since we added them to the table, and
// mark those entries.
//
// In addition, we want weakmap marking to work incrementally. So WeakMap
// mutations are barriered to keep the weak keys table up to date: entries are
// removed if their key is removed from the table, etc.
//
// You can break down various ways that WeakMap values get marked based on the
// order that the map and key are marked. All of these assume the map and key
// get marked at some point:
//
//   key marked, then map marked:
//    - value was marked with map in `markEntries()`
//   map marked, key already in map, key marked before weak marking mode:
//    - key added to gcEphemeronEdges when map marked in `markEntries()`
//    - value marked during `enterWeakMarkingMode`
//   map marked, key already in map, key marked after weak marking mode:
//    - when key is marked, gcEphemeronEdges[key] triggers marking of value in
//      `markImplicitEdges()`
//   map marked, key inserted into map, key marked:
//    - value was live when inserted and must get marked at some point
//

using WeakMapColors = HashMap<WeakMapBase*, js::gc::CellColor,
                              DefaultHasher<WeakMapBase*>, SystemAllocPolicy>;

class WeakMapBase;
using WeakMapList = SlimLinkedList<WeakMapBase>;

// Common base class for all WeakMap specializations, used for calling
// subclasses' GC-related methods.
class WeakMapBase : public SlimLinkedListElement<WeakMapBase> {
  friend class js::GCMarker;

 public:
  using CellColor = js::gc::CellColor;

  WeakMapBase(JSObject* memOf, JS::Zone* zone);
  virtual ~WeakMapBase() = default;

  JS::Zone* zone() const { return zone_; }

  // Whether this is a 'system' weakmap as opposed to a 'user' one. System
  // weakmaps are used internally by the engine and |memberOf| is null. User
  // ones are part of a JS WeakMap object pointed to by |memberOf|.
  bool isSystem() const { return !memberOf; }

  // Garbage collector entry points.

  // Unmark all weak maps in a zone.
  static void unmarkZone(JS::Zone* zone);
#ifdef DEBUG
  static void checkZoneUnmarked(JS::Zone* zone);
#else
  static void checkZoneUnmarked(JS::Zone* zone) {}
#endif

  // Check all weak maps in a zone that have been marked as live in this garbage
  // collection, and mark the values of all entries that have become strong
  // references to them. Return true if we marked any new values, indicating
  // that we need to make another pass. In other words, mark my marked maps'
  // marked members' mid-collection.
  static bool markZoneIteratively(JS::Zone* zone, GCMarker* marker);

  // Add zone edges for weakmaps in zone |mapZone| with key delegates in a
  // different zone.
  [[nodiscard]] static bool findSweepGroupEdgesForZone(JS::Zone* atomsZone,
                                                       JS::Zone* mapZone);

  // Trace all weak map bindings. Used by the cycle collector.
  static void traceAllMappings(WeakMapTracer* tracer);

#if defined(JS_GC_ZEAL)
  // Save information about which weak maps are marked for a zone.
  static bool saveZoneMarkedWeakMaps(JS::Zone* zone,
                                     WeakMapColors& markedWeakMaps);

  // Restore information about which weak maps are marked for many zones.
  static void restoreMarkedWeakMaps(WeakMapColors& markedWeakMaps);
#endif

#if defined(JS_GC_ZEAL) || defined(DEBUG)
  static bool checkMarkingForZone(JS::Zone* zone);
#endif

#ifdef JSGC_HASH_TABLE_CHECKS
  static void checkWeakMapsAfterMovingGC(JS::Zone* zone);
#endif

 protected:
  // Instance member functions called by the above. Instantiations of WeakMap
  // override these with definitions appropriate for their Key and Value types.
  virtual bool empty() const = 0;
  virtual void trace(JSTracer* tracer) = 0;
  virtual bool findSweepGroupEdges(Zone* atomsZone) = 0;
  virtual void traceWeakEdgesDuringSweeping(JSTracer* trc) = 0;
  virtual void traceMappings(WeakMapTracer* tracer) = 0;
  virtual void clearAndCompact() = 0;

  virtual bool markEntries(GCMarker* marker) = 0;

  // Trace any keys and values that are in the nursery. Return false if any
  // remain in the nursery.
  virtual bool traceNurseryEntriesOnMinorGC(JSTracer* trc) = 0;
  virtual bool sweepAfterMinorGC() = 0;

  // We have a key that, if it or its delegate is marked, may lead to a WeakMap
  // value getting marked. Insert the necessary edges into the appropriate
  // zone's gcEphemeronEdges or gcNurseryEphemeronEdges tables.
  [[nodiscard]] bool addEphemeronEdgesForEntry(gc::MarkColor mapColor,
                                               gc::TenuredCell* key,
                                               gc::Cell* delegate,
                                               gc::TenuredCell* value);
  [[nodiscard]] bool addEphemeronEdge(gc::MarkColor color, gc::TenuredCell* src,
                                      gc::TenuredCell* dst);

  gc::CellColor mapColor() const { return gc::CellColor(uint32_t(mapColor_)); }
  void setMapColor(gc::CellColor newColor) { mapColor_ = uint32_t(newColor); }

  bool isMarked() const { return gc::IsMarked(mapColor()); }

  // Attempt to mark the map and return the old color if successful.
  mozilla::Maybe<gc::CellColor> markMap(gc::MarkColor markColor);

  void setHasNurseryEntries();

#ifdef DEBUG
  virtual void checkCachedFlags() const = 0;
#endif

#ifdef JS_GC_ZEAL
  virtual bool checkMarking() const = 0;
  virtual bool allowKeysInOtherZones() const { return false; }
  friend bool gc::CheckWeakMapEntryMarking(const WeakMapBase*, gc::Cell*,
                                           gc::Cell*);
#endif

#ifdef JSGC_HASH_TABLE_CHECKS
  virtual void checkAfterMovingGC() const = 0;
#endif

  // Object that this weak map is part of, if any.
  HeapPtr<JSObject*> memberOf;

  // Zone containing this weak map.
  JS::Zone* zone_;

  // Whether this object has been marked during garbage collection and which
  // color it was marked.
  mozilla::Atomic<uint32_t, mozilla::Relaxed> mapColor_;

  // Cached information about keys to speed up findSweepGroupEdges.
  bool mayHaveKeyDelegates = false;
  bool mayHaveSymbolKeys = false;

  // Whether this map contains entries with nursery keys or values.
  bool hasNurseryEntries = false;

  // Whether the |nurseryKeys| vector contains the keys of all entries with
  // nursery keys or values. This can be false if it gets too large or on OOM.
  bool nurseryKeysValid = true;

  friend class JS::Zone;
  friend class js::Nursery;
};

// Get the hash from a Symbol.
HashNumber GetSymbolHash(JS::Symbol* sym);

// By default weak maps use default hasher for the key type, which hashes
// the pointer itself for pointer types.
template <typename T>
struct WeakMapKeyHasher : public DefaultHasher<T> {};

// We only support JS::Value keys that contain objects or symbols. For objects
// we hash the pointer and for symbols we use its stored hash, which is randomly
// generated on creation.
//
// Equality is based on a bitwise test not on JS Value semantics.
//
// Take care when modifying this code! Previously there have been security
// issues around using pointer hashing for maps (e.g. bug 1312001).
//
// Although this does use pointer hashing for objects, we don't think those
// concerns apply here because:
//
//  1) This uses an open addressed hash table rather than a chained one which
//     makes the attack much more difficult.
//
//  2) The allowed key types are restricted to objects and non-registered
//     symbols, so it's not possible to use int32 keys as were used in the
//     attack.
//
//  3) Symbols use their own random hash codes which can't be predicted.
//
//  4) Registered symbols are not allowed, which means it's not possible to leak
//     information about such symbols used by another zone.
//
//  5) Although sequentially allocated objects will have similar pointers,
//     ScrambleHashCode should work well enough to distribute these keys and
//     make predicting the hash code from the pointer difficult.
template <>
struct WeakMapKeyHasher<JS::Value> {
  using Key = JS::Value;
  using Lookup = JS::Value;

  static HashNumber hash(const Lookup& l) {
    checkValueType(l);
    if (l.isSymbol()) {
      return GetSymbolHash(l.toSymbol());
    }
    return mozilla::HashGeneric(l.asRawBits());
  }

  static bool match(const Key& k, const Lookup& l) {
    checkValueType(k);
    return k == l;
  }

  static void rekey(Key& k, const Key& newKey) { k = newKey; }

 private:
  static void checkValueType(const Value& value);
};

template <>
struct WeakMapKeyHasher<PreBarriered<JS::Value>> {
  using Key = PreBarriered<JS::Value>;
  using Lookup = JS::Value;

  static HashNumber hash(const Lookup& l) {
    return WeakMapKeyHasher<JS::Value>::hash(l);
  }
  static bool match(const Key& k, const Lookup& l) {
    return WeakMapKeyHasher<JS::Value>::match(k, l);
  }
  static void rekey(Key& k, const Lookup& newKey) { k.unbarrieredSet(newKey); }
};

template <class Key, class Value, class AllocPolicy>
class WeakMap : public WeakMapBase {
  using BarrieredKey = PreBarriered<Key>;
  using BarrieredValue = PreBarriered<Value>;

  using Map = HashMap<BarrieredKey, BarrieredValue,
                      WeakMapKeyHasher<BarrieredKey>, AllocPolicy>;
  using UnbarrieredMap =
      HashMap<Key, Value, WeakMapKeyHasher<Key>, AllocPolicy>;

  UnbarrieredMap map_;  // Barriers are added by |map()| accessor.

  // The keys of entries where either the key or value is allocated in the
  // nursery.
  GCVector<Key, 0, AllocPolicy> nurseryKeys;

 public:
  using Lookup = typename Map::Lookup;
  using Entry = typename Map::Entry;
  using Iterator = typename Map::Iterator;
  using ModIterator = typename Map::ModIterator;

  // Restrict the interface of HashMap::Ptr and AddPtr to remove mutable access
  // to the hash table entry which could otherwise bypass our barriers.

  using MutablePtr = typename Map::Ptr;
  class Ptr {
    MutablePtr ptr;
    friend class WeakMap;

   public:
    explicit Ptr(const MutablePtr& ptr) : ptr(ptr) {}
    bool found() const { return ptr.found(); }
    explicit operator bool() const { return found(); }
    const Entry& operator*() const { return *ptr; }
    const Entry* operator->() const { return &*ptr; }
  };

  using MutableAddPtr = typename Map::AddPtr;
  class AddPtr {
    MutableAddPtr ptr;
    friend class WeakMap;

   public:
    explicit AddPtr(const MutableAddPtr& ptr) : ptr(ptr) {}
    bool found() const { return ptr.found(); }
    explicit operator bool() const { return found(); }
    const Entry& operator*() const { return *ptr; }
    const Entry* operator->() const { return &*ptr; }
  };

  // Create a weak map owned by a JS object. Used for script-facing objects.
  explicit WeakMap(JSContext* cx, JSObject* memOf);

  // Create a weak map associated with a zone. For internal use by the engine.
  explicit WeakMap(JS::Zone* zone);

  ~WeakMap() override;

  Iterator iter() const { return map().iter(); }
  ModIterator modIter() { return map().modIter(); }
  uint32_t count() const { return map().count(); }
  bool empty() const override { return map().empty(); }
  bool has(const Lookup& lookup) const { return map().has(lookup); }
  void remove(const Lookup& lookup) { return map().remove(lookup); }
  void remove(Ptr ptr) { return map().remove(ptr.ptr); }

  // Get the value associated with a key, or a default constructed Value if the
  // key is not present in the map.
  Value get(const Lookup& l) const {
    Ptr ptr = lookup(l);
    if (!ptr) {
      return Value();
    }
    return ptr->value();
  }

  // Add a read barrier to prevent a gray value from escaping the weak map. This
  // is necessary because we don't unmark gray through weak maps.
  Ptr lookup(const Lookup& l) const {
    Ptr p = lookupUnbarriered(l);
    if (p) {
      valueReadBarrier(p->value());
    }
    return p;
  }

  Ptr lookupUnbarriered(const Lookup& l) const { return Ptr(map().lookup(l)); }

  AddPtr lookupForAdd(const Lookup& l) {
    AddPtr p(map().lookupForAdd(l));
    if (p) {
      valueReadBarrier(p->value());
    }
    return p;
  }

  [[nodiscard]] bool add(AddPtr& p, const Key& k, const Value& v) {
    MOZ_ASSERT(gc::ToMarkable(k));
    writeBarrier(k, v);
    return map().add(p.ptr, k, v);
  }

  [[nodiscard]] bool relookupOrAdd(AddPtr& p, const Key& k, const Value& v) {
    MOZ_ASSERT(gc::ToMarkable(k));
    writeBarrier(k, v);
    return map().relookupOrAdd(p.ptr, k, v);
  }

  [[nodiscard]] bool put(const Key& k, const Value& v) {
    MOZ_ASSERT(gc::ToMarkable(k));
    writeBarrier(k, v);
    return map().put(k, v);
  }

  [[nodiscard]] bool putNew(const Key& k, const Value& v) {
    MOZ_ASSERT(gc::ToMarkable(k));
    writeBarrier(k, v);
    return map().putNew(k, v);
  }

  void clear() {
    map().clear();
    nurseryKeys.clear();
    nurseryKeysValid = true;
    mayHaveSymbolKeys = false;
    if (!isSystem()) {
      mayHaveKeyDelegates = false;
    }
  }

#ifdef DEBUG
  bool hasEntry(const Key& key, const Value& value) const {
    Ptr p = lookupUnbarriered(key);
    return p && p->value() == value;
  }
#endif

  bool markEntry(GCMarker* marker, gc::CellColor mapColor, ModIterator& iter,
                 bool populateWeakKeysTable);

  void trace(JSTracer* trc) override;

  // Used by the debugger to trace cross-compartment edges.
  void traceKeys(JSTracer* trc);
  void traceKey(JSTracer* trc, ModIterator& iter);

  size_t shallowSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);

  static size_t offsetOfHashShift() {
    return offsetof(WeakMap, map_) + UnbarrieredMap::offsetOfHashShift();
  }
  static size_t offsetOfTable() {
    return offsetof(WeakMap, map_) + UnbarrieredMap::offsetOfTable();
  }
  static size_t offsetOfEntryCount() {
    return offsetof(WeakMap, map_) + UnbarrieredMap::offsetOfEntryCount();
  }

 protected:
  inline void assertMapIsSameZoneWithValue(const BarrieredValue& v);

  bool markEntries(GCMarker* marker) override;

  // Find sweep group edges for delegates, if the key type has delegates. (If
  // not, the optimizer should make this a nop.)
  bool findSweepGroupEdges(Zone* atomsZone) override;

#if DEBUG
  void assertEntriesNotAboutToBeFinalized();
  void checkCachedFlags() const override;
#endif

#ifdef JS_GC_ZEAL
  bool checkMarking() const override;
#endif

#ifdef JSGC_HASH_TABLE_CHECKS
  void checkAfterMovingGC() const override;
#endif

 private:
  static void staticAssertions();

  // Map accessor uses a cast to add barriers.
  Map& map() { return reinterpret_cast<Map&>(map_); }
  const Map& map() const { return reinterpret_cast<const Map&>(map_); }

  MutablePtr lookupMutableUnbarriered(const Lookup& l) {
    return map().lookup(l);
  }

  void valueReadBarrier(const JS::Value& v) const {
    // js::jit::WeakMapValueReadBarrier is a specialized version of this
    // function designed to be called from jitcode. If this code is changed, it
    // should be kept in sync.
    JS::ExposeValueToActiveJS(v);
    if (MOZ_UNLIKELY(v.isSymbol())) {
      gc::MarkSymbolForWeakMapReadBarrier(zone(), v.toSymbol());
    }
  }
  static void valueReadBarrier(JSObject* obj) {
    JS::ExposeObjectToActiveJS(obj);
  }
  static void valueReadBarrier(jit::JitCode* code) {
    gc::ExposeGCThingToActiveJS(JS::GCCellPtr(code));
  }

  void writeBarrier(const Key& key, const Value& value) {
    keyKindBarrier(key);
    nurseryEntryBarrier(key, value);
  }

  void keyKindBarrier(const JS::Value& key) {
    if (key.isSymbol() && !mayHaveSymbolKeys) {
      setMayHaveSymbolKeys();
    }
    if (key.isObject()) {
      keyKindBarrier(&key.toObject());
    }
  }
  void keyKindBarrier(JSObject* key) {
    // Fast path for non-proxy objects.
    if (!IsProxy(key)) {
      MOZ_ASSERT(!ObjectMayBeSwapped(key));
      return;
    }
    keyKindBarrierSlow(key);
  }
  void keyKindBarrierSlow(JSObject* key) {
    if (!mayHaveKeyDelegates) {
      JSObject* delegate = UncheckedUnwrapWithoutExpose(key);
      if (delegate != key || ObjectMayBeSwapped(key)) {
        setMayHaveKeyDelegates();
      }
    }
  }
  void keyKindBarrier(BaseScript* key) {}

  void nurseryEntryBarrier(const Key& key, const Value& value) {
    if ((gc::MightBeInNursery<Key>::value &&
         !JS::GCPolicy<Key>::isTenured(key)) ||
        (gc::MightBeInNursery<Value>::value &&
         !JS::GCPolicy<Value>::isTenured(value))) {
      if (!hasNurseryEntries) {
        setHasNurseryEntries();
      }

      addNurseryKey(key);
    }
  }

  void addNurseryKey(const Key& key);
  void setMayHaveSymbolKeys();
  void setMayHaveKeyDelegates();

  void traceWeakEdgesDuringSweeping(JSTracer* trc) override;

  void clearAndCompact() override {
    clear();
    map().compact();
    nurseryKeys.clearAndFree();
  }

  // memberOf can be nullptr, which means that the map is not part of a
  // JSObject.
  void traceMappings(WeakMapTracer* tracer) override;

  bool traceNurseryEntriesOnMinorGC(JSTracer* trc) override;
  bool sweepAfterMinorGC() override;
};

/* namespace js */

#endif /* gc_WeakMap_h */

Messung V0.5 in Prozent
C=81 H=100 G=90

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002