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: 2020 Marco Trevisan <marco.trevisan@canonical.com>
4 : :
5 : : #pragma once
6 : :
7 : : #include <config.h>
8 : :
9 : : #include <stdint.h>
10 : : #include <string.h> // for memset
11 : :
12 : : #include <cstddef> // for nullptr_t
13 : : #include <limits>
14 : : #include <string> // for to_string
15 : : #include <type_traits>
16 : :
17 : : #include <girepository.h>
18 : : #include <glib-object.h> // for GType
19 : : #include <glib.h> // for gboolean
20 : :
21 : : #include <js/RootingAPI.h> // for Handle
22 : : #include <js/TypeDecls.h> // for HandleValue
23 : :
24 : : #include "gi/arg-types-inl.h"
25 : : #include "gi/js-value-inl.h"
26 : : #include "gi/utils-inl.h"
27 : : #include "gjs/macros.h"
28 : :
29 : : // GIArgument accessor templates
30 : : //
31 : : // These are intended to make access to the GIArgument union more type-safe and
32 : : // reduce bugs that occur from assigning to one member and reading from another.
33 : : // (These bugs often work fine on one processor architecture but crash on
34 : : // another.)
35 : : //
36 : : // gjs_arg_member<T>(GIArgument*) - returns a reference to the appropriate union
37 : : // member that would hold the type T. Rarely used, unless as a pointer to a
38 : : // return location.
39 : : // gjs_arg_get<T>(GIArgument*) - returns the value of type T from the
40 : : // appropriate union member.
41 : : // gjs_arg_set(GIArgument*, T) - sets the appropriate union member for type T.
42 : : // gjs_arg_unset<T>(GIArgument*) - sets the appropriate zero value in the
43 : : // appropriate union member for type T.
44 : : // gjs_arg_steal<T>(GIArgument*) - sets the appropriate zero value in the
45 : : // appropriate union member for type T and returns the replaced value.
46 : :
47 : : template <auto GIArgument::*member>
48 : 349886 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
49 : 349886 : return (arg->*member);
50 : : }
51 : :
52 : : template <typename TAG>
53 : 349886 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
54 : : if constexpr (std::is_same_v<TAG, Gjs::Tag::GBoolean>)
55 : 2488 : return gjs_arg_member<&GIArgument::v_boolean>(arg);
56 : :
57 : : if constexpr (std::is_same_v<TAG, Gjs::Tag::GType>) {
58 : : // GType is defined differently on 32-bit vs. 64-bit architectures.
59 : : if constexpr (std::is_same_v<GType, gsize>)
60 : 2317 : return gjs_arg_member<&GIArgument::v_size>(arg);
61 : : else if constexpr (std::is_same_v<GType, gulong>)
62 : : return gjs_arg_member<&GIArgument::v_ulong>(arg);
63 : : }
64 : :
65 : : if constexpr (std::is_same_v<TAG, Gjs::Tag::Long>)
66 : 0 : return gjs_arg_member<&GIArgument::v_long>(arg);
67 : : if constexpr (std::is_same_v<TAG, Gjs::Tag::UnsignedLong>)
68 : 0 : return gjs_arg_member<&GIArgument::v_ulong>(arg);
69 : :
70 : : if constexpr (std::is_same_v<TAG, Gjs::Tag::Enum>)
71 : 3172 : return gjs_arg_member<&GIArgument::v_int>(arg);
72 : : if constexpr (std::is_same_v<TAG, Gjs::Tag::UnsignedEnum>)
73 : 2104 : return gjs_arg_member<&GIArgument::v_uint>(arg);
74 : :
75 : : if constexpr (std::is_same_v<TAG, bool>)
76 : 401 : return gjs_arg_member<&GIArgument::v_boolean>(arg);
77 : : if constexpr (std::is_same_v<TAG, int8_t>)
78 : 307 : return gjs_arg_member<&GIArgument::v_int8>(arg);
79 : : if constexpr (std::is_same_v<TAG, uint8_t>)
80 : 80 : return gjs_arg_member<&GIArgument::v_uint8>(arg);
81 : : if constexpr (std::is_same_v<TAG, int16_t>)
82 : 84 : return gjs_arg_member<&GIArgument::v_int16>(arg);
83 : : if constexpr (std::is_same_v<TAG, uint16_t>)
84 : 48 : return gjs_arg_member<&GIArgument::v_uint16>(arg);
85 : : if constexpr (std::is_same_v<TAG, int32_t>)
86 : 19971 : return gjs_arg_member<&GIArgument::v_int32>(arg);
87 : : if constexpr (std::is_same_v<TAG, uint32_t>)
88 : 40921 : return gjs_arg_member<&GIArgument::v_uint32>(arg);
89 : : if constexpr (std::is_same_v<TAG, int64_t>)
90 : 500 : return gjs_arg_member<&GIArgument::v_int64>(arg);
91 : : if constexpr (std::is_same_v<TAG, uint64_t>)
92 : 3563 : return gjs_arg_member<&GIArgument::v_uint64>(arg);
93 : :
94 : : // gunichar is stored in v_uint32
95 : : if constexpr (std::is_same_v<TAG, char32_t>)
96 : 8 : return gjs_arg_member<&GIArgument::v_uint32>(arg);
97 : :
98 : : if constexpr (std::is_same_v<TAG, float>)
99 : 89 : return gjs_arg_member<&GIArgument::v_float>(arg);
100 : :
101 : : if constexpr (std::is_same_v<TAG, double>)
102 : 148 : return gjs_arg_member<&GIArgument::v_double>(arg);
103 : :
104 : : if constexpr (std::is_same_v<TAG, char*>)
105 : 5947 : return gjs_arg_member<&GIArgument::v_string>(arg);
106 : :
107 : : if constexpr (std::is_same_v<TAG, void*>)
108 : 241162 : return gjs_arg_member<&GIArgument::v_pointer>(arg);
109 : :
110 : : if constexpr (std::is_same_v<TAG, std::nullptr_t>)
111 : : return gjs_arg_member<&GIArgument::v_pointer>(arg);
112 : :
113 : : if constexpr (std::is_pointer<TAG>()) {
114 : : using NonconstPtrT =
115 : : std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<TAG>>>;
116 : : return reinterpret_cast<NonconstPtrT&>(
117 : 26576 : gjs_arg_member<&GIArgument::v_pointer>(arg));
118 : : }
119 : : }
120 : :
121 : : template <typename TAG, typename = std::enable_if_t<
122 : : std::is_arithmetic_v<Gjs::Tag::RealT<TAG>>>>
123 : 16996 : constexpr inline void gjs_arg_set(GIArgument* arg, Gjs::Tag::RealT<TAG> v) {
124 : : if constexpr (std::is_same_v<TAG, bool> ||
125 : : std::is_same_v<TAG, Gjs::Tag::GBoolean>)
126 : 340 : v = !!v;
127 : :
128 : 16996 : gjs_arg_member<TAG>(arg) = v;
129 : 16996 : }
130 : :
131 : : // Specialization for types where TAG and RealT<TAG> are the same type, to allow
132 : : // inferring template parameter
133 : : template <typename T,
134 : : typename = std::enable_if_t<std::is_same_v<Gjs::Tag::RealT<T>, T> &&
135 : : std::is_arithmetic_v<T>>>
136 : 344 : constexpr inline void gjs_arg_set(GIArgument* arg, T v) {
137 : 344 : gjs_arg_set<T>(arg, v);
138 : 344 : }
139 : :
140 : : // Specialization for non-function pointers, so that you don't have to repeat
141 : : // the pointer type explicitly for type deduction, and that takes care of
142 : : // GIArgument not having constness
143 : : template <typename T, typename = std::enable_if_t<!std::is_function_v<T>>>
144 : 22446 : constexpr inline void gjs_arg_set(GIArgument* arg, T* v) {
145 : : using NonconstPtrT = std::add_pointer_t<std::remove_const_t<T>>;
146 : 22446 : gjs_arg_member<NonconstPtrT>(arg) = const_cast<NonconstPtrT>(v);
147 : 22446 : }
148 : :
149 : : // Overload for nullptr since it's not handled by TAG*
150 : 2 : constexpr inline void gjs_arg_set(GIArgument* arg, std::nullptr_t) {
151 : 2 : gjs_arg_member<void*>(arg) = nullptr;
152 : 2 : }
153 : :
154 : : // Store function pointers as void*. It is a requirement of GLib that your
155 : : // compiler can do this
156 : : template <typename ReturnT, typename... Args>
157 : 102 : constexpr inline void gjs_arg_set(GIArgument* arg, ReturnT (*v)(Args...)) {
158 : 102 : gjs_arg_member<void*>(arg) = reinterpret_cast<void*>(v);
159 : 102 : }
160 : :
161 : : // Specifying an integer-type tag and passing a void pointer, extracts a stuffed
162 : : // integer out of the pointer; otherwise just store the pointer in v_pointer
163 : : template <typename TAG = void*>
164 : 56453 : constexpr inline void gjs_arg_set(GIArgument* arg, void* v) {
165 : : using T = Gjs::Tag::RealT<TAG>;
166 : : if constexpr (std::is_integral_v<T>)
167 : : gjs_arg_set<TAG>(arg, gjs_pointer_to_int<T>(v));
168 : : else
169 : 56453 : gjs_arg_member<void*>(arg) = v;
170 : 56453 : }
171 : :
172 : : template <typename TAG>
173 : 196561 : [[nodiscard]] constexpr inline Gjs::Tag::RealT<TAG> gjs_arg_get(
174 : : GIArgument* arg) {
175 : : if constexpr (std::is_same_v<TAG, bool> ||
176 : : std::is_same_v<TAG, Gjs::Tag::GBoolean>)
177 : 1402 : return Gjs::Tag::RealT<TAG>(!!gjs_arg_member<TAG>(arg));
178 : :
179 : 195159 : return gjs_arg_member<TAG>(arg);
180 : : }
181 : :
182 : : template <typename TAG>
183 : : [[nodiscard]] constexpr inline void* gjs_arg_get_as_pointer(GIArgument* arg) {
184 : : return gjs_int_to_pointer(gjs_arg_get<TAG>(arg));
185 : : }
186 : :
187 : 31394 : constexpr inline void gjs_arg_unset(GIArgument* arg) {
188 : : // Clear all bits of the out C value. No one member is guaranteed to span
189 : : // the whole union on all architectures, so use memset() instead of
190 : : // gjs_arg_set<T>(arg, 0) for some type T.
191 : 31394 : memset(arg, 0, sizeof(GIArgument));
192 : 31394 : }
193 : :
194 : : template <typename TAG>
195 : 15627 : [[nodiscard]] constexpr inline Gjs::Tag::RealT<TAG> gjs_arg_steal(
196 : : GIArgument* arg) {
197 : 15627 : auto val = gjs_arg_get<TAG>(arg);
198 : 15627 : gjs_arg_unset(arg);
199 : 15627 : return val;
200 : : }
201 : :
202 : : // Implementation to store rounded (u)int64_t numbers into double
203 : :
204 : : template <typename BigTag>
205 : : [[nodiscard]] inline constexpr std::enable_if_t<
206 : : std::is_integral_v<Gjs::Tag::RealT<BigTag>> &&
207 : : (std::numeric_limits<Gjs::Tag::RealT<BigTag>>::max() >
208 : : std::numeric_limits<int32_t>::max()),
209 : : double>
210 : 39 : gjs_arg_get_maybe_rounded(GIArgument* arg) {
211 : : using BigT = Gjs::Tag::RealT<BigTag>;
212 : 39 : BigT val = gjs_arg_get<BigTag>(arg);
213 : :
214 [ + + + + : 76 : if (val < Gjs::min_safe_big_number<BigT>() ||
+ + ]
215 : 37 : val > Gjs::max_safe_big_number<BigT>()) {
216 : 7 : g_warning(
217 : : "Value %s cannot be safely stored in a JS Number "
218 : : "and may be rounded",
219 : : std::to_string(val).c_str());
220 : : }
221 : :
222 : 39 : return static_cast<double>(val);
223 : : }
224 : :
225 : : template <typename TAG>
226 : 29807 : GJS_JSAPI_RETURN_CONVENTION inline bool gjs_arg_set_from_js_value(
227 : : JSContext* cx, JS::HandleValue value, GIArgument* arg, bool* out_of_range) {
228 : : if constexpr (Gjs::type_has_js_getter<TAG>())
229 : 14371 : return Gjs::js_value_to_c<TAG>(cx, value, &gjs_arg_member<TAG>(arg));
230 : :
231 : 15436 : Gjs::Tag::JSValuePackT<TAG> val{};
232 : :
233 : : using T = Gjs::Tag::RealT<TAG>;
234 : : using HolderTag = Gjs::Tag::JSValuePackTag<TAG>;
235 [ + + ]: 15436 : if (!Gjs::js_value_to_c_checked<T, HolderTag>(cx, value, &val,
236 : : out_of_range))
237 : 17 : return false;
238 : :
239 [ + + ]: 15419 : if (*out_of_range)
240 : 8 : return false;
241 : :
242 : 15411 : gjs_arg_set<TAG>(arg, val);
243 : :
244 : 15411 : return true;
245 : : }
246 : :
247 : : // A helper function to retrieve array lengths from a GIArgument (letting the
248 : : // compiler generate good instructions in case of big endian machines)
249 : 468 : [[nodiscard]] constexpr size_t gjs_gi_argument_get_array_length(
250 : : GITypeTag tag, GIArgument* arg) {
251 [ - + - - : 468 : switch (tag) {
+ + + +
- ]
252 : 0 : case GI_TYPE_TAG_INT8:
253 : 0 : return gjs_arg_get<int8_t>(arg);
254 : 1 : case GI_TYPE_TAG_UINT8:
255 : 1 : return gjs_arg_get<uint8_t>(arg);
256 : 0 : case GI_TYPE_TAG_INT16:
257 : 0 : return gjs_arg_get<int16_t>(arg);
258 : 0 : case GI_TYPE_TAG_UINT16:
259 : 0 : return gjs_arg_get<uint16_t>(arg);
260 : 123 : case GI_TYPE_TAG_INT32:
261 : 123 : return gjs_arg_get<int32_t>(arg);
262 : 7 : case GI_TYPE_TAG_UINT32:
263 : 7 : return gjs_arg_get<uint32_t>(arg);
264 : 17 : case GI_TYPE_TAG_INT64:
265 : 17 : return gjs_arg_get<int64_t>(arg);
266 : 320 : case GI_TYPE_TAG_UINT64:
267 : 320 : return gjs_arg_get<uint64_t>(arg);
268 : 0 : default:
269 : : g_assert_not_reached();
270 : : }
271 : : }
272 : :
273 : : namespace Gjs {
274 : :
275 : 57647 : [[nodiscard]] static inline bool is_basic_type(GITypeTag tag, bool is_pointer) {
276 [ + + + - ]: 57647 : if (tag == GI_TYPE_TAG_VOID && is_pointer)
277 : 276 : return false; // void* is not a basic type
278 [ + + + + ]: 57371 : return GI_TYPE_TAG_IS_BASIC(tag);
279 : : }
280 : :
281 : 105 : [[nodiscard]] static inline bool basic_type_needs_release(GITypeTag tag) {
282 : 105 : g_assert(GI_TYPE_TAG_IS_BASIC(tag));
283 [ + + + + ]: 105 : return tag == GI_TYPE_TAG_FILENAME || tag == GI_TYPE_TAG_UTF8;
284 : : }
285 : :
286 : : } // namespace Gjs
|