LCOV - code coverage report
Current view: top level - js/src - jsweakmap.h (source / functions) Hit Total Coverage
Test: output.info Lines: 49 138 35.5 %
Date: 2017-07-14 16:53:18 Functions: 10 78 12.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef jsweakmap_h
       8             : #define jsweakmap_h
       9             : 
      10             : #include "mozilla/LinkedList.h"
      11             : #include "mozilla/Move.h"
      12             : 
      13             : #include "jscompartment.h"
      14             : #include "jsfriendapi.h"
      15             : #include "jsobj.h"
      16             : 
      17             : #include "gc/Marking.h"
      18             : #include "gc/StoreBuffer.h"
      19             : #include "js/HashTable.h"
      20             : 
      21             : namespace js {
      22             : 
      23             : class GCMarker;
      24             : class WeakMapBase;
      25             : 
      26             : // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
      27             : // a key is collected, the table entry disappears, dropping its reference to the value.
      28             : //
      29             : // More precisely:
      30             : //
      31             : //     A WeakMap entry is live if and only if both the WeakMap and the entry's key
      32             : //     are live. An entry holds a strong reference to its value.
      33             : //
      34             : // You must call this table's 'trace' method when its owning object is reached
      35             : // by the garbage collection tracer. Once a table is known to be live, the
      36             : // implementation takes care of the special weak marking (ie, marking through
      37             : // the implicit edges stored in the map) and of removing (sweeping) table
      38             : // entries when collection is complete.
      39             : 
      40             : typedef HashSet<WeakMapBase*, DefaultHasher<WeakMapBase*>, SystemAllocPolicy> WeakMapSet;
      41             : 
      42             : // Common base class for all WeakMap specializations, used for calling
      43             : // subclasses' GC-related methods.
      44             : class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
      45             : {
      46             :     friend class js::GCMarker;
      47             : 
      48             :   public:
      49             :     WeakMapBase(JSObject* memOf, JS::Zone* zone);
      50             :     virtual ~WeakMapBase();
      51             : 
      52          20 :     Zone* zone() const { return zone_; }
      53             : 
      54             :     // Garbage collector entry points.
      55             : 
      56             :     // Unmark all weak maps in a zone.
      57             :     static void unmarkZone(JS::Zone* zone);
      58             : 
      59             :     // Mark all the weakmaps in a zone.
      60             :     static void traceZone(JS::Zone* zone, JSTracer* tracer);
      61             : 
      62             :     // Check all weak maps in a zone that have been marked as live in this garbage
      63             :     // collection, and mark the values of all entries that have become strong references
      64             :     // to them. Return true if we marked any new values, indicating that we need to make
      65             :     // another pass. In other words, mark my marked maps' marked members' mid-collection.
      66             :     static bool markZoneIteratively(JS::Zone* zone, GCMarker* marker);
      67             : 
      68             :     // Add zone edges for weakmaps with key delegates in a different zone.
      69             :     static bool findInterZoneEdges(JS::Zone* zone);
      70             : 
      71             :     // Sweep the weak maps in a zone, removing dead weak maps and removing
      72             :     // entries of live weak maps whose keys are dead.
      73             :     static void sweepZone(JS::Zone* zone);
      74             : 
      75             :     // Trace all delayed weak map bindings. Used by the cycle collector.
      76             :     static void traceAllMappings(WeakMapTracer* tracer);
      77             : 
      78             :     // Save information about which weak maps are marked for a zone.
      79             :     static bool saveZoneMarkedWeakMaps(JS::Zone* zone, WeakMapSet& markedWeakMaps);
      80             : 
      81             :     // Restore information about which weak maps are marked for many zones.
      82             :     static void restoreMarkedWeakMaps(WeakMapSet& markedWeakMaps);
      83             : 
      84             :   protected:
      85             :     // Instance member functions called by the above. Instantiations of WeakMap override
      86             :     // these with definitions appropriate for their Key and Value types.
      87             :     virtual void trace(JSTracer* tracer) = 0;
      88             :     virtual bool findZoneEdges() = 0;
      89             :     virtual void sweep() = 0;
      90             :     virtual void traceMappings(WeakMapTracer* tracer) = 0;
      91             :     virtual void finish() = 0;
      92             : 
      93             :     // Any weakmap key types that want to participate in the non-iterative
      94             :     // ephemeron marking must override this method.
      95             :     virtual void markEntry(GCMarker* marker, gc::Cell* markedCell, JS::GCCellPtr l) = 0;
      96             : 
      97             :     virtual bool markIteratively(GCMarker* marker) = 0;
      98             : 
      99             :   protected:
     100             :     // Object that this weak map is part of, if any.
     101             :     GCPtrObject memberOf;
     102             : 
     103             :     // Zone containing this weak map.
     104             :     JS::Zone* zone_;
     105             : 
     106             :     // Whether this object has been traced during garbage collection.
     107             :     bool marked;
     108             : };
     109             : 
     110             : template <typename T>
     111           0 : static T extractUnbarriered(const WriteBarrieredBase<T>& v)
     112             : {
     113           0 :     return v.get();
     114             : }
     115             : template <typename T>
     116             : static T* extractUnbarriered(T* v)
     117             : {
     118             :     return v;
     119             : }
     120             : 
     121             : template <class Key, class Value,
     122             :           class HashPolicy = DefaultHasher<Key> >
     123           0 : class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>,
     124             :                 public WeakMapBase
     125             : {
     126             :   public:
     127             :     typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
     128             :     typedef typename Base::Enum Enum;
     129             :     typedef typename Base::Lookup Lookup;
     130             :     typedef typename Base::Entry Entry;
     131             :     typedef typename Base::Range Range;
     132             :     typedef typename Base::Ptr Ptr;
     133             :     typedef typename Base::AddPtr AddPtr;
     134             : 
     135          20 :     explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr)
     136          20 :         : Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()->zone()) { }
     137             : 
     138          20 :     bool init(uint32_t len = 16) {
     139          20 :         if (!Base::init(len))
     140           0 :             return false;
     141          20 :         zone()->gcWeakMapList().insertFront(this);
     142          20 :         marked = JS::IsIncrementalGCInProgress(TlsContext.get());
     143          20 :         return true;
     144             :     }
     145             : 
     146             :     // Overwritten to add a read barrier to prevent an incorrectly gray value
     147             :     // from escaping the weak map. See the UnmarkGrayTracer::onChild comment in
     148             :     // gc/Marking.cpp.
     149         827 :     Ptr lookup(const Lookup& l) const {
     150         827 :         Ptr p = Base::lookup(l);
     151         827 :         if (p)
     152         732 :             exposeGCThingToActiveJS(p->value());
     153         827 :         return p;
     154             :     }
     155             : 
     156           0 :     AddPtr lookupForAdd(const Lookup& l) const {
     157           0 :         AddPtr p = Base::lookupForAdd(l);
     158           0 :         if (p)
     159           0 :             exposeGCThingToActiveJS(p->value());
     160           0 :         return p;
     161             :     }
     162             : 
     163             :     Ptr lookupWithDefault(const Key& k, const Value& defaultValue) {
     164             :         Ptr p = Base::lookupWithDefault(k, defaultValue);
     165             :         if (p)
     166             :             exposeGCThingToActiveJS(p->value());
     167             :         return p;
     168             :     }
     169             : 
     170             :     // Resolve ambiguity with LinkedListElement<>::remove.
     171             :     using Base::remove;
     172             : 
     173             :     // Trace a WeakMap entry based on 'markedCell' getting marked, where
     174             :     // 'origKey' is the key in the weakmap. These will probably be the same,
     175             :     // but can be different eg when markedCell is a delegate for origKey.
     176             :     //
     177             :     // This implementation does not use 'markedCell'; it looks up origKey and
     178             :     // checks the mark bits on everything it cares about, one of which will be
     179             :     // markedCell. But a subclass might use it to optimize the liveness check.
     180           0 :     void markEntry(GCMarker* marker, gc::Cell* markedCell, JS::GCCellPtr origKey) override
     181             :     {
     182           0 :         MOZ_ASSERT(marked);
     183             : 
     184             :         // If this cast fails, then you're instantiating the WeakMap with a
     185             :         // Lookup that can't be constructed from a Cell*. The WeakKeyTable
     186             :         // mechanism is indexed with a GCCellPtr, so that won't work.
     187           0 :         Ptr p = Base::lookup(static_cast<Lookup>(origKey.asCell()));
     188           0 :         MOZ_ASSERT(p.found());
     189             : 
     190           0 :         Key key(p->key());
     191           0 :         MOZ_ASSERT((markedCell == extractUnbarriered(key)) || (markedCell == getDelegate(key)));
     192           0 :         if (gc::IsMarked(marker->runtime(), &key)) {
     193           0 :             TraceEdge(marker, &p->value(), "ephemeron value");
     194           0 :         } else if (keyNeedsMark(key)) {
     195           0 :             TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
     196           0 :             TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
     197           0 :             MOZ_ASSERT(key == p->key()); // No moving
     198             :         }
     199           0 :         key.unsafeSet(nullptr); // Prevent destructor from running barriers.
     200           0 :     }
     201             : 
     202          15 :     void trace(JSTracer* trc) override {
     203          15 :         MOZ_ASSERT_IF(JS::CurrentThreadIsHeapBusy(), isInList());
     204             : 
     205          15 :         TraceNullableEdge(trc, &memberOf, "WeakMap owner");
     206             : 
     207          15 :         if (!Base::initialized())
     208           0 :             return;
     209             : 
     210          15 :         if (trc->isMarkingTracer()) {
     211           2 :             MOZ_ASSERT(trc->weakMapAction() == ExpandWeakMaps);
     212           2 :             marked = true;
     213           2 :             (void) markIteratively(GCMarker::fromTracer(trc));
     214           2 :             return;
     215             :         }
     216             : 
     217          13 :         if (trc->weakMapAction() == DoNotTraceWeakMaps)
     218           0 :             return;
     219             : 
     220             :         // Trace keys only if weakMapAction() says to.
     221          13 :         if (trc->weakMapAction() == TraceWeakMapKeysValues) {
     222         417 :             for (Enum e(*this); !e.empty(); e.popFront())
     223         404 :                 TraceEdge(trc, &e.front().mutableKey(), "WeakMap entry key");
     224             :         }
     225             : 
     226             :         // Always trace all values (unless weakMapAction() is
     227             :         // DoNotTraceWeakMaps).
     228         417 :         for (Range r = Base::all(); !r.empty(); r.popFront())
     229         404 :             TraceEdge(trc, &r.front().value(), "WeakMap entry value");
     230             :     }
     231             : 
     232             :   protected:
     233           0 :     static void addWeakEntry(GCMarker* marker, JS::GCCellPtr key, gc::WeakMarkable markable)
     234             :     {
     235           0 :         Zone* zone = key.asCell()->asTenured().zone();
     236             : 
     237           0 :         auto p = zone->gcWeakKeys().get(key);
     238           0 :         if (p) {
     239           0 :             gc::WeakEntryVector& weakEntries = p->value;
     240           0 :             if (!weakEntries.append(Move(markable)))
     241           0 :                 marker->abortLinearWeakMarking();
     242             :         } else {
     243           0 :             gc::WeakEntryVector weakEntries;
     244           0 :             MOZ_ALWAYS_TRUE(weakEntries.append(Move(markable)));
     245           0 :             if (!zone->gcWeakKeys().put(JS::GCCellPtr(key), Move(weakEntries)))
     246           0 :                 marker->abortLinearWeakMarking();
     247             :         }
     248           0 :     }
     249             : 
     250           2 :     bool markIteratively(GCMarker* marker) override {
     251           2 :         MOZ_ASSERT(marked);
     252             : 
     253           2 :         bool markedAny = false;
     254             : 
     255          65 :         for (Enum e(*this); !e.empty(); e.popFront()) {
     256             :             // If the entry is live, ensure its key and value are marked.
     257          63 :             bool keyIsMarked = gc::IsMarked(marker->runtime(), &e.front().mutableKey());
     258          63 :             if (!keyIsMarked && keyNeedsMark(e.front().key())) {
     259           0 :                 TraceEdge(marker, &e.front().mutableKey(), "proxy-preserved WeakMap entry key");
     260           0 :                 keyIsMarked = true;
     261           0 :                 markedAny = true;
     262             :             }
     263             : 
     264          63 :             if (keyIsMarked) {
     265           0 :                 if (!gc::IsMarked(marker->runtime(), &e.front().value())) {
     266           0 :                     TraceEdge(marker, &e.front().value(), "WeakMap entry value");
     267           0 :                     markedAny = true;
     268             :                 }
     269          63 :             } else if (marker->isWeakMarkingTracer()) {
     270             :                 // Entry is not yet known to be live. Record this weakmap and
     271             :                 // the lookup key in the list of weak keys. Also record the
     272             :                 // delegate, if any, because marking the delegate also marks
     273             :                 // the entry.
     274           0 :                 JS::GCCellPtr weakKey(extractUnbarriered(e.front().key()));
     275           0 :                 gc::WeakMarkable markable(this, weakKey);
     276           0 :                 addWeakEntry(marker, weakKey, markable);
     277           0 :                 if (JSObject* delegate = getDelegate(e.front().key()))
     278           0 :                     addWeakEntry(marker, JS::GCCellPtr(delegate), markable);
     279             :             }
     280             :         }
     281             : 
     282           2 :         return markedAny;
     283             :     }
     284             : 
     285          63 :     JSObject* getDelegate(JSObject* key) const {
     286          63 :         JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp();
     287          63 :         if (!op)
     288          63 :             return nullptr;
     289             : 
     290           0 :         JSObject* obj = op(key);
     291           0 :         if (!obj)
     292           0 :             return nullptr;
     293             : 
     294           0 :         MOZ_ASSERT(obj->runtimeFromActiveCooperatingThread() == zone()->runtimeFromActiveCooperatingThread());
     295           0 :         return obj;
     296             :     }
     297             : 
     298           0 :     JSObject* getDelegate(JSScript* script) const {
     299           0 :         return nullptr;
     300             :     }
     301             : 
     302             :   private:
     303         732 :     void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
     304           0 :     void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
     305             : 
     306          63 :     bool keyNeedsMark(JSObject* key) const {
     307          63 :         JSObject* delegate = getDelegate(key);
     308             :         /*
     309             :          * Check if the delegate is marked with any color to properly handle
     310             :          * gray marking when the key's delegate is black and the map is gray.
     311             :          */
     312          63 :         return delegate && gc::IsMarkedUnbarriered(zone()->runtimeFromActiveCooperatingThread(), &delegate);
     313             :     }
     314             : 
     315           0 :     bool keyNeedsMark(JSScript* script) const {
     316           0 :         return false;
     317             :     }
     318             : 
     319           0 :     bool findZoneEdges() override {
     320             :         // This is overridden by ObjectValueMap.
     321           0 :         return true;
     322             :     }
     323             : 
     324           0 :     void sweep() override {
     325             :         /* Remove all entries whose keys remain unmarked. */
     326           0 :         for (Enum e(*this); !e.empty(); e.popFront()) {
     327           0 :             if (gc::IsAboutToBeFinalized(&e.front().mutableKey()))
     328           0 :                 e.removeFront();
     329             :         }
     330             :         /*
     331             :          * Once we've swept, all remaining edges should stay within the
     332             :          * known-live part of the graph.
     333             :          */
     334           0 :         assertEntriesNotAboutToBeFinalized();
     335           0 :     }
     336             : 
     337           0 :     void finish() override {
     338           0 :         Base::finish();
     339           0 :     }
     340             : 
     341             :     /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
     342           0 :     void traceMappings(WeakMapTracer* tracer) override {
     343           0 :         for (Range r = Base::all(); !r.empty(); r.popFront()) {
     344           0 :             gc::Cell* key = gc::ToMarkable(r.front().key());
     345           0 :             gc::Cell* value = gc::ToMarkable(r.front().value());
     346           0 :             if (key && value) {
     347           0 :                 tracer->trace(memberOf,
     348           0 :                               JS::GCCellPtr(r.front().key().get()),
     349           0 :                               JS::GCCellPtr(r.front().value().get()));
     350             :             }
     351             :         }
     352           0 :     }
     353             : 
     354             :   protected:
     355           0 :     void assertEntriesNotAboutToBeFinalized() {
     356             : #if DEBUG
     357           0 :         for (Range r = Base::all(); !r.empty(); r.popFront()) {
     358           0 :             Key k(r.front().key());
     359           0 :             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k));
     360           0 :             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
     361           0 :             MOZ_ASSERT(k == r.front().key());
     362             :         }
     363             : #endif
     364           0 :     }
     365             : };
     366             : 
     367             : /* WeakMap methods exposed so they can be installed in the self-hosting global. */
     368             : 
     369             : extern JSObject*
     370             : InitBareWeakMapCtor(JSContext* cx, js::HandleObject obj);
     371             : 
     372             : extern bool
     373             : WeakMap_has(JSContext* cx, unsigned argc, Value* vp);
     374             : 
     375             : extern bool
     376             : WeakMap_get(JSContext* cx, unsigned argc, Value* vp);
     377             : 
     378             : extern bool
     379             : WeakMap_set(JSContext* cx, unsigned argc, Value* vp);
     380             : 
     381             : extern bool
     382             : WeakMap_delete(JSContext* cx, unsigned argc, Value* vp);
     383             : 
     384             : extern JSObject*
     385             : InitWeakMapClass(JSContext* cx, HandleObject obj);
     386             : 
     387             : 
     388           0 : class ObjectValueMap : public WeakMap<HeapPtr<JSObject*>, HeapPtr<Value>,
     389             :                                       MovableCellHasher<HeapPtr<JSObject*>>>
     390             : {
     391             :   public:
     392          20 :     ObjectValueMap(JSContext* cx, JSObject* obj)
     393          20 :       : WeakMap<HeapPtr<JSObject*>, HeapPtr<Value>,
     394          20 :                 MovableCellHasher<HeapPtr<JSObject*>>>(cx, obj)
     395          20 :     {}
     396             : 
     397             :     virtual bool findZoneEdges();
     398             : };
     399             : 
     400             : 
     401             : // Generic weak map for mapping objects to other objects.
     402           0 : class ObjectWeakMap
     403             : {
     404             :     ObjectValueMap map;
     405             : 
     406             :   public:
     407             :     explicit ObjectWeakMap(JSContext* cx);
     408             :     bool init();
     409             : 
     410             :     JS::Zone* zone() const { return map.zone(); }
     411             : 
     412             :     JSObject* lookup(const JSObject* obj);
     413             :     bool add(JSContext* cx, JSObject* obj, JSObject* target);
     414             :     void clear();
     415             : 
     416             :     void trace(JSTracer* trc);
     417             :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf);
     418           0 :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) {
     419           0 :         return mallocSizeOf(this) + sizeOfExcludingThis(mallocSizeOf);
     420             :     }
     421             : 
     422             : #ifdef JSGC_HASH_TABLE_CHECKS
     423             :     void checkAfterMovingGC();
     424             : #endif
     425             : };
     426             : 
     427             : } /* namespace js */
     428             : 
     429             : namespace JS {
     430             : 
     431             : template <>
     432             : struct DeletePolicy<js::ObjectValueMap> : public js::GCManagedDeletePolicy<js::ObjectValueMap>
     433             : {};
     434             : 
     435             : } /* namespace JS */
     436             : 
     437             : #endif /* jsweakmap_h */

Generated by: LCOV version 1.13