1/*
2 * Copyright (C) 2013-2018 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#include <wtf/DumbPtrTraits.h>
29#include <wtf/HashCountedSet.h>
30#include <wtf/RefPtr.h>
31
32namespace WebCore {
33
34class Node;
35
36class GCReachableRefMap {
37public:
38 static inline bool contains(Node& node) { return map().contains(&node); }
39 static inline void add(Node& node) { map().add(&node); }
40 static inline void remove(Node& node) { map().remove(&node); }
41
42private:
43 static HashCountedSet<Node*>& map();
44};
45
46template <typename T, typename = std::enable_if_t<std::is_same<T, typename std::remove_const<T>::type>::value>>
47class GCReachableRef {
48 WTF_MAKE_NONCOPYABLE(GCReachableRef);
49public:
50
51 template<typename = std::enable_if_t<std::is_base_of<Node, T>::value>>
52 GCReachableRef(T& object)
53 : m_ptr(&object)
54 {
55 GCReachableRefMap::add(*m_ptr);
56 }
57
58 ~GCReachableRef()
59 {
60 if (m_ptr)
61 GCReachableRefMap::remove(*m_ptr);
62 }
63
64 GCReachableRef(GCReachableRef&& other)
65 : m_ptr(WTFMove(other.m_ptr))
66 {
67 }
68
69 T* operator->() const { return &get(); }
70 T* ptr() const RETURNS_NONNULL { return &get(); }
71 T& get() const { ASSERT(m_ptr); return *m_ptr; }
72 operator T&() const { return get(); }
73 bool operator!() const { return !get(); }
74
75 // Hash table deleted values, which are only constructed and never copied or destroyed.
76 GCReachableRef(WTF::HashTableDeletedValueType)
77 : m_ptr(RefPtr<T>::hashTableDeletedValue())
78 { }
79 bool isHashTableDeletedValue() const { return m_ptr.isHashTableDeletedValue(); }
80
81 GCReachableRef(WTF::HashTableEmptyValueType)
82 : m_ptr(nullptr)
83 { }
84 bool isHashTableEmptyValue() const { return !m_ptr; }
85
86 const T* ptrAllowingHashTableEmptyValue() const { ASSERT(m_ptr || isHashTableEmptyValue()); return m_ptr.get(); }
87 T* ptrAllowingHashTableEmptyValue() { ASSERT(m_ptr || isHashTableEmptyValue()); return m_ptr.get(); }
88
89 void assignToHashTableEmptyValue(GCReachableRef&& reference)
90 {
91 ASSERT(!m_ptr);
92 m_ptr = WTFMove(reference.m_ptr);
93 ASSERT(m_ptr);
94 }
95
96private:
97 RefPtr<T> m_ptr;
98};
99
100} // namespace WebCore
101
102namespace WTF {
103
104template<typename P> struct HashTraits<WebCore::GCReachableRef<P>> : SimpleClassHashTraits<WebCore::GCReachableRef<P>> {
105 static const bool emptyValueIsZero = true;
106 static WebCore::GCReachableRef<P> emptyValue() { return HashTableEmptyValue; }
107
108 template <typename>
109 static void constructEmptyValue(WebCore::GCReachableRef<P>& slot)
110 {
111 new (NotNull, std::addressof(slot)) WebCore::GCReachableRef<P>(HashTableEmptyValue);
112 }
113
114 static const bool hasIsEmptyValueFunction = true;
115 static bool isEmptyValue(const WebCore::GCReachableRef<P>& value) { return value.isHashTableEmptyValue(); }
116
117 static void assignToEmpty(WebCore::GCReachableRef<P>& emptyValue, WebCore::GCReachableRef<P>&& newValue)
118 {
119 ASSERT(isEmptyValue(emptyValue));
120 emptyValue.assignToHashTableEmptyValue(WTFMove(newValue));
121 }
122
123 typedef P* PeekType;
124 static PeekType peek(const Ref<P>& value) { return const_cast<PeekType>(value.ptrAllowingHashTableEmptyValue()); }
125 static PeekType peek(P* value) { return value; }
126
127 typedef Optional<Ref<P>> TakeType;
128 static TakeType take(Ref<P>&& value) { return isEmptyValue(value) ? WTF::nullopt : Optional<Ref<P>>(WTFMove(value)); }
129};
130
131template <typename T, typename U>
132struct GetPtrHelper<WebCore::GCReachableRef<T, U>> {
133 typedef T* PtrType;
134 static T* getPtr(const WebCore::GCReachableRef<T, U>& reference) { return const_cast<T*>(reference.ptr()); }
135};
136
137template <typename T, typename U>
138struct IsSmartPtr<WebCore::GCReachableRef<T, U>> {
139 static const bool value = true;
140};
141
142template<typename P> struct PtrHash<WebCore::GCReachableRef<P>> : PtrHashBase<WebCore::GCReachableRef<P>, IsSmartPtr<WebCore::GCReachableRef<P>>::value> {
143 static const bool safeToCompareToEmptyOrDeleted = false;
144};
145
146template<typename P> struct DefaultHash<WebCore::GCReachableRef<P>> {
147 typedef PtrHash<WebCore::GCReachableRef<P>> Hash;
148};
149
150} // namespace WTF
151
152