Branch data Line data Source code
1 : : /* -*- mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; -*- */
2 : : // SPDX-License-Identifier: MIT OR LGPL-2.0-or-later
3 : : // SPDX-FileCopyrightText: 2017 Chun-wei Fan <fanchunwei@src.gnome.org>
4 : : // SPDX-FileCopyrightText: 2018-2020 Canonical, Ltd
5 : : // SPDX-FileCopyrightText: 2018, 2024 Philip Chimento <philip.chimento@gmail.com>
6 : :
7 : : #pragma once
8 : :
9 : : #include <config.h>
10 : :
11 : : #include <stdlib.h>
12 : :
13 : : #include <type_traits>
14 : : #include <utility>
15 : :
16 : : #include <glib-object.h>
17 : : #include <glib.h>
18 : : // IWYU pragma: no_forward_declare _GVariant
19 : :
20 : : #include <js/Utility.h> // for UniqueChars
21 : :
22 : : // Auto pointers. We don't use GLib's g_autofree and friends because they only
23 : : // work on GCC and Clang, and we try to support MSVC where possible. But this is
24 : : // C++, so we use C++ classes.
25 : :
26 : : namespace Gjs {
27 : :
28 : : // A sentinel object used to pick the AutoPointer constructor that adds a
29 : : // reference: AutoFoo foo{pointer, TakeOwnership{}};
30 : : struct TakeOwnership {};
31 : :
32 : : template <typename F = void>
33 : : using AutoPointerRefFunction = F* (*)(F*);
34 : :
35 : : template <typename F = void>
36 : : using AutoPointerFreeFunction = void (*)(F*);
37 : :
38 : : template <typename T, typename F = void,
39 : : AutoPointerFreeFunction<F> free_func = free,
40 : : AutoPointerRefFunction<F> ref_func = nullptr>
41 : : struct AutoPointer {
42 : : using Tp =
43 : : std::conditional_t<std::is_array_v<T>, std::remove_extent_t<T>, T>;
44 : : using Ptr = std::add_pointer_t<Tp>;
45 : : using ConstPtr = std::add_pointer_t<std::add_const_t<Tp>>;
46 : : using RvalueRef = std::add_lvalue_reference_t<Tp>;
47 : :
48 : : protected:
49 : : using BaseType = AutoPointer<T, F, free_func, ref_func>;
50 : :
51 : : private:
52 : : template <typename FunctionType, FunctionType function>
53 : : static constexpr bool has_function() {
54 : : using NullType = std::integral_constant<FunctionType, nullptr>;
55 : : using ActualType = std::integral_constant<FunctionType, function>;
56 : :
57 : : return !std::is_same_v<ActualType, NullType>;
58 : : }
59 : :
60 : : public:
61 : : static constexpr bool has_free_function() {
62 : : return has_function<AutoPointerFreeFunction<F>, free_func>();
63 : : }
64 : :
65 : : static constexpr bool has_ref_function() {
66 : : return has_function<AutoPointerRefFunction<F>, ref_func>();
67 : : }
68 : :
69 : 1227437 : constexpr AutoPointer(Ptr ptr = nullptr) // NOLINT(runtime/explicit)
70 : 1227437 : : m_ptr(ptr) {}
71 : : template <typename U, typename = std::enable_if_t<std::is_same_v<U, Tp> &&
72 : : std::is_array_v<T>>>
73 : : explicit constexpr AutoPointer(U ptr[]) : m_ptr(ptr) {}
74 : :
75 : 3206 : constexpr AutoPointer(Ptr ptr, const TakeOwnership&) : AutoPointer(ptr) {
76 : 3206 : m_ptr = copy();
77 : 3206 : }
78 : 15 : constexpr AutoPointer(ConstPtr ptr, const TakeOwnership& o)
79 : 15 : : AutoPointer(const_cast<Ptr>(ptr), o) {}
80 : 229 : constexpr AutoPointer(AutoPointer&& other) : AutoPointer() {
81 : 229 : this->swap(other);
82 : 229 : }
83 : 235 : constexpr AutoPointer(AutoPointer const& other) : AutoPointer() {
84 : 235 : *this = other;
85 : 235 : }
86 : :
87 : 321262 : constexpr AutoPointer& operator=(Ptr ptr) {
88 : 321262 : reset(ptr);
89 : 321262 : return *this;
90 : : }
91 : :
92 : 24206 : constexpr AutoPointer& operator=(AutoPointer&& other) {
93 : 24206 : this->swap(other);
94 : 24206 : return *this;
95 : : }
96 : :
97 : 261 : constexpr AutoPointer& operator=(AutoPointer const& other) {
98 : 261 : AutoPointer dup{other.get(), TakeOwnership{}};
99 : 261 : this->swap(dup);
100 : 522 : return *this;
101 : 261 : }
102 : :
103 : : template <typename U = T>
104 : 20211 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> operator->() {
105 : 20211 : return m_ptr;
106 : : }
107 : :
108 : : template <typename U = T>
109 : 25 : constexpr std::enable_if_t<!std::is_array_v<U>, ConstPtr> operator->()
110 : : const {
111 : 25 : return m_ptr;
112 : : }
113 : :
114 : : template <typename U = T>
115 : 287240 : constexpr std::enable_if_t<std::is_array_v<U>, RvalueRef> operator[](
116 : : int index) {
117 : 287240 : return m_ptr[index];
118 : : }
119 : :
120 : : template <typename U = T>
121 : : constexpr std::enable_if_t<std::is_array_v<U>, std::add_const_t<RvalueRef>>
122 : 951383 : operator[](int index) const {
123 : 951383 : return m_ptr[index];
124 : : }
125 : :
126 : 1 : constexpr Tp operator*() const { return *m_ptr; }
127 : 725233 : constexpr operator Ptr() { return m_ptr; }
128 : 101148 : constexpr operator Ptr() const { return m_ptr; }
129 : : constexpr operator ConstPtr() const { return m_ptr; }
130 : 734533 : constexpr operator bool() const { return m_ptr != nullptr; }
131 : :
132 : 885283 : [[nodiscard]] constexpr Ptr get() const { return m_ptr; }
133 : 431877 : [[nodiscard]] constexpr Ptr* out() { return &m_ptr; }
134 : : [[nodiscard]]
135 : : constexpr ConstPtr* out() const {
136 : : return const_cast<ConstPtr*>(&m_ptr);
137 : : }
138 : :
139 : 265108 : constexpr Ptr release() {
140 : 265108 : auto* ptr = m_ptr;
141 : 265108 : m_ptr = nullptr;
142 : 265108 : return ptr;
143 : : }
144 : :
145 : 1906112 : constexpr void reset(Ptr ptr = nullptr) {
146 : 1906112 : Ptr old_ptr = m_ptr;
147 : 1906112 : m_ptr = ptr;
148 : :
149 : : if constexpr (has_free_function()) {
150 [ + + ]: 1862159 : if (old_ptr)
151 : : free_func(reinterpret_cast<F*>(old_ptr));
152 : : }
153 : 1906112 : }
154 : :
155 : 24699 : constexpr void swap(AutoPointer& other) {
156 : 24699 : std::swap(this->m_ptr, other.m_ptr);
157 : 24699 : }
158 : :
159 : 1560590 : /* constexpr */ ~AutoPointer() { // one day, with -std=c++2a
160 : 1560590 : reset();
161 : 1560590 : }
162 : :
163 : : template <typename U = T>
164 : : [[nodiscard]]
165 : 3230 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> copy() const {
166 : : static_assert(has_ref_function(), "No ref function provided");
167 [ + + ]: 3230 : return m_ptr ? reinterpret_cast<Ptr>(
168 : 3226 : ref_func(reinterpret_cast<F*>(m_ptr)))
169 : 4 : : nullptr;
170 : : }
171 : :
172 : : template <typename C>
173 : : [[nodiscard]]
174 : 4174 : constexpr C* as() const {
175 : 4174 : return const_cast<C*>(reinterpret_cast<const C*>(m_ptr));
176 : : }
177 : :
178 : : private:
179 : : Ptr m_ptr;
180 : : };
181 : :
182 : : template <typename T, typename F, AutoPointerFreeFunction<F> free_func,
183 : : AutoPointerRefFunction<F> ref_func>
184 : 1 : constexpr bool operator==(AutoPointer<T, F, free_func, ref_func> const& lhs,
185 : : AutoPointer<T, F, free_func, ref_func> const& rhs) {
186 : 1 : return lhs.get() == rhs.get();
187 : : }
188 : :
189 : : template <typename T>
190 : 313 : using AutoFree = AutoPointer<T>;
191 : :
192 : : struct AutoCharFuncs {
193 : 389 : static char* dup(char* str) { return g_strdup(str); }
194 [ - + ]: 162679 : static void free(char* str) { g_free(str); }
195 : : };
196 : : using AutoChar =
197 : 162679 : AutoPointer<char, char, AutoCharFuncs::free, AutoCharFuncs::dup>;
198 : :
199 : : // This moves a string owned by the JS runtime into the GLib domain. This is
200 : : // only possible because currently, js_free() and g_free() both ultimately call
201 : : // free(). It would cause crashes if SpiderMonkey were to stop supporting
202 : : // embedders using the system allocator in the future. In that case, this
203 : : // function would have to copy the string.
204 : : [[nodiscard]]
205 : 5788 : inline AutoChar js_chars_to_glib(JS::UniqueChars&& js_chars) {
206 : 5788 : return {js_chars.release()};
207 : : }
208 : :
209 : 77593 : using AutoStrv = AutoPointer<char*, char*, g_strfreev, g_strdupv>;
210 : :
211 : : template <typename T>
212 : 80339 : using AutoUnref = AutoPointer<T, void, g_object_unref, g_object_ref>;
213 : :
214 : : using AutoGVariant =
215 : : AutoPointer<GVariant, GVariant, g_variant_unref, g_variant_ref>;
216 : :
217 : : using AutoParam =
218 : 425 : AutoPointer<GParamSpec, GParamSpec, g_param_spec_unref, g_param_spec_ref>;
219 : :
220 : : using AutoGClosure =
221 : 293 : AutoPointer<GClosure, GClosure, g_closure_unref, g_closure_ref>;
222 : :
223 : : template <typename V, typename T>
224 : 294023 : constexpr void AutoPointerDeleter(T v) {
225 : : if constexpr (std::is_array_v<V>)
226 [ + - ]: 308003 : delete[] reinterpret_cast<std::remove_extent_t<V>*>(v);
227 : : else
228 [ + - ]: 42193 : delete v;
229 : 294023 : }
230 : :
231 : : template <typename T>
232 : 294023 : using AutoCppPointer = AutoPointer<T, T, AutoPointerDeleter<T>>;
233 : :
234 : : template <typename T = GTypeClass>
235 : 1095 : class AutoTypeClass : public AutoPointer<T, void, &g_type_class_unref> {
236 : 1095 : explicit AutoTypeClass(void* ptr = nullptr)
237 : 1095 : : AutoPointer<T, void, g_type_class_unref>(static_cast<T*>(ptr)) {}
238 : :
239 : : public:
240 : 1095 : explicit AutoTypeClass(GType gtype)
241 : 1095 : : AutoTypeClass(g_type_class_ref(gtype)) {}
242 : : };
243 : :
244 : : template <typename T>
245 : : struct SmartPointer : AutoPointer<T> {
246 : : using AutoPointer<T>::AutoPointer;
247 : : };
248 : :
249 : : template <>
250 : : struct SmartPointer<char*> : AutoStrv {
251 : : using AutoStrv::AutoPointer;
252 : : };
253 : :
254 : : template <>
255 : : struct SmartPointer<GStrv> : AutoStrv {
256 : : using AutoStrv::AutoPointer;
257 : : };
258 : :
259 : : template <>
260 : : struct SmartPointer<GObject> : AutoUnref<GObject> {
261 : : using AutoUnref<GObject>::AutoPointer;
262 : : };
263 : :
264 : : template <>
265 : : struct SmartPointer<GVariant> : AutoGVariant {
266 : : using AutoGVariant::AutoPointer;
267 : : };
268 : :
269 : : template <>
270 : 82 : struct SmartPointer<GList> : AutoPointer<GList, GList, g_list_free> {
271 : : using AutoPointer::AutoPointer;
272 : : };
273 : :
274 : : template <>
275 : 46 : struct SmartPointer<GSList> : AutoPointer<GSList, GSList, g_slist_free> {
276 : : using AutoPointer::AutoPointer;
277 : : };
278 : :
279 : : } // namespace Gjs
|