#ifndef WeakGCMap_h
#define WeakGCMap_h
-#include "Collector.h"
+#include "Handle.h"
+#include "JSGlobalData.h"
#include <wtf/HashMap.h>
namespace JSC {
-class JSCell;
+// A HashMap for GC'd values that removes entries when the associated value
+// dies.
+template <typename KeyType, typename MappedType> struct DefaultWeakGCMapFinalizerCallback {
+ static void* finalizerContextFor(KeyType key)
+ {
+ return reinterpret_cast<void*>(key);
+ }
+
+ static KeyType keyForFinalizer(void* context, typename HandleTypes<MappedType>::ExternalType)
+ {
+ return reinterpret_cast<KeyType>(context);
+ }
+};
-// A HashMap whose get() function returns emptyValue() for cells awaiting destruction.
-template<typename KeyType, typename MappedType>
-class WeakGCMap : public FastAllocBase {
- /*
- Invariants:
- * A value enters the WeakGCMap marked. (Guaranteed by set().)
- * A value that becomes unmarked leaves the WeakGCMap before being recycled. (Guaranteed by the value's destructor removing it from the WeakGCMap.)
- * A value that becomes unmarked leaves the WeakGCMap before becoming marked again. (Guaranteed by all destructors running before the mark phase begins.)
- * During the mark phase, all values in the WeakGCMap are valid. (Guaranteed by all destructors running before the mark phase begins.)
- */
+template<typename KeyType, typename MappedType, typename FinalizerCallback = DefaultWeakGCMapFinalizerCallback<KeyType, MappedType>, typename HashArg = typename DefaultHash<KeyType>::Hash, typename KeyTraitsArg = HashTraits<KeyType> >
+class WeakGCMap : private WeakHandleOwner {
+ WTF_MAKE_FAST_ALLOCATED;
+ WTF_MAKE_NONCOPYABLE(WeakGCMap);
+
+ typedef HashMap<KeyType, WeakImpl*, HashArg, KeyTraitsArg> MapType;
+ typedef typename HandleTypes<MappedType>::ExternalType ExternalType;
+ typedef typename MapType::iterator map_iterator;
public:
- typedef typename HashMap<KeyType, MappedType>::iterator iterator;
- typedef typename HashMap<KeyType, MappedType>::const_iterator const_iterator;
-
+
+ struct iterator {
+ friend class WeakGCMap;
+ iterator(map_iterator iter)
+ : m_iterator(iter)
+ {
+ }
+
+ std::pair<KeyType, ExternalType> get() const { return std::make_pair(m_iterator->first, HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&m_iterator->second->jsValue()))); }
+
+ iterator& operator++() { ++m_iterator; return *this; }
+
+ // postfix ++ intentionally omitted
+
+ // Comparison.
+ bool operator==(const iterator& other) const { return m_iterator == other.m_iterator; }
+ bool operator!=(const iterator& other) const { return m_iterator != other.m_iterator; }
+
+ private:
+ map_iterator m_iterator;
+ };
+
+ typedef WTF::HashTableAddResult<iterator> AddResult;
+
+ WeakGCMap()
+ {
+ }
+
bool isEmpty() { return m_map.isEmpty(); }
+ void clear()
+ {
+ map_iterator end = m_map.end();
+ for (map_iterator ptr = m_map.begin(); ptr != end; ++ptr)
+ WeakSet::deallocate(ptr->second);
+ m_map.clear();
+ }
- MappedType get(const KeyType& key) const;
- pair<iterator, bool> set(const KeyType&, const MappedType&);
- MappedType take(const KeyType& key);
+ bool contains(const KeyType& key) const
+ {
+ return m_map.contains(key);
+ }
- // These unchecked functions provide access to a value even if the value's
- // mark bit is not set. This is used, among other things, to retrieve values
- // during the GC mark phase, which begins by clearing all mark bits.
+ iterator find(const KeyType& key)
+ {
+ return m_map.find(key);
+ }
- MappedType uncheckedGet(const KeyType& key) const { return m_map.get(key); }
- bool uncheckedRemove(const KeyType&, const MappedType&);
+ void remove(iterator iter)
+ {
+ ASSERT(iter.m_iterator != m_map.end());
+ WeakImpl* impl = iter.m_iterator->second;
+ ASSERT(impl);
+ WeakSet::deallocate(impl);
+ m_map.remove(iter.m_iterator);
+ }
- iterator uncheckedBegin() { return m_map.begin(); }
- iterator uncheckedEnd() { return m_map.end(); }
+ ExternalType get(const KeyType& key) const
+ {
+ return HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&m_map.get(key)->jsValue()));
+ }
- const_iterator uncheckedBegin() const { return m_map.begin(); }
- const_iterator uncheckedEnd() const { return m_map.end(); }
+ AddResult add(JSGlobalData&, const KeyType& key, ExternalType value)
+ {
+ typename MapType::AddResult result = m_map.add(key, 0);
+ if (result.isNewEntry)
+ result.iterator->second = WeakSet::allocate(value, this, FinalizerCallback::finalizerContextFor(key));
-private:
- HashMap<KeyType, MappedType> m_map;
-};
+ // WeakGCMap exposes a different iterator, so we need to wrap it and create our own AddResult.
+ return AddResult(iterator(result.iterator), result.isNewEntry);
+ }
-template<typename KeyType, typename MappedType>
-inline MappedType WeakGCMap<KeyType, MappedType>::get(const KeyType& key) const
-{
- MappedType result = m_map.get(key);
- if (result == HashTraits<MappedType>::emptyValue())
- return result;
- if (!Heap::isCellMarked(result))
- return HashTraits<MappedType>::emptyValue();
- return result;
-}
-
-template<typename KeyType, typename MappedType>
-MappedType WeakGCMap<KeyType, MappedType>::take(const KeyType& key)
-{
- MappedType result = m_map.take(key);
- if (result == HashTraits<MappedType>::emptyValue())
+ void set(JSGlobalData& globalData, const KeyType& key, ExternalType value)
+ {
+ ASSERT_UNUSED(globalData, globalData.apiLock().currentThreadIsHoldingLock());
+ typename MapType::AddResult result = m_map.add(key, 0);
+ if (!result.isNewEntry)
+ WeakSet::deallocate(result.iterator->second);
+ result.iterator->second = WeakSet::allocate(value, this, FinalizerCallback::finalizerContextFor(key));
+ }
+
+ ExternalType take(const KeyType& key)
+ {
+ WeakImpl* impl = m_map.take(key);
+ if (!impl)
+ return HashTraits<ExternalType>::emptyValue();
+ ExternalType result = HandleTypes<MappedType>::getFromSlot(const_cast<JSValue*>(&impl->jsValue()));
+ WeakSet::deallocate(impl);
return result;
- if (!Heap::isCellMarked(result))
- return HashTraits<MappedType>::emptyValue();
- return result;
-}
-
-template<typename KeyType, typename MappedType>
-pair<typename HashMap<KeyType, MappedType>::iterator, bool> WeakGCMap<KeyType, MappedType>::set(const KeyType& key, const MappedType& value)
-{
- Heap::markCell(value); // If value is newly allocated, it's not marked, so mark it now.
- pair<iterator, bool> result = m_map.add(key, value);
- if (!result.second) { // pre-existing entry
- result.second = !Heap::isCellMarked(result.first->second);
- result.first->second = value;
}
- return result;
-}
-
-template<typename KeyType, typename MappedType>
-bool WeakGCMap<KeyType, MappedType>::uncheckedRemove(const KeyType& key, const MappedType& value)
-{
- iterator it = m_map.find(key);
- if (it == m_map.end())
- return false;
- if (it->second != value)
- return false;
- m_map.remove(it);
- return true;
-}
+
+ size_t size() { return m_map.size(); }
+
+ iterator begin() { return iterator(m_map.begin()); }
+ iterator end() { return iterator(m_map.end()); }
+
+ ~WeakGCMap()
+ {
+ clear();
+ }
+
+private:
+ virtual void finalize(Handle<Unknown> handle, void* context)
+ {
+ WeakImpl* impl = m_map.take(FinalizerCallback::keyForFinalizer(context, HandleTypes<MappedType>::getFromSlot(handle.slot())));
+ ASSERT(impl);
+ WeakSet::deallocate(impl);
+ }
+
+ MapType m_map;
+};
} // namespace JSC