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 : 1189739 : constexpr AutoPointer(Ptr ptr = nullptr) // NOLINT(runtime/explicit)
70 : 1189739 : : 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 : 3116 : constexpr AutoPointer(Ptr ptr, const TakeOwnership&) : AutoPointer(ptr) {
76 : 3116 : m_ptr = copy();
77 : 3116 : }
78 : 15 : constexpr AutoPointer(ConstPtr ptr, const TakeOwnership& o)
79 : 15 : : AutoPointer(const_cast<Ptr>(ptr), o) {}
80 : 227 : constexpr AutoPointer(AutoPointer&& other) : AutoPointer() {
81 : 227 : this->swap(other);
82 : 227 : }
83 : 235 : constexpr AutoPointer(AutoPointer const& other) : AutoPointer() {
84 : 235 : *this = other;
85 : 235 : }
86 : :
87 : 311110 : constexpr AutoPointer& operator=(Ptr ptr) {
88 : 311110 : reset(ptr);
89 : 311110 : return *this;
90 : : }
91 : :
92 : 23886 : constexpr AutoPointer& operator=(AutoPointer&& other) {
93 : 23886 : this->swap(other);
94 : 23886 : 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 : 20075 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> operator->() {
105 : 20075 : 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 : 278970 : constexpr std::enable_if_t<std::is_array_v<U>, RvalueRef> operator[](
116 : : int index) {
117 : 278970 : 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 : 922687 : operator[](int index) const {
123 : 922687 : return m_ptr[index];
124 : : }
125 : :
126 : 1 : constexpr Tp operator*() const { return *m_ptr; }
127 : 707682 : constexpr operator Ptr() { return m_ptr; }
128 : 92149 : constexpr operator Ptr() const { return m_ptr; }
129 : : constexpr operator ConstPtr() const { return m_ptr; }
130 : 698419 : constexpr operator bool() const { return m_ptr != nullptr; }
131 : :
132 : 857149 : constexpr Ptr get() const { return m_ptr; }
133 : 417185 : constexpr Ptr* out() { return &m_ptr; }
134 : : constexpr ConstPtr* out() const { return const_cast<ConstPtr*>(&m_ptr); }
135 : :
136 : 252930 : constexpr Ptr release() {
137 : 252930 : auto* ptr = m_ptr;
138 : 252930 : m_ptr = nullptr;
139 : 252930 : return ptr;
140 : : }
141 : :
142 : 1845904 : constexpr void reset(Ptr ptr = nullptr) {
143 : 1845904 : Ptr old_ptr = m_ptr;
144 : 1845904 : m_ptr = ptr;
145 : :
146 : : if constexpr (has_free_function()) {
147 [ + + ]: 1802426 : if (old_ptr)
148 : : free_func(reinterpret_cast<F*>(old_ptr));
149 : : }
150 : 1845904 : }
151 : :
152 : 24377 : constexpr void swap(AutoPointer& other) {
153 : 24377 : std::swap(this->m_ptr, other.m_ptr);
154 : 24377 : }
155 : :
156 : 1510674 : /* constexpr */ ~AutoPointer() { // one day, with -std=c++2a
157 : 1510674 : reset();
158 : 1510674 : }
159 : :
160 : : template <typename U = T>
161 : : [[nodiscard]]
162 : 3140 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> copy() const {
163 : : static_assert(has_ref_function(), "No ref function provided");
164 [ + + ]: 3140 : return m_ptr ? reinterpret_cast<Ptr>(
165 : 3136 : ref_func(reinterpret_cast<F*>(m_ptr)))
166 : 4 : : nullptr;
167 : : }
168 : :
169 : : template <typename C>
170 : : [[nodiscard]]
171 : 3389 : constexpr C* as() const {
172 : 3389 : return const_cast<C*>(reinterpret_cast<const C*>(m_ptr));
173 : : }
174 : :
175 : : private:
176 : : Ptr m_ptr;
177 : : };
178 : :
179 : : template <typename T, typename F, AutoPointerFreeFunction<F> free_func,
180 : : AutoPointerRefFunction<F> ref_func>
181 : 1 : constexpr bool operator==(AutoPointer<T, F, free_func, ref_func> const& lhs,
182 : : AutoPointer<T, F, free_func, ref_func> const& rhs) {
183 : 1 : return lhs.get() == rhs.get();
184 : : }
185 : :
186 : : template <typename T>
187 : 312 : using AutoFree = AutoPointer<T>;
188 : :
189 : : struct AutoCharFuncs {
190 : 642 : static char* dup(char* str) { return g_strdup(str); }
191 : 161127 : static void free(char* str) { g_free(str); }
192 : : };
193 : : using AutoChar =
194 : 161127 : AutoPointer<char, char, AutoCharFuncs::free, AutoCharFuncs::dup>;
195 : :
196 : : // This moves a string owned by the JS runtime into the GLib domain. This is
197 : : // only possible because currently, js_free() and g_free() both ultimately call
198 : : // free(). It would cause crashes if SpiderMonkey were to stop supporting
199 : : // embedders using the system allocator in the future. In that case, this
200 : : // function would have to copy the string.
201 : : [[nodiscard]]
202 : 5612 : inline AutoChar js_chars_to_glib(JS::UniqueChars&& js_chars) {
203 : 5612 : return {js_chars.release()};
204 : : }
205 : :
206 : 76791 : using AutoStrv = AutoPointer<char*, char*, g_strfreev, g_strdupv>;
207 : :
208 : : template <typename T>
209 : 81416 : using AutoUnref = AutoPointer<T, void, g_object_unref, g_object_ref>;
210 : :
211 : : using AutoGVariant =
212 : : AutoPointer<GVariant, GVariant, g_variant_unref, g_variant_ref>;
213 : :
214 : : using AutoParam =
215 : 425 : AutoPointer<GParamSpec, GParamSpec, g_param_spec_unref, g_param_spec_ref>;
216 : :
217 : : using AutoGClosure =
218 : 289 : AutoPointer<GClosure, GClosure, g_closure_unref, g_closure_ref>;
219 : :
220 : : template <typename V, typename T>
221 : 284454 : constexpr void AutoPointerDeleter(T v) {
222 : : if constexpr (std::is_array_v<V>)
223 [ + - ]: 297791 : delete[] reinterpret_cast<std::remove_extent_t<V>*>(v);
224 : : else
225 [ + - ]: 40123 : delete v;
226 : 284454 : }
227 : :
228 : : template <typename T>
229 : 284454 : using AutoCppPointer = AutoPointer<T, T, AutoPointerDeleter<T>>;
230 : :
231 : : template <typename T = GTypeClass>
232 : 1062 : class AutoTypeClass : public AutoPointer<T, void, &g_type_class_unref> {
233 : 1062 : explicit AutoTypeClass(void* ptr = nullptr)
234 : 1062 : : AutoPointer<T, void, g_type_class_unref>(static_cast<T*>(ptr)) {}
235 : :
236 : : public:
237 : 1062 : explicit AutoTypeClass(GType gtype)
238 : 1062 : : AutoTypeClass(g_type_class_ref(gtype)) {}
239 : : };
240 : :
241 : : template <typename T>
242 : : struct SmartPointer : AutoPointer<T> {
243 : : using AutoPointer<T>::AutoPointer;
244 : : };
245 : :
246 : : template <>
247 : : struct SmartPointer<char*> : AutoStrv {
248 : : using AutoStrv::AutoPointer;
249 : : };
250 : :
251 : : template <>
252 : : struct SmartPointer<GStrv> : AutoStrv {
253 : : using AutoStrv::AutoPointer;
254 : : };
255 : :
256 : : template <>
257 : : struct SmartPointer<GObject> : AutoUnref<GObject> {
258 : : using AutoUnref<GObject>::AutoPointer;
259 : : };
260 : :
261 : : template <>
262 : : struct SmartPointer<GVariant> : AutoGVariant {
263 : : using AutoGVariant::AutoPointer;
264 : : };
265 : :
266 : : template <>
267 : 82 : struct SmartPointer<GList> : AutoPointer<GList, GList, g_list_free> {
268 : : using AutoPointer::AutoPointer;
269 : : };
270 : :
271 : : template <>
272 : 46 : struct SmartPointer<GSList> : AutoPointer<GSList, GSList, g_slist_free> {
273 : : using AutoPointer::AutoPointer;
274 : : };
275 : :
276 : : } // namespace Gjs
|