Bug 1479388 - Move most WeakMap inline method definitions into a separate header r=sfink

This commit is contained in:
Jon Coppeard 2018-07-31 09:50:08 +01:00
Родитель 8c1a2345bc
Коммит e0ec59962b
9 изменённых файлов: 37 добавлений и 202 удалений

Просмотреть файл

@ -11,6 +11,8 @@
#include "vm/ProxyObject.h" #include "vm/ProxyObject.h"
#include "gc/WeakMap-inl.h"
namespace js { namespace js {
static bool static bool

Просмотреть файл

@ -8,7 +8,7 @@
#define builtin_WeakMapObject_h #define builtin_WeakMapObject_h
#include "gc/WeakMap.h" #include "gc/WeakMap.h"
#include "vm/JSObject.h" #include "vm/NativeObject.h"
namespace js { namespace js {

Просмотреть файл

@ -14,6 +14,7 @@
#include "ds/InlineTable.h" #include "ds/InlineTable.h"
#include "frontend/ParseNode.h" #include "frontend/ParseNode.h"
#include "frontend/TokenStream.h" #include "frontend/TokenStream.h"
#include "gc/Zone.h"
#include "vm/BytecodeUtil.h" #include "vm/BytecodeUtil.h"
#include "vm/EnvironmentObject.h" #include "vm/EnvironmentObject.h"
#include "vm/JSAtom.h" #include "vm/JSAtom.h"

Просмотреть файл

@ -4,7 +4,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gc/WeakMap.h" #include "gc/WeakMap-inl.h"
#include <string.h> #include <string.h>

Просмотреть файл

@ -8,20 +8,24 @@
#define gc_WeakMap_h #define gc_WeakMap_h
#include "mozilla/LinkedList.h" #include "mozilla/LinkedList.h"
#include "mozilla/Move.h"
#include "jsfriendapi.h"
#include "gc/Barrier.h"
#include "gc/DeletePolicy.h" #include "gc/DeletePolicy.h"
#include "gc/StoreBuffer.h"
#include "js/HashTable.h" #include "js/HashTable.h"
#include "vm/JSObject.h"
#include "vm/Realm.h" namespace JS {
class Zone;
} // namespace JS
namespace js { namespace js {
class GCMarker; class GCMarker;
class WeakMapBase; class WeakMapBase;
struct WeakMapTracer;
namespace gc {
struct WeakMarkable;
} // namespace gc
// A subclass template of js::HashMap whose keys and values may be garbage-collected. When // 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. // a key is collected, the table entry disappears, dropping its reference to the value.
@ -49,7 +53,7 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
WeakMapBase(JSObject* memOf, JS::Zone* zone); WeakMapBase(JSObject* memOf, JS::Zone* zone);
virtual ~WeakMapBase(); virtual ~WeakMapBase();
Zone* zone() const { return zone_; } JS::Zone* zone() const { return zone_; }
// Garbage collector entry points. // Garbage collector entry points.
@ -107,17 +111,6 @@ class WeakMapBase : public mozilla::LinkedListElement<WeakMapBase>
bool marked; bool marked;
}; };
template <typename T>
static T extractUnbarriered(const WriteBarrieredBase<T>& v)
{
return v.get();
}
template <typename T>
static T* extractUnbarriered(T* v)
{
return v;
}
template <class Key, class Value, template <class Key, class Value,
class HashPolicy = DefaultHasher<Key> > class HashPolicy = DefaultHasher<Key> >
class WeakMap : public HashMap<Key, Value, HashPolicy, ZoneAllocPolicy>, class WeakMap : public HashMap<Key, Value, HashPolicy, ZoneAllocPolicy>,
@ -132,16 +125,9 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, ZoneAllocPolicy>,
typedef typename Base::Ptr Ptr; typedef typename Base::Ptr Ptr;
typedef typename Base::AddPtr AddPtr; typedef typename Base::AddPtr AddPtr;
explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr) explicit WeakMap(JSContext* cx, JSObject* memOf = nullptr);
: Base(cx->zone()), WeakMapBase(memOf, cx->zone()) { }
bool init(uint32_t len = 16) { bool init(uint32_t len = 16);
if (!Base::init(len))
return false;
zone()->gcWeakMapList().insertFront(this);
marked = JS::IsIncrementalGCInProgress(TlsContext.get());
return true;
}
// Overwritten to add a read barrier to prevent an incorrectly gray value // Overwritten to add a read barrier to prevent an incorrectly gray value
// from escaping the weak map. See the UnmarkGrayTracer::onChild comment in // from escaping the weak map. See the UnmarkGrayTracer::onChild comment in
@ -170,204 +156,46 @@ class WeakMap : public HashMap<Key, Value, HashPolicy, ZoneAllocPolicy>,
// Resolve ambiguity with LinkedListElement<>::remove. // Resolve ambiguity with LinkedListElement<>::remove.
using Base::remove; using Base::remove;
// Trace a WeakMap entry based on 'markedCell' getting marked, where void markEntry(GCMarker* marker, gc::Cell* markedCell, JS::GCCellPtr origKey) override;
// 'origKey' is the key in the weakmap. These will probably be the same,
// but can be different eg when markedCell is a delegate for origKey.
//
// This implementation does not use 'markedCell'; it looks up origKey and
// checks the mark bits on everything it cares about, one of which will be
// markedCell. But a subclass might use it to optimize the liveness check.
void markEntry(GCMarker* marker, gc::Cell* markedCell, JS::GCCellPtr origKey) override
{
MOZ_ASSERT(marked);
// If this cast fails, then you're instantiating the WeakMap with a void trace(JSTracer* trc) override;
// Lookup that can't be constructed from a Cell*. The WeakKeyTable
// mechanism is indexed with a GCCellPtr, so that won't work.
Ptr p = Base::lookup(static_cast<Lookup>(origKey.asCell()));
MOZ_ASSERT(p.found());
Key key(p->key());
MOZ_ASSERT((markedCell == extractUnbarriered(key)) || (markedCell == getDelegate(key)));
if (gc::IsMarked(marker->runtime(), &key)) {
TraceEdge(marker, &p->value(), "ephemeron value");
} else if (keyNeedsMark(key)) {
TraceEdge(marker, &p->value(), "WeakMap ephemeron value");
TraceEdge(marker, &key, "proxy-preserved WeakMap ephemeron key");
MOZ_ASSERT(key == p->key()); // No moving
}
key.unsafeSet(nullptr); // Prevent destructor from running barriers.
}
void trace(JSTracer* trc) override {
MOZ_ASSERT_IF(JS::RuntimeHeapIsBusy(), isInList());
TraceNullableEdge(trc, &memberOf, "WeakMap owner");
if (!Base::initialized())
return;
if (trc->isMarkingTracer()) {
MOZ_ASSERT(trc->weakMapAction() == ExpandWeakMaps);
marked = true;
(void) markIteratively(GCMarker::fromTracer(trc));
return;
}
if (trc->weakMapAction() == DoNotTraceWeakMaps)
return;
// Trace keys only if weakMapAction() says to.
if (trc->weakMapAction() == TraceWeakMapKeysValues) {
for (Enum e(*this); !e.empty(); e.popFront())
TraceEdge(trc, &e.front().mutableKey(), "WeakMap entry key");
}
// Always trace all values (unless weakMapAction() is
// DoNotTraceWeakMaps).
for (Range r = Base::all(); !r.empty(); r.popFront())
TraceEdge(trc, &r.front().value(), "WeakMap entry value");
}
protected: protected:
static void addWeakEntry(GCMarker* marker, JS::GCCellPtr key, gc::WeakMarkable markable) static void addWeakEntry(GCMarker* marker, JS::GCCellPtr key,
{ const gc::WeakMarkable& markable);
Zone* zone = key.asCell()->asTenured().zone();
auto p = zone->gcWeakKeys().get(key); bool markIteratively(GCMarker* marker) override;
if (p) {
gc::WeakEntryVector& weakEntries = p->value;
if (!weakEntries.append(std::move(markable)))
marker->abortLinearWeakMarking();
} else {
gc::WeakEntryVector weakEntries;
MOZ_ALWAYS_TRUE(weakEntries.append(std::move(markable)));
if (!zone->gcWeakKeys().put(JS::GCCellPtr(key), std::move(weakEntries)))
marker->abortLinearWeakMarking();
}
}
bool markIteratively(GCMarker* marker) override { JSObject* getDelegate(JSObject* key) const;
MOZ_ASSERT(marked); JSObject* getDelegate(JSScript* script) const;
JSObject* getDelegate(LazyScript* script) const;
bool markedAny = false;
for (Enum e(*this); !e.empty(); e.popFront()) {
// If the entry is live, ensure its key and value are marked.
bool keyIsMarked = gc::IsMarked(marker->runtime(), &e.front().mutableKey());
if (!keyIsMarked && keyNeedsMark(e.front().key())) {
TraceEdge(marker, &e.front().mutableKey(), "proxy-preserved WeakMap entry key");
keyIsMarked = true;
markedAny = true;
}
if (keyIsMarked) {
if (!gc::IsMarked(marker->runtime(), &e.front().value())) {
TraceEdge(marker, &e.front().value(), "WeakMap entry value");
markedAny = true;
}
} else if (marker->isWeakMarkingTracer()) {
// Entry is not yet known to be live. Record this weakmap and
// the lookup key in the list of weak keys. Also record the
// delegate, if any, because marking the delegate also marks
// the entry.
JS::GCCellPtr weakKey(extractUnbarriered(e.front().key()));
gc::WeakMarkable markable(this, weakKey);
addWeakEntry(marker, weakKey, markable);
if (JSObject* delegate = getDelegate(e.front().key()))
addWeakEntry(marker, JS::GCCellPtr(delegate), markable);
}
}
return markedAny;
}
JSObject* getDelegate(JSObject* key) const {
JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp();
if (!op)
return nullptr;
JSObject* obj = op(key);
if (!obj)
return nullptr;
MOZ_ASSERT(obj->runtimeFromMainThread() == zone()->runtimeFromMainThread());
return obj;
}
JSObject* getDelegate(JSScript* script) const {
return nullptr;
}
JSObject* getDelegate(LazyScript* script) const {
return nullptr;
}
private: private:
void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); } void exposeGCThingToActiveJS(const JS::Value& v) const { JS::ExposeValueToActiveJS(v); }
void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); } void exposeGCThingToActiveJS(JSObject* obj) const { JS::ExposeObjectToActiveJS(obj); }
bool keyNeedsMark(JSObject* key) const { bool keyNeedsMark(JSObject* key) const;
JSObject* delegate = getDelegate(key); bool keyNeedsMark(JSScript* script) const;
/* bool keyNeedsMark(LazyScript* script) const;
* Check if the delegate is marked with any color to properly handle
* gray marking when the key's delegate is black and the map is gray.
*/
return delegate && gc::IsMarkedUnbarriered(zone()->runtimeFromMainThread(), &delegate);
}
bool keyNeedsMark(JSScript* script) const {
return false;
}
bool keyNeedsMark(LazyScript* script) const {
return false;
}
bool findZoneEdges() override { bool findZoneEdges() override {
// This is overridden by ObjectValueMap. // This is overridden by ObjectValueMap.
return true; return true;
} }
void sweep() override { void sweep() override;
/* Remove all entries whose keys remain unmarked. */
for (Enum e(*this); !e.empty(); e.popFront()) {
if (gc::IsAboutToBeFinalized(&e.front().mutableKey()))
e.removeFront();
}
/*
* Once we've swept, all remaining edges should stay within the
* known-live part of the graph.
*/
assertEntriesNotAboutToBeFinalized();
}
void finish() override { void finish() override {
Base::finish(); Base::finish();
} }
/* memberOf can be nullptr, which means that the map is not part of a JSObject. */ /* memberOf can be nullptr, which means that the map is not part of a JSObject. */
void traceMappings(WeakMapTracer* tracer) override { void traceMappings(WeakMapTracer* tracer) override;
for (Range r = Base::all(); !r.empty(); r.popFront()) {
gc::Cell* key = gc::ToMarkable(r.front().key());
gc::Cell* value = gc::ToMarkable(r.front().value());
if (key && value) {
tracer->trace(memberOf,
JS::GCCellPtr(r.front().key().get()),
JS::GCCellPtr(r.front().value().get()));
}
}
}
protected: protected:
void assertEntriesNotAboutToBeFinalized() {
#if DEBUG #if DEBUG
for (Range r = Base::all(); !r.empty(); r.popFront()) { void assertEntriesNotAboutToBeFinalized();
Key k(r.front().key());
MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k));
MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
MOZ_ASSERT(k == r.front().key());
}
#endif #endif
}
}; };

Просмотреть файл

@ -6,7 +6,7 @@
#include "js/WeakMapPtr.h" #include "js/WeakMapPtr.h"
#include "gc/WeakMap.h" #include "gc/WeakMap-inl.h"
// //
// Machinery for the externally-linkable JS::WeakMapPtr, which wraps js::WeakMap // Machinery for the externally-linkable JS::WeakMapPtr, which wraps js::WeakMap

Просмотреть файл

@ -9,6 +9,7 @@
#include "vm/Debugger.h" #include "vm/Debugger.h"
#include "gc/WeakMap-inl.h"
#include "vm/Stack-inl.h" #include "vm/Stack-inl.h"
/* static */ inline bool /* static */ inline bool

Просмотреть файл

@ -219,7 +219,9 @@ class DebuggerWeakMap : private WeakMap<HeapPtr<UnbarrieredKey>, HeapPtr<JSObjec
e.removeFront(); e.removeFront();
} }
} }
#ifdef DEBUG
Base::assertEntriesNotAboutToBeFinalized(); Base::assertEntriesNotAboutToBeFinalized();
#endif
} }
MOZ_MUST_USE bool incZoneCount(JS::Zone* zone) { MOZ_MUST_USE bool incZoneCount(JS::Zone* zone) {

Просмотреть файл

@ -16,6 +16,7 @@
#include "vm/ArrayBufferObject.h" #include "vm/ArrayBufferObject.h"
#include "vm/ErrorObject.h" #include "vm/ErrorObject.h"
#include "vm/JSFunction.h" #include "vm/JSFunction.h"
#include "vm/Realm.h"
#include "vm/RegExpStatics.h" #include "vm/RegExpStatics.h"
#include "vm/Runtime.h" #include "vm/Runtime.h"