The table head of test freshness page should not scroll with the page.
[WebKit-https.git] / Source / WTF / wtf / Poisoned.h
1 /*
2  * Copyright (C) 2017-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. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #pragma once
27
28 #include <cstddef>
29 #include <utility>
30 #include <wtf/Assertions.h>
31
32 #define ENABLE_POISON_ASSERTS 0
33
34 #if !ENABLE(POISON)
35 #undef ENABLE_POISON_ASSERTS
36 #define ENABLE_POISON_ASSERTS 0
37 #endif
38
39 namespace WTF {
40
41 using PoisonedBits = uintptr_t;
42
43 namespace PoisonedImplHelper {
44
45 template<typename T>
46 struct isFunctionPointer : std::integral_constant<bool, std::is_function<typename std::remove_pointer<T>::type>::value> { };
47
48 template<typename T>
49 struct isVoidPointer : std::integral_constant<bool, std::is_void<typename std::remove_pointer<T>::type>::value> { };
50
51 template<typename T>
52 struct isConvertibleToReference : std::integral_constant<bool, !isFunctionPointer<T>::value && !isVoidPointer<T>::value> { };
53
54 template<typename T>
55 typename std::enable_if_t<!isConvertibleToReference<T>::value, int>&
56 asReference(T) { RELEASE_ASSERT_NOT_REACHED(); }
57
58 template<typename T>
59 typename std::enable_if_t<isConvertibleToReference<T>::value, typename std::remove_pointer<T>::type>&
60 asReference(T ptr) { return *ptr; }
61
62 } // namespace PoisonedImplHelper
63
64 enum AlreadyPoisonedTag { AlreadyPoisoned };
65
66 template<uintptr_t& poisonKey>
67 class Poison {
68 public:
69     template<typename PoisonedType = void>
70     static uintptr_t key(const PoisonedType* = nullptr) { return poisonKey; }
71 };
72
73 template<typename Poison, typename T, typename = std::enable_if_t<sizeof(T) == sizeof(void*)>>
74 class Poisoned {
75 public:
76     static constexpr bool isPoisonedType = true;
77
78     Poisoned() { }
79
80     Poisoned(std::nullptr_t) { }
81
82     Poisoned(T ptr)
83         : m_poisonedBits(poison(ptr))
84     { }
85
86     Poisoned(const Poisoned&) = default;
87
88     template<typename Other, typename = std::enable_if_t<Other::isPoisonedType>>
89     Poisoned(const Other& other)
90         : m_poisonedBits(poison<T>(other.unpoisoned()))
91     { }
92
93     Poisoned(Poisoned&&) = default;
94
95     explicit Poisoned(AlreadyPoisonedTag, PoisonedBits poisonedBits)
96         : m_poisonedBits(poisonedBits)
97     { }
98
99 #if ENABLE(POISON_ASSERTS)
100     template<typename U = void*>
101     static bool isPoisoned(U value) { return !value || (reinterpret_cast<uintptr_t>(value) & 0xffff000000000000); }
102     template<typename U = void*>
103     static void assertIsPoisoned(U value) { RELEASE_ASSERT(isPoisoned(value)); }
104     template<typename U = void*>
105     static void assertIsNotPoisoned(U value) { RELEASE_ASSERT(!isPoisoned(value)); }
106 #else
107     template<typename U = void*> static void assertIsPoisoned(U) { }
108     template<typename U = void*> static void assertIsNotPoisoned(U) { }
109 #endif
110     void assertIsPoisoned() const { assertIsPoisoned(m_poisonedBits); }
111     void assertIsNotPoisoned() const { assertIsNotPoisoned(m_poisonedBits); }
112
113     template<typename U = T>
114     U unpoisoned() const { return unpoison<U>(m_poisonedBits); }
115
116     void clear() { m_poisonedBits = 0; }
117
118     auto& operator*() const { ASSERT(m_poisonedBits); return PoisonedImplHelper::asReference(unpoison(m_poisonedBits)); }
119     ALWAYS_INLINE T operator->() const { return unpoison(m_poisonedBits); }
120
121     template<typename U = PoisonedBits>
122     U bits() const { return bitwise_cast<U>(m_poisonedBits); }
123
124     bool operator!() const { return !m_poisonedBits; }
125     explicit operator bool() const { return !!m_poisonedBits; }
126
127     bool operator==(const Poisoned& b) const { return m_poisonedBits == b.m_poisonedBits; }
128     bool operator!=(const Poisoned& b) const { return m_poisonedBits != b.m_poisonedBits; }
129
130     bool operator==(T b) const { return unpoisoned() == b; }
131     bool operator!=(T b) const { return unpoisoned() != b; }
132     bool operator<(T b) const { return unpoisoned() < b; }
133     bool operator<=(T b) const { return unpoisoned() <= b; }
134     bool operator>(T b) const { return unpoisoned() > b; }
135     bool operator>=(T b) const { return unpoisoned() >= b; }
136
137     Poisoned& operator=(T ptr)
138     {
139         m_poisonedBits = poison(ptr);
140         return *this;
141     }
142     Poisoned& operator=(const Poisoned&) = default;
143
144     Poisoned& operator=(std::nullptr_t)
145     {
146         clear();
147         return *this;
148     }
149
150     template<typename Other, typename = std::enable_if_t<Other::isPoisonedType>>
151     Poisoned& operator=(const Other& other)
152     {
153         m_poisonedBits = poison<T>(other.unpoisoned());
154         return *this;
155     }
156
157     void swap(Poisoned& other)
158     {
159         std::swap(m_poisonedBits, other.m_poisonedBits);
160     }
161
162     void swap(std::nullptr_t) { clear(); }
163
164     template<typename Other, typename = std::enable_if_t<Other::isPoisonedType>>
165     void swap(Other& other)
166     {
167         T t1 = this->unpoisoned();
168         T t2 = other.unpoisoned();
169         std::swap(t1, t2);
170         m_poisonedBits = poison(t1);
171         other.m_poisonedBits = other.poison(t2);
172     }
173
174     void swap(T& t2)
175     {
176         T t1 = this->unpoisoned();
177         std::swap(t1, t2);
178         m_poisonedBits = poison(t1);
179     }
180
181     template<class U>
182     T exchange(U&& newValue)
183     {
184         T oldValue = unpoisoned();
185         m_poisonedBits = poison(std::forward<U>(newValue));
186         return oldValue;
187     }
188
189 private:
190     template<typename U>
191     ALWAYS_INLINE PoisonedBits poison(U ptr) const { return poison<PoisonedBits>(this, bitwise_cast<uintptr_t>(ptr)); }
192     template<typename U = T>
193     ALWAYS_INLINE U unpoison(PoisonedBits poisonedBits) const { return unpoison<U>(this, poisonedBits); }
194
195     constexpr static PoisonedBits poison(const Poisoned*, std::nullptr_t) { return 0; }
196 #if ENABLE(POISON)
197     template<typename U>
198     ALWAYS_INLINE static PoisonedBits poison(const Poisoned* thisPoisoned, U ptr) { return ptr ? bitwise_cast<PoisonedBits>(ptr) ^ Poison::key(thisPoisoned) : 0; }
199     template<typename U = T>
200     ALWAYS_INLINE static U unpoison(const Poisoned* thisPoisoned, PoisonedBits poisonedBits) { return poisonedBits ? bitwise_cast<U>(poisonedBits ^ Poison::key(thisPoisoned)) : bitwise_cast<U>(0ll); }
201 #else
202     template<typename U>
203     ALWAYS_INLINE static PoisonedBits poison(const Poisoned*, U ptr) { return bitwise_cast<PoisonedBits>(ptr); }
204     template<typename U = T>
205     ALWAYS_INLINE static U unpoison(const Poisoned*, PoisonedBits poisonedBits) { return bitwise_cast<U>(poisonedBits); }
206 #endif
207
208     PoisonedBits m_poisonedBits { 0 };
209
210     template<typename, typename, typename> friend class Poisoned;
211 };
212
213 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType && U::isPoisonedType && !std::is_same<T, U>::value>>
214 inline bool operator==(const T& a, const U& b) { return a.unpoisoned() == b.unpoisoned(); }
215
216 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType && U::isPoisonedType && !std::is_same<T, U>::value>>
217 inline bool operator!=(const T& a, const U& b) { return a.unpoisoned() != b.unpoisoned(); }
218
219 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType && U::isPoisonedType>>
220 inline bool operator<(const T& a, const U& b) { return a.unpoisoned() < b.unpoisoned(); }
221
222 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType && U::isPoisonedType>>
223 inline bool operator<=(const T& a, const U& b) { return a.unpoisoned() <= b.unpoisoned(); }
224
225 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType && U::isPoisonedType>>
226 inline bool operator>(const T& a, const U& b) { return a.unpoisoned() > b.unpoisoned(); }
227
228 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType && U::isPoisonedType>>
229 inline bool operator>=(const T& a, const U& b) { return a.unpoisoned() >= b.unpoisoned(); }
230
231 template<typename T, typename U, typename = std::enable_if_t<T::isPoisonedType>>
232 inline void swap(T& a, U& b)
233 {
234     a.swap(b);
235 }
236
237 WTF_EXPORT_PRIVATE uintptr_t makePoison();
238
239 template<typename Poison, typename T>
240 struct PoisonedPtrTraits {
241     using StorageType = Poisoned<Poison, T*>;
242
243     template<class U> static ALWAYS_INLINE T* exchange(StorageType& ptr, U&& newValue) { return ptr.exchange(newValue); }
244
245     template<typename Other>
246     static ALWAYS_INLINE void swap(Poisoned<Poison, T*>& a, Other& b) { a.swap(b); }
247
248     static ALWAYS_INLINE T* unwrap(const StorageType& ptr) { return ptr.unpoisoned(); }
249 };
250
251 template<typename Poison, typename T>
252 struct PoisonedValueTraits {
253     using StorageType = Poisoned<Poison, T>;
254
255     template<class U> static ALWAYS_INLINE T exchange(StorageType& val, U&& newValue) { return val.exchange(newValue); }
256
257     template<typename Other>
258     static ALWAYS_INLINE void swap(Poisoned<Poison, T>& a, Other& b) { a.swap(b); }
259
260     static ALWAYS_INLINE T unwrap(const StorageType& val) { return val.unpoisoned(); }
261 };
262
263 } // namespace WTF
264
265 using WTF::AlreadyPoisoned;
266 using WTF::Poison;
267 using WTF::Poisoned;
268 using WTF::PoisonedBits;
269 using WTF::PoisonedPtrTraits;
270 using WTF::PoisonedValueTraits;
271 using WTF::makePoison;