1 | /* |
2 | * Copyright (C) 2005-2019 Apple Inc. All rights reserved. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public License |
15 | * along with this library; see the file COPYING.LIB. If not, write to |
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | * |
19 | */ |
20 | |
21 | #pragma once |
22 | |
23 | #include <wtf/Platform.h> |
24 | |
25 | #if USE(CF) || defined(__OBJC__) |
26 | |
27 | #include <wtf/HashTraits.h> |
28 | #include <algorithm> |
29 | #include <cstddef> |
30 | |
31 | #if USE(CF) |
32 | #include <CoreFoundation/CoreFoundation.h> |
33 | #endif |
34 | |
35 | #ifdef __OBJC__ |
36 | #import <Foundation/Foundation.h> |
37 | #endif |
38 | |
39 | #ifndef CF_BRIDGED_TYPE |
40 | #define CF_BRIDGED_TYPE(T) |
41 | #endif |
42 | |
43 | #ifndef CF_RELEASES_ARGUMENT |
44 | #define CF_RELEASES_ARGUMENT |
45 | #endif |
46 | |
47 | #ifndef NS_RELEASES_ARGUMENT |
48 | #define NS_RELEASES_ARGUMENT |
49 | #endif |
50 | |
51 | namespace WTF { |
52 | |
53 | // Unlike most most of our smart pointers, RetainPtr can take either the pointer type or the pointed-to type, |
54 | // so both RetainPtr<NSDictionary> and RetainPtr<CFDictionaryRef> will work. |
55 | |
56 | template<typename T> class RetainPtr; |
57 | |
58 | template<typename T> RetainPtr<T> adoptCF(T CF_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; |
59 | #ifdef __OBJC__ |
60 | template<typename T> RetainPtr<typename RetainPtr<T>::HelperPtrType> adoptNS(T NS_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; |
61 | #endif |
62 | |
63 | template<typename T> class RetainPtr { |
64 | public: |
65 | typedef typename std::remove_pointer<T>::type ValueType; |
66 | typedef ValueType* PtrType; |
67 | typedef CFTypeRef StorageType; |
68 | |
69 | #ifdef __OBJC__ |
70 | typedef typename std::conditional<std::is_convertible<T, id>::value && !std::is_same<T, id>::value, typename std::remove_pointer<T>::type, T>::type HelperPtrType; |
71 | #else |
72 | typedef T HelperPtrType; |
73 | #endif |
74 | |
75 | RetainPtr() : m_ptr(nullptr) { } |
76 | RetainPtr(PtrType ptr) : m_ptr(toStorageType(ptr)) { if (m_ptr) CFRetain(m_ptr); } |
77 | |
78 | RetainPtr(const RetainPtr& o) : m_ptr(o.m_ptr) { if (StorageType ptr = m_ptr) CFRetain(ptr); } |
79 | |
80 | RetainPtr(RetainPtr&& o) : m_ptr(toStorageType(o.leakRef())) { } |
81 | template<typename U> RetainPtr(RetainPtr<U>&& o) : m_ptr(toStorageType(o.leakRef())) { } |
82 | |
83 | // Hash table deleted values, which are only constructed and never copied or destroyed. |
84 | RetainPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { } |
85 | bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); } |
86 | |
87 | ~RetainPtr(); |
88 | |
89 | template<typename U> RetainPtr(const RetainPtr<U>&); |
90 | |
91 | void clear(); |
92 | PtrType leakRef() WARN_UNUSED_RETURN; |
93 | PtrType autorelease(); |
94 | #ifdef __OBJC__ |
95 | id bridgingAutorelease(); |
96 | #endif |
97 | |
98 | PtrType get() const { return fromStorageType(m_ptr); } |
99 | PtrType operator->() const { return fromStorageType(m_ptr); } |
100 | explicit operator PtrType() const { return fromStorageType(m_ptr); } |
101 | explicit operator bool() const { return m_ptr; } |
102 | |
103 | bool operator!() const { return !m_ptr; } |
104 | |
105 | // This conversion operator allows implicit conversion to bool but not to other integer types. |
106 | typedef StorageType RetainPtr::*UnspecifiedBoolType; |
107 | operator UnspecifiedBoolType() const { return m_ptr ? &RetainPtr::m_ptr : nullptr; } |
108 | |
109 | RetainPtr& operator=(const RetainPtr&); |
110 | template<typename U> RetainPtr& operator=(const RetainPtr<U>&); |
111 | RetainPtr& operator=(PtrType); |
112 | template<typename U> RetainPtr& operator=(U*); |
113 | |
114 | RetainPtr& operator=(RetainPtr&&); |
115 | template<typename U> RetainPtr& operator=(RetainPtr<U>&&); |
116 | |
117 | void swap(RetainPtr&); |
118 | |
119 | template<typename U> friend RetainPtr<U> adoptCF(U CF_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; |
120 | #ifdef __OBJC__ |
121 | template<typename U> friend RetainPtr<typename RetainPtr<U>::HelperPtrType> adoptNS(U NS_RELEASES_ARGUMENT) WARN_UNUSED_RETURN; |
122 | #endif |
123 | |
124 | private: |
125 | enum AdoptTag { Adopt }; |
126 | RetainPtr(PtrType ptr, AdoptTag) : m_ptr(toStorageType(ptr)) { } |
127 | |
128 | static PtrType hashTableDeletedValue() { return reinterpret_cast<PtrType>(-1); } |
129 | |
130 | #ifdef __OBJC__ |
131 | template<typename U> |
132 | typename std::enable_if<std::is_convertible<U, id>::value, PtrType>::type |
133 | fromStorageTypeHelper(StorageType ptr) const |
134 | { |
135 | return (__bridge PtrType)const_cast<CF_BRIDGED_TYPE(id) void*>(ptr); |
136 | } |
137 | |
138 | template<typename U> |
139 | typename std::enable_if<!std::is_convertible<U, id>::value, PtrType>::type |
140 | fromStorageTypeHelper(StorageType ptr) const |
141 | { |
142 | return (PtrType)const_cast<CF_BRIDGED_TYPE(id) void*>(ptr); |
143 | } |
144 | |
145 | PtrType fromStorageType(StorageType ptr) const { return fromStorageTypeHelper<PtrType>(ptr); } |
146 | StorageType toStorageType(id ptr) const { return (__bridge StorageType)ptr; } |
147 | StorageType toStorageType(CFTypeRef ptr) const { return (StorageType)ptr; } |
148 | #else |
149 | PtrType fromStorageType(StorageType ptr) const |
150 | { |
151 | return (PtrType)const_cast<CF_BRIDGED_TYPE(id) void*>(ptr); |
152 | } |
153 | StorageType toStorageType(PtrType ptr) const { return (StorageType)ptr; } |
154 | #endif |
155 | |
156 | #ifdef __OBJC__ |
157 | template<typename U> std::enable_if_t<std::is_convertible<U, id>::value, PtrType> autoreleaseHelper(); |
158 | template<typename U> std::enable_if_t<!std::is_convertible<U, id>::value, PtrType> autoreleaseHelper(); |
159 | #endif |
160 | |
161 | StorageType m_ptr; |
162 | }; |
163 | |
164 | // Helper function for creating a RetainPtr using template argument deduction. |
165 | template<typename T> RetainPtr<typename RetainPtr<T>::HelperPtrType> retainPtr(T) WARN_UNUSED_RETURN; |
166 | |
167 | template<typename T> inline RetainPtr<T>::~RetainPtr() |
168 | { |
169 | if (StorageType ptr = std::exchange(m_ptr, nullptr)) |
170 | CFRelease(ptr); |
171 | } |
172 | |
173 | template<typename T> template<typename U> inline RetainPtr<T>::RetainPtr(const RetainPtr<U>& o) |
174 | : m_ptr(toStorageType(o.get())) |
175 | { |
176 | if (StorageType ptr = m_ptr) |
177 | CFRetain(ptr); |
178 | } |
179 | |
180 | template<typename T> inline void RetainPtr<T>::clear() |
181 | { |
182 | if (StorageType ptr = std::exchange(m_ptr, nullptr)) |
183 | CFRelease(ptr); |
184 | } |
185 | |
186 | template<typename T> inline auto RetainPtr<T>::leakRef() -> PtrType |
187 | { |
188 | return fromStorageType(std::exchange(m_ptr, nullptr)); |
189 | } |
190 | |
191 | #ifndef __OBJC__ |
192 | |
193 | template<typename T> inline auto RetainPtr<T>::autorelease() -> PtrType |
194 | { |
195 | if (m_ptr) |
196 | CFAutorelease(m_ptr); |
197 | return leakRef(); |
198 | } |
199 | |
200 | #else |
201 | |
202 | template<typename T> template<typename U> inline auto RetainPtr<T>::autoreleaseHelper() -> std::enable_if_t<std::is_convertible<U, id>::value, PtrType> |
203 | { |
204 | return CFBridgingRelease(std::exchange(m_ptr, nullptr)); |
205 | } |
206 | |
207 | template<typename T> template<typename U> inline auto RetainPtr<T>::autoreleaseHelper() -> std::enable_if_t<!std::is_convertible<U, id>::value, PtrType> |
208 | { |
209 | if (m_ptr) |
210 | CFAutorelease(m_ptr); |
211 | return leakRef(); |
212 | } |
213 | |
214 | template<typename T> inline auto RetainPtr<T>::autorelease() -> PtrType |
215 | { |
216 | return autoreleaseHelper<PtrType>(); |
217 | } |
218 | |
219 | // FIXME: It would be nice if we could base the return type on the type that is toll-free bridged with T rather than using id. |
220 | template<typename T> inline id RetainPtr<T>::bridgingAutorelease() |
221 | { |
222 | static_assert((!std::is_convertible<PtrType, id>::value), "Don't use bridgingAutorelease for Objective-C pointer types." ); |
223 | return CFBridgingRelease(leakRef()); |
224 | } |
225 | |
226 | #endif |
227 | |
228 | template<typename T> inline RetainPtr<T>& RetainPtr<T>::operator=(const RetainPtr& o) |
229 | { |
230 | RetainPtr ptr = o; |
231 | swap(ptr); |
232 | return *this; |
233 | } |
234 | |
235 | template<typename T> template<typename U> inline RetainPtr<T>& RetainPtr<T>::operator=(const RetainPtr<U>& o) |
236 | { |
237 | RetainPtr ptr = o; |
238 | swap(ptr); |
239 | return *this; |
240 | } |
241 | |
242 | template<typename T> inline RetainPtr<T>& RetainPtr<T>::operator=(PtrType optr) |
243 | { |
244 | RetainPtr ptr = optr; |
245 | swap(ptr); |
246 | return *this; |
247 | } |
248 | |
249 | template<typename T> template<typename U> inline RetainPtr<T>& RetainPtr<T>::operator=(U* optr) |
250 | { |
251 | RetainPtr ptr = optr; |
252 | swap(ptr); |
253 | return *this; |
254 | } |
255 | |
256 | template<typename T> inline RetainPtr<T>& RetainPtr<T>::operator=(RetainPtr&& o) |
257 | { |
258 | RetainPtr ptr = WTFMove(o); |
259 | swap(ptr); |
260 | return *this; |
261 | } |
262 | |
263 | template<typename T> template<typename U> inline RetainPtr<T>& RetainPtr<T>::operator=(RetainPtr<U>&& o) |
264 | { |
265 | RetainPtr ptr = WTFMove(o); |
266 | swap(ptr); |
267 | return *this; |
268 | } |
269 | |
270 | template<typename T> inline void RetainPtr<T>::swap(RetainPtr& o) |
271 | { |
272 | std::swap(m_ptr, o.m_ptr); |
273 | } |
274 | |
275 | template<typename T> inline void swap(RetainPtr<T>& a, RetainPtr<T>& b) |
276 | { |
277 | a.swap(b); |
278 | } |
279 | |
280 | template<typename T, typename U> inline bool operator==(const RetainPtr<T>& a, const RetainPtr<U>& b) |
281 | { |
282 | return a.get() == b.get(); |
283 | } |
284 | |
285 | template<typename T, typename U> inline bool operator==(const RetainPtr<T>& a, U* b) |
286 | { |
287 | return a.get() == b; |
288 | } |
289 | |
290 | template<typename T, typename U> inline bool operator==(T* a, const RetainPtr<U>& b) |
291 | { |
292 | return a == b.get(); |
293 | } |
294 | |
295 | template<typename T, typename U> inline bool operator!=(const RetainPtr<T>& a, const RetainPtr<U>& b) |
296 | { |
297 | return a.get() != b.get(); |
298 | } |
299 | |
300 | template<typename T, typename U> inline bool operator!=(const RetainPtr<T>& a, U* b) |
301 | { |
302 | return a.get() != b; |
303 | } |
304 | |
305 | template<typename T, typename U> inline bool operator!=(T* a, const RetainPtr<U>& b) |
306 | { |
307 | return a != b.get(); |
308 | } |
309 | |
310 | template<typename T> inline RetainPtr<T> adoptCF(T CF_RELEASES_ARGUMENT ptr) |
311 | { |
312 | #ifdef __OBJC__ |
313 | static_assert((!std::is_convertible<T, id>::value), "Don't use adoptCF with Objective-C pointer types, use adoptNS." ); |
314 | #endif |
315 | return RetainPtr<T>(ptr, RetainPtr<T>::Adopt); |
316 | } |
317 | |
318 | #ifdef __OBJC__ |
319 | template<typename T> inline RetainPtr<typename RetainPtr<T>::HelperPtrType> adoptNS(T NS_RELEASES_ARGUMENT ptr) |
320 | { |
321 | #if __has_feature(objc_arc) |
322 | return ptr; |
323 | #elif defined(OBJC_NO_GC) |
324 | return RetainPtr<typename RetainPtr<T>::HelperPtrType>(ptr, RetainPtr<typename RetainPtr<T>::HelperPtrType>::Adopt); |
325 | #else |
326 | RetainPtr<typename RetainPtr<T>::HelperPtrType> result = ptr; |
327 | [ptr release]; |
328 | return result; |
329 | #endif |
330 | } |
331 | #endif |
332 | |
333 | template<typename T> inline RetainPtr<typename RetainPtr<T>::HelperPtrType> retainPtr(T ptr) |
334 | { |
335 | return ptr; |
336 | } |
337 | |
338 | template <typename T> struct IsSmartPtr<RetainPtr<T>> { |
339 | static constexpr bool value = true; |
340 | }; |
341 | |
342 | template<typename P> struct HashTraits<RetainPtr<P>> : SimpleClassHashTraits<RetainPtr<P>> { |
343 | }; |
344 | |
345 | template<typename P> struct DefaultHash<RetainPtr<P>> { |
346 | typedef PtrHash<RetainPtr<P>> Hash; |
347 | }; |
348 | |
349 | template <typename P> |
350 | struct RetainPtrObjectHashTraits : SimpleClassHashTraits<RetainPtr<P>> { |
351 | static const RetainPtr<P>& emptyValue() |
352 | { |
353 | static RetainPtr<P>& null = *(new RetainPtr<P>); |
354 | return null; |
355 | } |
356 | }; |
357 | |
358 | template <typename P> |
359 | struct RetainPtrObjectHash { |
360 | static unsigned hash(const RetainPtr<P>& o) |
361 | { |
362 | ASSERT_WITH_MESSAGE(o.get(), "attempt to use null RetainPtr in HashTable" ); |
363 | return static_cast<unsigned>(CFHash(o.get())); |
364 | } |
365 | static bool equal(const RetainPtr<P>& a, const RetainPtr<P>& b) |
366 | { |
367 | return CFEqual(a.get(), b.get()); |
368 | } |
369 | static constexpr bool safeToCompareToEmptyOrDeleted = false; |
370 | }; |
371 | |
372 | inline bool safeCFEqual(CFTypeRef a, CFTypeRef b) |
373 | { |
374 | return (!a && !b) || (a && b && CFEqual(a, b)); |
375 | } |
376 | |
377 | inline CFHashCode safeCFHash(CFTypeRef a) |
378 | { |
379 | return a ? CFHash(a) : 0; |
380 | } |
381 | |
382 | #ifdef __OBJC__ |
383 | template<typename T> T* dynamic_objc_cast(id object) |
384 | { |
385 | if ([object isKindOfClass:[T class]]) |
386 | return (T *)object; |
387 | |
388 | return nil; |
389 | } |
390 | #endif |
391 | |
392 | } // namespace WTF |
393 | |
394 | using WTF::RetainPtr; |
395 | using WTF::adoptCF; |
396 | using WTF::retainPtr; |
397 | |
398 | #ifdef __OBJC__ |
399 | using WTF::adoptNS; |
400 | using WTF::dynamic_objc_cast; |
401 | #endif |
402 | |
403 | #endif // USE(CF) || defined(__OBJC__) |
404 | |