/* -*- 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 GCHashTable_h #define GCHashTable_h #include "js/HashTable.h" #include "js/RootingAPI.h" #include "js/TracingAPI.h" namespace js { // Define a reasonable default GC policy for GC-aware Maps. template struct DefaultMapGCPolicy { using KeyPolicy = DefaultGCPolicy; using ValuePolicy = DefaultGCPolicy; static bool needsSweep(Key* key, Value* value) { return KeyPolicy::needsSweep(key) || ValuePolicy::needsSweep(value); } }; // A GCHashMap is a GC-aware HashMap, meaning that it has additional trace and // sweep methods that know how to visit all keys and values in the table. // HashMaps that contain GC pointers will generally want to use this GCHashMap // specialization in lieu of HashMap, either because those pointers must be // traced to be kept alive -- in which case, KeyPolicy and/or ValuePolicy // should do the appropriate tracing -- or because those pointers are weak and // must be swept during a GC -- in which case needsSweep should be set // appropriately. // // Most types of GC pointers as keys and values can be traced with no extra // infrastructure. For structs, the DefaultGCPolicy will call a trace() // method on the struct. For other structs and non-gc-pointer members, ensure // that there is a specialization of DefaultGCPolicy with an appropriate // trace() static method available to handle the custom type. Generic helpers // can be found in js/public/TracingAPI.h. // // Note that this HashMap only knows *how* to trace and sweep (and the tracing // can handle keys that move), but it does not itself cause tracing or sweeping // to be invoked. For tracing, it must be used with Rooted or PersistentRooted, // or barriered and traced manually. For sweeping, currently it requires an // explicit call to .sweep(). // template , typename AllocPolicy = TempAllocPolicy, typename GCPolicy = DefaultMapGCPolicy> class GCHashMap : public HashMap, public JS::Traceable { using Base = HashMap; public: explicit GCHashMap(AllocPolicy a = AllocPolicy()) : Base(a) {} static void trace(GCHashMap* map, JSTracer* trc) { map->trace(trc); } void trace(JSTracer* trc) { if (!this->initialized()) return; for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { GCPolicy::ValuePolicy::trace(trc, &e.front().value(), "hashmap value"); GCPolicy::KeyPolicy::trace(trc, &e.front().mutableKey(), "hashmap key"); } } void sweep() { if (!this->initialized()) return; for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { if (GCPolicy::needsSweep(&e.front().mutableKey(), &e.front().value())) e.removeFront(); } } // GCHashMap is movable GCHashMap(GCHashMap&& rhs) : Base(mozilla::Forward(rhs)) {} void operator=(GCHashMap&& rhs) { MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited"); Base::operator=(mozilla::Forward(rhs)); } private: // GCHashMap is not copyable or assignable GCHashMap(const GCHashMap& hm) = delete; GCHashMap& operator=(const GCHashMap& hm) = delete; }; template class GCHashMapOperations { using Map = GCHashMap; 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(this)->get(); } 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(); } bool has(const Lookup& l) const { return map().lookup(l).found(); } }; template class MutableGCHashMapOperations : public GCHashMapOperations { using Map = GCHashMap; 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(this)->get(); } 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 bool add(AddPtr& p, KeyInput&& k, ValueInput&& v) { return map().add(p, mozilla::Forward(k), mozilla::Forward(v)); } template bool add(AddPtr& p, KeyInput&& k) { return map().add(p, mozilla::Forward(k), Map::Value()); } template bool relookupOrAdd(AddPtr& p, KeyInput&& k, ValueInput&& v) { return map().relookupOrAdd(p, k, mozilla::Forward(k), mozilla::Forward(v)); } template bool put(KeyInput&& k, ValueInput&& v) { return map().put(mozilla::Forward(k), mozilla::Forward(v)); } template bool putNew(KeyInput&& k, ValueInput&& v) { return map().putNew(mozilla::Forward(k), mozilla::Forward(v)); } }; template class RootedBase> : public MutableGCHashMapOperations>, A,B,C,D,E> {}; template class MutableHandleBase> : public MutableGCHashMapOperations>, A,B,C,D,E> {}; template class HandleBase> : public GCHashMapOperations>, A,B,C,D,E> {}; // A GCHashSet is a HashSet with an additional trace method that knows // be traced to be kept alive will generally want to use this GCHashSet // specializeation in lieu of HashSet. // // Most types of GC pointers can be traced with no extra infrastructure. For // structs and non-gc-pointer members, ensure that there is a specialization of // DefaultGCPolicy with an appropriate trace method available to handle the // custom type. Generic helpers can be found in js/public/TracingAPI.h. // // Note that although this HashSet's trace will deal correctly with moved // elements, it does not itself know when to barrier or trace elements. To // function properly it must either be used with Rooted or barriered and traced // manually. template , typename AllocPolicy = TempAllocPolicy, typename GCPolicy = DefaultGCPolicy> class GCHashSet : public HashSet, public JS::Traceable { using Base = HashSet; public: explicit GCHashSet(AllocPolicy a = AllocPolicy()) : Base(a) {} static void trace(GCHashSet* set, JSTracer* trc) { set->trace(trc); } void trace(JSTracer* trc) { if (!this->initialized()) return; for (typename Base::Enum e(*this); !e.empty(); e.popFront()) GCPolicy::trace(trc, &e.mutableFront(), "hashset element"); } void sweep() { if (!this->initialized()) return; for (typename Base::Enum e(*this); !e.empty(); e.popFront()) { if (GCPolicy::needsSweep(&e.mutableFront())) e.removeFront(); } } // GCHashSet is movable GCHashSet(GCHashSet&& rhs) : Base(mozilla::Forward(rhs)) {} void operator=(GCHashSet&& rhs) { MOZ_ASSERT(this != &rhs, "self-move assignment is prohibited"); Base::operator=(mozilla::Forward(rhs)); } private: // GCHashSet is not copyable or assignable GCHashSet(const GCHashSet& hs) = delete; GCHashSet& operator=(const GCHashSet& hs) = delete; }; template class GCHashSetOperations { using Set = GCHashSet; using Lookup = typename Set::Lookup; using Ptr = typename Set::Ptr; using AddPtr = typename Set::AddPtr; using Range = typename Set::Range; using Enum = typename Set::Enum; const Set& set() const { return static_cast(this)->extract(); } public: bool initialized() const { return set().initialized(); } Ptr lookup(const Lookup& l) const { return set().lookup(l); } AddPtr lookupForAdd(const Lookup& l) const { return set().lookupForAdd(l); } Range all() const { return set().all(); } bool empty() const { return set().empty(); } uint32_t count() const { return set().count(); } size_t capacity() const { return set().capacity(); } bool has(const Lookup& l) const { return set().lookup(l).found(); } }; template class MutableGCHashSetOperations : public GCHashSetOperations { using Set = GCHashSet; using Lookup = typename Set::Lookup; using Ptr = typename Set::Ptr; using AddPtr = typename Set::AddPtr; using Range = typename Set::Range; using Enum = typename Set::Enum; Set& set() { return static_cast(this)->extract(); } public: bool init(uint32_t len = 16) { return set().init(len); } void clear() { set().clear(); } void finish() { set().finish(); } void remove(const Lookup& l) { set().remove(l); } template bool add(AddPtr& p, TInput&& t) { return set().add(p, mozilla::Forward(t)); } template bool relookupOrAdd(AddPtr& p, const Lookup& l, TInput&& t) { return set().relookupOrAdd(p, l, mozilla::Forward(t)); } template bool put(TInput&& t) { return set().put(mozilla::Forward(t)); } template bool putNew(TInput&& t) { return set().putNew(mozilla::Forward(t)); } template bool putNew(const Lookup& l, TInput&& t) { return set().putNew(l, mozilla::Forward(t)); } }; template class RootedBase> : public MutableGCHashSetOperations>, T, HP, AP, GP> { using Set = GCHashSet; friend class GCHashSetOperations, T, HP, AP, GP>; const Set& extract() const { return *static_cast*>(this)->address(); } friend class MutableGCHashSetOperations, T, HP, AP, GP>; Set& extract() { return *static_cast*>(this)->address(); } }; template class MutableHandleBase> : public MutableGCHashSetOperations>, T, HP, AP, GP> { using Set = GCHashSet; friend class GCHashSetOperations, T, HP, AP, GP>; const Set& extract() const { return *static_cast*>(this)->address(); } friend class MutableGCHashSetOperations, T, HP, AP, GP>; Set& extract() { return *static_cast*>(this)->address(); } }; template class HandleBase> : public GCHashSetOperations>, T, HP, AP, GP> { using Set = GCHashSet; friend class GCHashSetOperations, T, HP, AP, GP>; const Set& extract() const { return *static_cast*>(this)->address(); } }; } /* namespace js */ #endif /* GCHashTable_h */