зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1185749 - Implement a DynamicTraceable HashMap subclass that can be used with Rooted; r=jonco
--HG-- extra : rebase_source : ec7e634e3cc4f6a60bddc613cda346ec9d347d89
This commit is contained in:
Родитель
5689b3b6a6
Коммит
4819fcdd0f
|
@ -11,6 +11,7 @@
|
|||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/LinkedList.h"
|
||||
#include "mozilla/Move.h"
|
||||
#include "mozilla/TypeTraits.h"
|
||||
|
||||
#include "jspubtd.h"
|
||||
|
@ -643,7 +644,7 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
|
|||
|
||||
/* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
|
||||
template <typename CX>
|
||||
void init(CX* cx) {
|
||||
void registerWithRootLists(CX* cx) {
|
||||
js::ThingRootKind kind = js::RootKind<T>::rootKind();
|
||||
this->stack = &cx->roots.stackRoots_[kind];
|
||||
this->prev = *stack;
|
||||
|
@ -656,15 +657,16 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
|
|||
: ptr(js::GCMethods<T>::initial())
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(js::ContextFriendFields::get(cx));
|
||||
registerWithRootLists(js::ContextFriendFields::get(cx));
|
||||
}
|
||||
|
||||
Rooted(JSContext* cx, const T& initial
|
||||
template <typename S>
|
||||
Rooted(JSContext* cx, S&& initial
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: ptr(initial)
|
||||
: ptr(mozilla::Forward<S>(initial))
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(js::ContextFriendFields::get(cx));
|
||||
registerWithRootLists(js::ContextFriendFields::get(cx));
|
||||
}
|
||||
|
||||
explicit Rooted(js::ContextFriendFields* cx
|
||||
|
@ -672,15 +674,16 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
|
|||
: ptr(js::GCMethods<T>::initial())
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(cx);
|
||||
registerWithRootLists(cx);
|
||||
}
|
||||
|
||||
Rooted(js::ContextFriendFields* cx, const T& initial
|
||||
template <typename S>
|
||||
Rooted(js::ContextFriendFields* cx, S&& initial
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: ptr(initial)
|
||||
: ptr(mozilla::Forward<S>(initial))
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(cx);
|
||||
registerWithRootLists(cx);
|
||||
}
|
||||
|
||||
explicit Rooted(js::PerThreadDataFriendFields* pt
|
||||
|
@ -688,15 +691,16 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
|
|||
: ptr(js::GCMethods<T>::initial())
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(pt);
|
||||
registerWithRootLists(pt);
|
||||
}
|
||||
|
||||
Rooted(js::PerThreadDataFriendFields* pt, const T& initial
|
||||
template <typename S>
|
||||
Rooted(js::PerThreadDataFriendFields* pt, S&& initial
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: ptr(initial)
|
||||
: ptr(mozilla::Forward<S>(initial))
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(pt);
|
||||
registerWithRootLists(pt);
|
||||
}
|
||||
|
||||
explicit Rooted(JSRuntime* rt
|
||||
|
@ -704,15 +708,16 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
|
|||
: ptr(js::GCMethods<T>::initial())
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(js::PerThreadDataFriendFields::getMainThread(rt));
|
||||
registerWithRootLists(js::PerThreadDataFriendFields::getMainThread(rt));
|
||||
}
|
||||
|
||||
Rooted(JSRuntime* rt, const T& initial
|
||||
template <typename S>
|
||||
Rooted(JSRuntime* rt, S&& initial
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
|
||||
: ptr(initial)
|
||||
: ptr(mozilla::Forward<S>(initial))
|
||||
{
|
||||
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
|
||||
init(js::PerThreadDataFriendFields::getMainThread(rt));
|
||||
registerWithRootLists(js::PerThreadDataFriendFields::getMainThread(rt));
|
||||
}
|
||||
|
||||
~Rooted() {
|
||||
|
|
|
@ -0,0 +1,186 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
* 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_HashTable_h
|
||||
#define gc_HashTable_h
|
||||
|
||||
#include "js/HashTable.h"
|
||||
#include "js/RootingAPI.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
template <typename> struct DefaultTracer;
|
||||
|
||||
// A TraceableHashMap is a HashMap with an additional trace method that knows
|
||||
// how to visit all keys and values in the table. HashMaps that contain GC
|
||||
// pointers that must be traced to be kept alive will generally want to use
|
||||
// this TraceableHashMap specializeation in lieu of HashMap.
|
||||
//
|
||||
// Most types of GC pointers as keys and values can be traced with no extra
|
||||
// infrastructure. For structs and non-gc-pointer members, ensure that there
|
||||
// is a specialization of DefaultTracer<T> with an appropriate trace method
|
||||
// available to handle the custom type.
|
||||
//
|
||||
// Note that although this HashMap's trace will deal correctly with moved keys,
|
||||
// it does not itself know when to barrier or trace keys. To function properly
|
||||
// it must either be used with Rooted, or barriered and traced manually.
|
||||
template <typename Key,
|
||||
typename Value,
|
||||
typename HashPolicy = DefaultHasher<Key>,
|
||||
typename AllocPolicy = TempAllocPolicy,
|
||||
typename KeyTraceFunc = DefaultTracer<Key>,
|
||||
typename ValueTraceFunc = DefaultTracer<Value>>
|
||||
class TraceableHashMap : public HashMap<Key, Value, HashPolicy, AllocPolicy>,
|
||||
public JS::DynamicTraceable
|
||||
{
|
||||
using Base = HashMap<Key, Value, HashPolicy, AllocPolicy>;
|
||||
|
||||
public:
|
||||
explicit TraceableHashMap(AllocPolicy a = AllocPolicy()) : Base(a) {}
|
||||
|
||||
void trace(JSTracer* trc) override {
|
||||
if (!this->initialized())
|
||||
return;
|
||||
for (typename Base::Enum e(*this); !e.empty(); e.popFront()) {
|
||||
ValueTraceFunc::trace(trc, &e.front().value(), "hashmap value");
|
||||
Key key = e.front().key();
|
||||
KeyTraceFunc::trace(trc, &key, "hashmap key");
|
||||
if (key != e.front().key())
|
||||
e.rekeyFront(key);
|
||||
}
|
||||
}
|
||||
|
||||
// TraceableHashMap is movable
|
||||
TraceableHashMap(TraceableHashMap&& rhs) : Base(mozilla::Forward<TraceableHashMap>(rhs)) {}
|
||||
void operator=(TraceableHashMap&& rhs) {
|
||||
MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited");
|
||||
Base::operator=(mozilla::Forward<TraceableHashMap>(rhs));
|
||||
}
|
||||
|
||||
private:
|
||||
// TraceableHashMap is not copyable or assignable
|
||||
TraceableHashMap(const TraceableHashMap& hm) = delete;
|
||||
TraceableHashMap& operator=(const TraceableHashMap& hm) = delete;
|
||||
};
|
||||
|
||||
template <typename Outer, typename... MapArgs>
|
||||
class TraceableHashMapOperations
|
||||
{
|
||||
using Map = TraceableHashMap<MapArgs...>;
|
||||
using Lookup = typename Map::Lookup;
|
||||
using Ptr = typename Map::Ptr;
|
||||
using AddPtr = typename Map::AddPtr;
|
||||
using Range = typename Map::Range;
|
||||
using Enum = typename Map::Enum;
|
||||
|
||||
const Map& map() const { return static_cast<const Outer*>(this)->extract(); }
|
||||
|
||||
public:
|
||||
bool initialized() const { return map().initialized(); }
|
||||
Ptr lookup(const Lookup& l) const { return map().lookup(l); }
|
||||
AddPtr lookupForAdd(const Lookup& l) const { return map().lookupForAdd(l); }
|
||||
Range all() const { return map().all(); }
|
||||
bool empty() const { return map().empty(); }
|
||||
uint32_t count() const { return map().count(); }
|
||||
size_t capacity() const { return map().capacity(); }
|
||||
uint32_t generation() const { return map().generation(); }
|
||||
bool has(const Lookup& l) const { return map().lookup(l).found(); }
|
||||
};
|
||||
|
||||
template <typename Outer, typename... MapArgs>
|
||||
class MutableTraceableHashMapOperations
|
||||
: public TraceableHashMapOperations<Outer, MapArgs...>
|
||||
{
|
||||
using Map = TraceableHashMap<MapArgs...>;
|
||||
using Lookup = typename Map::Lookup;
|
||||
using Ptr = typename Map::Ptr;
|
||||
using AddPtr = typename Map::AddPtr;
|
||||
using Range = typename Map::Range;
|
||||
using Enum = typename Map::Enum;
|
||||
|
||||
Map& map() { return static_cast<Outer*>(this)->extract(); }
|
||||
|
||||
public:
|
||||
bool init(uint32_t len = 16) { return map().init(len); }
|
||||
void clear() { map().clear(); }
|
||||
void finish() { map().finish(); }
|
||||
void remove(Ptr p) { map().remove(p); }
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) {
|
||||
return map().add(p, mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
|
||||
template<typename KeyInput>
|
||||
bool add(AddPtr& p, KeyInput&& k) {
|
||||
return map().add(p, mozilla::Forward<KeyInput>(k), Map::Value());
|
||||
}
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) {
|
||||
return map().relookupOrAdd(p, k,
|
||||
mozilla::Forward<KeyInput>(k),
|
||||
mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool put(KeyInput&& k, ValueInput&& v) {
|
||||
return map().put(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
|
||||
template<typename KeyInput, typename ValueInput>
|
||||
bool putNew(KeyInput&& k, ValueInput&& v) {
|
||||
return map().putNew(mozilla::Forward<KeyInput>(k), mozilla::Forward<ValueInput>(v));
|
||||
}
|
||||
};
|
||||
|
||||
template <template <typename...> class TraceableHashMap, typename... MapArgs>
|
||||
class RootedBase<TraceableHashMap<MapArgs...>>
|
||||
: public MutableTraceableHashMapOperations<JS::Rooted<TraceableHashMap<MapArgs...>>, MapArgs...>
|
||||
{
|
||||
using Map = TraceableHashMap<MapArgs...>;
|
||||
|
||||
friend class TraceableHashMapOperations<JS::Rooted<Map>, MapArgs...>;
|
||||
const Map& extract() const { return *static_cast<const JS::Rooted<Map>*>(this)->address(); }
|
||||
|
||||
friend class MutableTraceableHashMapOperations<JS::Rooted<Map>, MapArgs...>;
|
||||
Map& extract() { return *static_cast<JS::Rooted<Map>*>(this)->address(); }
|
||||
};
|
||||
|
||||
template <template <typename...> class TraceableHashMap, typename... MapArgs>
|
||||
class MutableHandleBase<TraceableHashMap<MapArgs...>>
|
||||
: public MutableTraceableHashMapOperations<JS::MutableHandle<TraceableHashMap<MapArgs...>>,
|
||||
MapArgs...>
|
||||
{
|
||||
using Map = TraceableHashMap<MapArgs...>;
|
||||
|
||||
friend class TraceableHashMapOperations<JS::MutableHandle<Map>, MapArgs...>;
|
||||
const Map& extract() const {
|
||||
return *static_cast<const JS::MutableHandle<Map>*>(this)->address();
|
||||
}
|
||||
|
||||
friend class MutableTraceableHashMapOperations<JS::MutableHandle<Map>, MapArgs...>;
|
||||
Map& extract() { return *static_cast<JS::MutableHandle<Map>*>(this)->address(); }
|
||||
};
|
||||
|
||||
template <template <typename...> class TraceableHashMap, typename... MapArgs>
|
||||
class HandleBase<TraceableHashMap<MapArgs...>>
|
||||
: public TraceableHashMapOperations<JS::Handle<TraceableHashMap<MapArgs...>>, MapArgs...>
|
||||
{
|
||||
using Map = TraceableHashMap<MapArgs...>;
|
||||
friend class TraceableHashMapOperations<JS::Handle<Map>, MapArgs...>;
|
||||
const Map& extract() const { return *static_cast<const JS::Handle<Map>*>(this)->address(); }
|
||||
};
|
||||
|
||||
// The default implementation of DefaultTracer will leave alone POD types.
|
||||
template <typename T> struct DefaultTracer {
|
||||
static_assert(mozilla::IsPod<T>::value, "non-pod types must not be ignored");
|
||||
static void trace(JSTracer* trc, T* t, const char* name) {}
|
||||
};
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
#endif /* gc_HashTable_h */
|
|
@ -10,6 +10,7 @@
|
|||
#include "jsfriendapi.h"
|
||||
|
||||
#include "gc/Barrier.h"
|
||||
#include "js/TraceableHashTable.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -130,6 +131,22 @@ TraceCycleCollectorChildren(JS::CallbackTracer* trc, ObjectGroup* group);
|
|||
|
||||
} // namespace gc
|
||||
|
||||
template <typename T>
|
||||
struct DefaultTracer<T*>
|
||||
{
|
||||
static void trace(JSTracer* trc, T** t, const char* name) {
|
||||
TraceManuallyBarrieredEdge(trc, t, name);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct DefaultTracer<RelocatablePtr<T*>>
|
||||
{
|
||||
static void trace(JSTracer* trc, RelocatablePtr<T*> t, const char* name) {
|
||||
TraceEdge(trc, t, name);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace js
|
||||
|
||||
#endif /* js_Tracer_h */
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
* 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/. */
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TraceableHashTable.h"
|
||||
|
||||
#include "jsapi-tests/tests.h"
|
||||
|
||||
BEGIN_TEST(testGCExactRooting)
|
||||
|
@ -152,3 +155,78 @@ BEGIN_TEST(testGCRootedDynamicStructInternalStackStorageAugmented)
|
|||
return true;
|
||||
}
|
||||
END_TEST(testGCRootedDynamicStructInternalStackStorageAugmented)
|
||||
|
||||
using MyHashMap = js::TraceableHashMap<js::Shape*, JSObject*>;
|
||||
|
||||
BEGIN_TEST(testGCRootedHashMap)
|
||||
{
|
||||
JS::Rooted<MyHashMap> map(cx, MyHashMap(cx));
|
||||
CHECK(map.init(15));
|
||||
CHECK(map.initialized());
|
||||
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
||||
RootedValue val(cx, UndefinedValue());
|
||||
char buffer[2];
|
||||
buffer[0] = 'a' + i;
|
||||
buffer[1] = '\0';
|
||||
CHECK(JS_SetProperty(cx, obj, buffer, val));
|
||||
CHECK(map.putNew(obj->as<NativeObject>().lastProperty(), obj));
|
||||
}
|
||||
|
||||
JS_GC(rt);
|
||||
JS_GC(rt);
|
||||
|
||||
for (auto r = map.all(); !r.empty(); r.popFront()) {
|
||||
RootedObject obj(cx, r.front().value());
|
||||
CHECK(obj->as<NativeObject>().lastProperty() == r.front().key());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testGCRootedHashMap)
|
||||
|
||||
static bool
|
||||
FillMyHashMap(JSContext* cx, MutableHandle<MyHashMap> map)
|
||||
{
|
||||
for (size_t i = 0; i < 10; ++i) {
|
||||
RootedObject obj(cx, JS_NewObject(cx, nullptr));
|
||||
RootedValue val(cx, UndefinedValue());
|
||||
char buffer[2];
|
||||
buffer[0] = 'a' + i;
|
||||
buffer[1] = '\0';
|
||||
if (!JS_SetProperty(cx, obj, buffer, val))
|
||||
return false;
|
||||
if (!map.putNew(obj->as<NativeObject>().lastProperty(), obj))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
CheckMyHashMap(JSContext* cx, Handle<MyHashMap> map)
|
||||
{
|
||||
for (auto r = map.all(); !r.empty(); r.popFront()) {
|
||||
RootedObject obj(cx, r.front().value());
|
||||
if (obj->as<NativeObject>().lastProperty() != r.front().key())
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
BEGIN_TEST(testGCHandleHashMap)
|
||||
{
|
||||
JS::Rooted<MyHashMap> map(cx, MyHashMap(cx));
|
||||
CHECK(map.init(15));
|
||||
CHECK(map.initialized());
|
||||
|
||||
CHECK(FillMyHashMap(cx, &map));
|
||||
|
||||
JS_GC(rt);
|
||||
JS_GC(rt);
|
||||
|
||||
CHECK(CheckMyHashMap(cx, map));
|
||||
|
||||
return true;
|
||||
}
|
||||
END_TEST(testGCHandleHashMap)
|
||||
|
|
|
@ -122,6 +122,7 @@ EXPORTS.js += [
|
|||
'../public/RootingAPI.h',
|
||||
'../public/SliceBudget.h',
|
||||
'../public/StructuredClone.h',
|
||||
'../public/TraceableHashTable.h',
|
||||
'../public/TraceKind.h',
|
||||
'../public/TracingAPI.h',
|
||||
'../public/TrackedOptimizationInfo.h',
|
||||
|
|
Загрузка…
Ссылка в новой задаче