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> // IWYU pragma: keep
15 : : // (https://github.com/include-what-you-use/include-what-you-use/issues/636)
16 : :
17 : : #include <glib-object.h>
18 : : #include <glib.h>
19 : :
20 : : // For some reason, this needs to be here because otherwise
21 : : // AutoPointer<char*>::swap() fails to link.
22 : : #include <js/GCPolicyAPI.h> // IWYU pragma: keep
23 : : #include <js/Utility.h> // for UniqueChars
24 : :
25 : : // Auto pointers. We don't use GLib's g_autofree and friends because they only
26 : : // work on GCC and Clang, and we try to support MSVC where possible. But this is
27 : : // C++, so we use C++ classes.
28 : :
29 : : namespace Gjs {
30 : :
31 : : // A sentinel object used to pick the AutoPointer constructor that adds a
32 : : // reference: AutoFoo foo{pointer, TakeOwnership{}};
33 : : struct TakeOwnership {};
34 : :
35 : : template <typename F = void>
36 : : using AutoPointerRefFunction = F* (*)(F*);
37 : :
38 : : template <typename F = void>
39 : : using AutoPointerFreeFunction = void (*)(F*);
40 : :
41 : : template <typename T, typename F = void,
42 : : AutoPointerFreeFunction<F> free_func = free,
43 : : AutoPointerRefFunction<F> ref_func = nullptr>
44 : : struct AutoPointer {
45 : : using Tp =
46 : : std::conditional_t<std::is_array_v<T>, std::remove_extent_t<T>, T>;
47 : : using Ptr = std::add_pointer_t<Tp>;
48 : : using ConstPtr = std::add_pointer_t<std::add_const_t<Tp>>;
49 : : using RvalueRef = std::add_lvalue_reference_t<Tp>;
50 : :
51 : : protected:
52 : : using BaseType = AutoPointer<T, F, free_func, ref_func>;
53 : :
54 : : private:
55 : : template <typename FunctionType, FunctionType function>
56 : : static constexpr bool has_function() {
57 : : using NullType = std::integral_constant<FunctionType, nullptr>;
58 : : using ActualType = std::integral_constant<FunctionType, function>;
59 : :
60 : : return !std::is_same_v<ActualType, NullType>;
61 : : }
62 : :
63 : : public:
64 : : static constexpr bool has_free_function() {
65 : : return has_function<AutoPointerFreeFunction<F>, free_func>();
66 : : }
67 : :
68 : : static constexpr bool has_ref_function() {
69 : : return has_function<AutoPointerRefFunction<F>, ref_func>();
70 : : }
71 : :
72 : 1380676 : constexpr AutoPointer(Ptr ptr = nullptr) // NOLINT(runtime/explicit)
73 : 1380676 : : m_ptr(ptr) {}
74 : : template <typename U, typename = std::enable_if_t<std::is_same_v<U, Tp> &&
75 : : std::is_array_v<T>>>
76 : : explicit constexpr AutoPointer(U ptr[]) : m_ptr(ptr) {}
77 : :
78 : 19910 : constexpr AutoPointer(Ptr ptr, const TakeOwnership&) : AutoPointer(ptr) {
79 : 19910 : m_ptr = copy();
80 : 19910 : }
81 : 15 : constexpr AutoPointer(ConstPtr ptr, const TakeOwnership& o)
82 : 15 : : AutoPointer(const_cast<Ptr>(ptr), o) {}
83 : 272 : constexpr AutoPointer(AutoPointer&& other) : AutoPointer() {
84 : 272 : this->swap(other);
85 : 272 : }
86 : 1492 : constexpr AutoPointer(AutoPointer const& other) : AutoPointer() {
87 : 1492 : *this = other;
88 : 1492 : }
89 : :
90 : 293474 : constexpr AutoPointer& operator=(Ptr ptr) {
91 : 293474 : reset(ptr);
92 : 293474 : return *this;
93 : : }
94 : :
95 : 32437 : constexpr AutoPointer& operator=(AutoPointer&& other) {
96 : 32437 : this->swap(other);
97 : 32437 : return *this;
98 : : }
99 : :
100 : 1518 : constexpr AutoPointer& operator=(AutoPointer const& other) {
101 : 1518 : AutoPointer dup{other.get(), TakeOwnership{}};
102 : 1518 : this->swap(dup);
103 : 3036 : return *this;
104 : 1518 : }
105 : :
106 : : template <typename U = T>
107 : 17217 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> operator->() {
108 : 17217 : return m_ptr;
109 : : }
110 : :
111 : : template <typename U = T>
112 : 20 : constexpr std::enable_if_t<!std::is_array_v<U>, ConstPtr> operator->()
113 : : const {
114 : 20 : return m_ptr;
115 : : }
116 : :
117 : : template <typename U = T>
118 : 266561 : constexpr std::enable_if_t<std::is_array_v<U>, RvalueRef> operator[](
119 : : int index) {
120 : 266561 : return m_ptr[index];
121 : : }
122 : :
123 : : template <typename U = T>
124 : : constexpr std::enable_if_t<std::is_array_v<U>, std::add_const_t<RvalueRef>>
125 : 874844 : operator[](int index) const {
126 : 874844 : return m_ptr[index];
127 : : }
128 : :
129 : 1 : constexpr Tp operator*() const { return *m_ptr; }
130 : 1204565 : constexpr operator Ptr() { return m_ptr; }
131 : 451915 : constexpr operator Ptr() const { return m_ptr; }
132 : : constexpr operator ConstPtr() const { return m_ptr; }
133 : 430302 : constexpr operator bool() const { return m_ptr != nullptr; }
134 : :
135 : 829750 : constexpr Ptr get() const { return m_ptr; }
136 : 413994 : constexpr Ptr* out() { return &m_ptr; }
137 : : constexpr ConstPtr* out() const { return const_cast<ConstPtr*>(&m_ptr); }
138 : :
139 : 243529 : constexpr Ptr release() {
140 : 243529 : auto* ptr = m_ptr;
141 : 243529 : m_ptr = nullptr;
142 : 243529 : return ptr;
143 : : }
144 : :
145 : 2028661 : constexpr void reset(Ptr ptr = nullptr) {
146 : 2028661 : Ptr old_ptr = m_ptr;
147 : 2028661 : m_ptr = ptr;
148 : :
149 : : if constexpr (has_free_function()) {
150 [ + + ]: 1987959 : if (old_ptr)
151 : : free_func(reinterpret_cast<F*>(old_ptr));
152 : : }
153 : 2028661 : }
154 : :
155 : 34230 : constexpr void swap(AutoPointer& other) {
156 : 34230 : std::swap(this->m_ptr, other.m_ptr);
157 : 34230 : }
158 : :
159 : 1701888 : /* constexpr */ ~AutoPointer() { // one day, with -std=c++2a
160 : 1701888 : reset();
161 : 1701888 : }
162 : :
163 : : template <typename U = T>
164 : : [[nodiscard]]
165 : 19932 : constexpr std::enable_if_t<!std::is_array_v<U>, Ptr> copy() const {
166 : : static_assert(has_ref_function(), "No ref function provided");
167 [ + + ]: 19932 : return m_ptr ? reinterpret_cast<Ptr>(
168 : 19731 : ref_func(reinterpret_cast<F*>(m_ptr)))
169 : 19932 : : nullptr;
170 : : }
171 : :
172 : : template <typename C>
173 : : [[nodiscard]]
174 : 96790 : constexpr C* as() const {
175 : 96790 : 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 : 70 : using AutoFree = AutoPointer<T>;
191 : :
192 : : struct AutoCharFuncs {
193 : 570 : static char* dup(char* str) { return g_strdup(str); }
194 : 127236 : static void free(char* str) { g_free(str); }
195 : : };
196 : : using AutoChar =
197 : 127236 : 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 : 5213 : [[nodiscard]] inline AutoChar js_chars_to_glib(JS::UniqueChars&& js_chars) {
205 : 5213 : return {js_chars.release()};
206 : : }
207 : :
208 : 79423 : using AutoStrv = AutoPointer<char*, char*, g_strfreev, g_strdupv>;
209 : :
210 : : template <typename T>
211 : 45534 : using AutoUnref = AutoPointer<T, void, g_object_unref, g_object_ref>;
212 : :
213 : : using AutoGVariant =
214 : : AutoPointer<GVariant, GVariant, g_variant_unref, g_variant_ref>;
215 : :
216 : : using AutoParam =
217 : 425 : AutoPointer<GParamSpec, GParamSpec, g_param_spec_unref, g_param_spec_ref>;
218 : :
219 : : using AutoGClosure =
220 : 287 : AutoPointer<GClosure, GClosure, g_closure_unref, g_closure_ref>;
221 : :
222 : : template <typename V, typename T>
223 : 267445 : constexpr void AutoPointerDeleter(T v) {
224 : : if constexpr (std::is_array_v<V>)
225 [ + - ]: 278932 : delete[] reinterpret_cast<std::remove_extent_t<V>*>(v);
226 : : else
227 [ + - ]: 35407 : delete v;
228 : 267445 : }
229 : :
230 : : template <typename T>
231 : 267445 : using AutoCppPointer = AutoPointer<T, T, AutoPointerDeleter<T>>;
232 : :
233 : : template <typename T = GTypeClass>
234 : 1755 : struct AutoTypeClass : AutoPointer<T, void, &g_type_class_unref> {
235 : 1755 : AutoTypeClass(gpointer ptr = nullptr) // NOLINT(runtime/explicit)
236 : 1755 : : AutoPointer<T, void, g_type_class_unref>(static_cast<T*>(ptr)) {}
237 : 1755 : explicit AutoTypeClass(GType gtype)
238 : 1755 : : 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 : 79 : struct SmartPointer<GList> : AutoPointer<GList, GList, g_list_free> {
268 : : using AutoPointer::AutoPointer;
269 : : };
270 : :
271 : : template <>
272 : 39 : struct SmartPointer<GSList> : AutoPointer<GSList, GSList, g_slist_free> {
273 : : using AutoPointer::AutoPointer;
274 : : };
275 : :
276 : : } // namespace Gjs
|