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:
Terrence Cole 2015-07-20 08:20:35 -07:00
Родитель 5689b3b6a6
Коммит 4819fcdd0f
5 изменённых файлов: 304 добавлений и 17 удалений

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

@ -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',