js/src/jsweakmap.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     7 #ifndef jsweakmap_h
     8 #define jsweakmap_h
    10 #include "jscompartment.h"
    11 #include "jsfriendapi.h"
    12 #include "jsobj.h"
    14 #include "gc/Marking.h"
    15 #include "js/HashTable.h"
    17 namespace js {
    19 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
    20 // a key is collected, the table entry disappears, dropping its reference to the value.
    21 //
    22 // More precisely:
    23 //
    24 //     A WeakMap entry is collected if and only if either the WeakMap or the entry's key
    25 //     is collected. If an entry is not collected, it remains in the WeakMap and it has a
    26 //     strong reference to the value.
    27 //
    28 // You must call this table's 'trace' method when the object of which it is a part is
    29 // reached by the garbage collection tracer. Once a table is known to be live, the
    30 // implementation takes care of the iterative marking needed for weak tables and removing
    31 // table entries when collection is complete.
    33 // The value for the next pointer for maps not in the map list.
    34 static WeakMapBase * const WeakMapNotInList = reinterpret_cast<WeakMapBase *>(1);
    36 typedef Vector<WeakMapBase *, 0, SystemAllocPolicy> WeakMapVector;
    38 // Common base class for all WeakMap specializations. The collector uses this to call
    39 // their markIteratively and sweep methods.
    40 class WeakMapBase {
    41   public:
    42     WeakMapBase(JSObject *memOf, JSCompartment *c);
    43     virtual ~WeakMapBase();
    45     void trace(JSTracer *tracer) {
    46         if (IS_GC_MARKING_TRACER(tracer)) {
    47             // We don't do anything with a WeakMap at trace time. Rather, we wait until as
    48             // many keys as possible have been marked, and add ourselves to the list of
    49             // known-live WeakMaps to be scanned in the iterative marking phase, by
    50             // markAllIteratively.
    51             JS_ASSERT(tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps);
    53             // Add ourselves to the list if we are not already in the list. We can already
    54             // be in the list if the weak map is marked more than once due delayed marking.
    55             if (next == WeakMapNotInList) {
    56                 next = compartment->gcWeakMapList;
    57                 compartment->gcWeakMapList = this;
    58             }
    59         } else {
    60             // If we're not actually doing garbage collection, the keys won't be marked
    61             // nicely as needed by the true ephemeral marking algorithm --- custom tracers
    62             // such as the cycle collector must use their own means for cycle detection.
    63             // So here we do a conservative approximation: pretend all keys are live.
    64             if (tracer->eagerlyTraceWeakMaps() == DoNotTraceWeakMaps)
    65                 return;
    67             nonMarkingTraceValues(tracer);
    68             if (tracer->eagerlyTraceWeakMaps() == TraceWeakMapKeysValues)
    69                 nonMarkingTraceKeys(tracer);
    70         }
    71     }
    73     // Garbage collector entry points.
    75     // Check all weak maps in a compartment that have been marked as live in this garbage
    76     // collection, and mark the values of all entries that have become strong references
    77     // to them. Return true if we marked any new values, indicating that we need to make
    78     // another pass. In other words, mark my marked maps' marked members' mid-collection.
    79     static bool markCompartmentIteratively(JSCompartment *c, JSTracer *tracer);
    81     // Remove entries whose keys are dead from all weak maps in a compartment marked as
    82     // live in this garbage collection.
    83     static void sweepCompartment(JSCompartment *c);
    85     // Trace all delayed weak map bindings. Used by the cycle collector.
    86     static void traceAllMappings(WeakMapTracer *tracer);
    88     bool isInList() { return next != WeakMapNotInList; }
    89     void check() { JS_ASSERT(!isInList()); }
    91     // Remove everything from the weak map list for a compartment.
    92     static void resetCompartmentWeakMapList(JSCompartment *c);
    94     // Save the live weak map list for a compartment, appending the data to a vector.
    95     static bool saveCompartmentWeakMapList(JSCompartment *c, WeakMapVector &vector);
    97     // Restore live weak map lists for multiple compartments from a vector.
    98     static void restoreCompartmentWeakMapLists(WeakMapVector &vector);
   100     // Remove a weakmap from the live weakmaps list
   101     static void removeWeakMapFromList(WeakMapBase *weakmap);
   103   protected:
   104     // Instance member functions called by the above. Instantiations of WeakMap override
   105     // these with definitions appropriate for their Key and Value types.
   106     virtual void nonMarkingTraceKeys(JSTracer *tracer) = 0;
   107     virtual void nonMarkingTraceValues(JSTracer *tracer) = 0;
   108     virtual bool markIteratively(JSTracer *tracer) = 0;
   109     virtual void sweep() = 0;
   110     virtual void traceMappings(WeakMapTracer *tracer) = 0;
   112     // Object that this weak map is part of, if any.
   113     JSObject *memberOf;
   115     // Compartment that this weak map is part of.
   116     JSCompartment *compartment;
   118   private:
   119     // Link in a list of WeakMaps to mark iteratively and sweep in this garbage
   120     // collection, headed by JSCompartment::gcWeakMapList. The last element of
   121     // the list has nullptr as its next. Maps not in the list have
   122     // WeakMapNotInList as their next.  We must distinguish these cases to
   123     // avoid creating infinite lists when a weak map gets traced twice due to
   124     // delayed marking.
   125     WeakMapBase *next;
   126 };
   128 template <class Key, class Value,
   129           class HashPolicy = DefaultHasher<Key> >
   130 class WeakMap : public HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy>, public WeakMapBase
   131 {
   132   public:
   133     typedef HashMap<Key, Value, HashPolicy, RuntimeAllocPolicy> Base;
   134     typedef typename Base::Enum Enum;
   135     typedef typename Base::Lookup Lookup;
   136     typedef typename Base::Range Range;
   138     explicit WeakMap(JSContext *cx, JSObject *memOf = nullptr)
   139         : Base(cx->runtime()), WeakMapBase(memOf, cx->compartment()) { }
   141   private:
   142     bool markValue(JSTracer *trc, Value *x) {
   143         if (gc::IsMarked(x))
   144             return false;
   145         gc::Mark(trc, x, "WeakMap entry value");
   146         JS_ASSERT(gc::IsMarked(x));
   147         return true;
   148     }
   150     void nonMarkingTraceKeys(JSTracer *trc) {
   151         for (Enum e(*this); !e.empty(); e.popFront()) {
   152             Key key(e.front().key());
   153             gc::Mark(trc, &key, "WeakMap entry key");
   154             if (key != e.front().key())
   155                 entryMoved(e, key);
   156         }
   157     }
   159     void nonMarkingTraceValues(JSTracer *trc) {
   160         for (Range r = Base::all(); !r.empty(); r.popFront())
   161             gc::Mark(trc, &r.front().value(), "WeakMap entry value");
   162     }
   164     bool keyNeedsMark(JSObject *key) {
   165         if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
   166             JSObject *delegate = op(key);
   167             /*
   168              * Check if the delegate is marked with any color to properly handle
   169              * gray marking when the key's delegate is black and the map is
   170              * gray.
   171              */
   172             return delegate && gc::IsObjectMarked(&delegate);
   173         }
   174         return false;
   175     }
   177     bool keyNeedsMark(gc::Cell *cell) {
   178         return false;
   179     }
   181     bool markIteratively(JSTracer *trc) {
   182         bool markedAny = false;
   183         for (Enum e(*this); !e.empty(); e.popFront()) {
   184             /* If the entry is live, ensure its key and value are marked. */
   185             Key key(e.front().key());
   186             if (gc::IsMarked(const_cast<Key *>(&key))) {
   187                 if (markValue(trc, &e.front().value()))
   188                     markedAny = true;
   189                 if (e.front().key() != key)
   190                     entryMoved(e, key);
   191             } else if (keyNeedsMark(key)) {
   192                 gc::Mark(trc, &e.front().value(), "WeakMap entry value");
   193                 gc::Mark(trc, &key, "proxy-preserved WeakMap entry key");
   194                 if (e.front().key() != key)
   195                     entryMoved(e, key);
   196                 markedAny = true;
   197             }
   198             key.unsafeSet(nullptr);
   199         }
   200         return markedAny;
   201     }
   203     void sweep() {
   204         /* Remove all entries whose keys remain unmarked. */
   205         for (Enum e(*this); !e.empty(); e.popFront()) {
   206             Key k(e.front().key());
   207             if (gc::IsAboutToBeFinalized(&k))
   208                 e.removeFront();
   209             else if (k != e.front().key())
   210                 entryMoved(e, k);
   211         }
   212         /*
   213          * Once we've swept, all remaining edges should stay within the
   214          * known-live part of the graph.
   215          */
   216         assertEntriesNotAboutToBeFinalized();
   217     }
   219     /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
   220     void traceMappings(WeakMapTracer *tracer) {
   221         for (Range r = Base::all(); !r.empty(); r.popFront()) {
   222             gc::Cell *key = gc::ToMarkable(r.front().key());
   223             gc::Cell *value = gc::ToMarkable(r.front().value());
   224             if (key && value) {
   225                 tracer->callback(tracer, memberOf,
   226                                  key, gc::TraceKind(r.front().key()),
   227                                  value, gc::TraceKind(r.front().value()));
   228             }
   229         }
   230     }
   232     /* Rekey an entry when moved, ensuring we do not trigger barriers. */
   233     void entryMoved(Enum &eArg, const Key &k) {
   234         typedef typename HashMap<typename Unbarriered<Key>::type,
   235                                  typename Unbarriered<Value>::type,
   236                                  typename Unbarriered<HashPolicy>::type,
   237                                  RuntimeAllocPolicy>::Enum UnbarrieredEnum;
   238         UnbarrieredEnum &e = reinterpret_cast<UnbarrieredEnum &>(eArg);
   239         e.rekeyFront(reinterpret_cast<const typename Unbarriered<Key>::type &>(k));
   240     }
   242 protected:
   243     void assertEntriesNotAboutToBeFinalized() {
   244 #if DEBUG
   245         for (Range r = Base::all(); !r.empty(); r.popFront()) {
   246             Key k(r.front().key());
   247             JS_ASSERT(!gc::IsAboutToBeFinalized(&k));
   248             JS_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
   249             JS_ASSERT(k == r.front().key());
   250         }
   251 #endif
   252     }
   253 };
   255 } /* namespace js */
   257 extern JSObject *
   258 js_InitWeakMapClass(JSContext *cx, js::HandleObject obj);
   260 #endif /* jsweakmap_h */

mercurial