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 : :
11 : : #include <cstddef> // for nullptr_t
12 : : #include <limits>
13 : : #include <string> // for to_string
14 : : #include <type_traits>
15 : :
16 : : #include <girepository.h>
17 : : #include <glib-object.h> // for GType
18 : : #include <glib.h> // for gboolean
19 : : #include <js/TypeDecls.h> // for HandleValue
20 : :
21 : : #include "gi/js-value-inl.h"
22 : : #include "gi/utils-inl.h"
23 : : #include "gjs/macros.h"
24 : :
25 : : // GIArgument accessor templates
26 : : //
27 : : // These are intended to make access to the GIArgument union more type-safe and
28 : : // reduce bugs that occur from assigning to one member and reading from another.
29 : : // (These bugs often work fine on one processor architecture but crash on
30 : : // another.)
31 : : //
32 : : // gjs_arg_member<T>(GIArgument*) - returns a reference to the appropriate union
33 : : // member that would hold the type T. Rarely used, unless as a pointer to a
34 : : // return location.
35 : : // gjs_arg_get<T>(GIArgument*) - returns the value of type T from the
36 : : // appropriate union member.
37 : : // gjs_arg_set(GIArgument*, T) - sets the appropriate union member for type T.
38 : : // gjs_arg_unset<T>(GIArgument*) - sets the appropriate zero value in the
39 : : // appropriate union member for type T.
40 : : // gjs_arg_steal<T>(GIArgument*) - sets the appropriate zero value in the
41 : : // appropriate union member for type T and returns the replaced value.
42 : :
43 : : template <auto GIArgument::*member>
44 : 271108 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
45 : 271108 : return (arg->*member);
46 : : }
47 : :
48 : : /* The tag is needed to disambiguate types such as gboolean and GType
49 : : * which are in fact typedef's of other generic types.
50 : : * Setting a tag for a type allows to perform proper specialization. */
51 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
52 : 271108 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
53 : : if constexpr (TAG == GI_TYPE_TAG_VOID) {
54 : : if constexpr (std::is_same_v<T, bool>)
55 : 441 : return gjs_arg_member<&GIArgument::v_boolean>(arg);
56 : : if constexpr (std::is_same_v<T, int8_t>)
57 : 42 : return gjs_arg_member<&GIArgument::v_int8>(arg);
58 : : if constexpr (std::is_same_v<T, uint8_t>)
59 : 47 : return gjs_arg_member<&GIArgument::v_uint8>(arg);
60 : : if constexpr (std::is_same_v<T, int16_t>)
61 : 36 : return gjs_arg_member<&GIArgument::v_int16>(arg);
62 : : if constexpr (std::is_same_v<T, uint16_t>)
63 : 16 : return gjs_arg_member<&GIArgument::v_uint16>(arg);
64 : : if constexpr (std::is_same_v<T, int32_t>)
65 : 15642 : return gjs_arg_member<&GIArgument::v_int32>(arg);
66 : : if constexpr (std::is_same_v<T, uint32_t>)
67 : 26531 : return gjs_arg_member<&GIArgument::v_uint32>(arg);
68 : : if constexpr (std::is_same_v<T, int64_t>)
69 : 304 : return gjs_arg_member<&GIArgument::v_int64>(arg);
70 : : if constexpr (std::is_same_v<T, uint64_t>)
71 : 3518 : return gjs_arg_member<&GIArgument::v_uint64>(arg);
72 : :
73 : : // gunichar is stored in v_uint32
74 : : if constexpr (std::is_same_v<T, char32_t>)
75 : 2 : return gjs_arg_member<&GIArgument::v_uint32>(arg);
76 : :
77 : : if constexpr (std::is_same_v<T, float>)
78 : 48 : return gjs_arg_member<&GIArgument::v_float>(arg);
79 : :
80 : : if constexpr (std::is_same_v<T, double>)
81 : 38 : return gjs_arg_member<&GIArgument::v_double>(arg);
82 : :
83 : : if constexpr (std::is_same_v<T, char*>)
84 : 5302 : return gjs_arg_member<&GIArgument::v_string>(arg);
85 : :
86 : : if constexpr (std::is_same_v<T, void*>)
87 : 190656 : return gjs_arg_member<&GIArgument::v_pointer>(arg);
88 : :
89 : : if constexpr (std::is_same_v<T, std::nullptr_t>)
90 : 0 : return gjs_arg_member<&GIArgument::v_pointer>(arg);
91 : :
92 : : if constexpr (std::is_pointer<T>()) {
93 : : using NonconstPtrT = std::add_pointer_t<
94 : : std::remove_const_t<std::remove_pointer_t<T>>>;
95 : : return reinterpret_cast<NonconstPtrT&>(
96 : 20318 : gjs_arg_member<&GIArgument::v_pointer>(arg));
97 : : }
98 : : }
99 : :
100 : : if constexpr (TAG == GI_TYPE_TAG_BOOLEAN && std::is_same_v<T, gboolean>)
101 : 1205 : return gjs_arg_member<&GIArgument::v_boolean>(arg);
102 : :
103 : : if constexpr (TAG == GI_TYPE_TAG_GTYPE && std::is_same_v<T, GType>) {
104 : : // GType is defined differently on 32-bit vs. 64-bit architectures.
105 : : if constexpr (std::is_same_v<GType, gsize>)
106 : 1482 : return gjs_arg_member<&GIArgument::v_size>(arg);
107 : : else if constexpr (std::is_same_v<GType, gulong>)
108 : : return gjs_arg_member<&GIArgument::v_ulong>(arg);
109 : : }
110 : :
111 : : if constexpr (TAG == GI_TYPE_TAG_INTERFACE && std::is_integral_v<T>) {
112 : : if constexpr (std::is_signed_v<T>)
113 : 5478 : return gjs_arg_member<&GIArgument::v_int>(arg);
114 : : else
115 : 2 : return gjs_arg_member<&GIArgument::v_uint>(arg);
116 : : }
117 : : }
118 : :
119 : : typedef enum {
120 : : GJS_TYPE_TAG_LONG = 0,
121 : : } ExtraTag;
122 : :
123 : : template <typename T, ExtraTag TAG>
124 : 0 : [[nodiscard]] constexpr inline decltype(auto) gjs_arg_member(GIArgument* arg) {
125 : : if constexpr (TAG == GJS_TYPE_TAG_LONG &&
126 : : std::is_same_v<T, long>) // NOLINT(runtime/int)
127 : 0 : return gjs_arg_member<&GIArgument::v_long>(arg);
128 : : else if constexpr (TAG == GJS_TYPE_TAG_LONG &&
129 : : std::is_same_v<T, unsigned long>) // NOLINT(runtime/int)
130 : 0 : return gjs_arg_member<&GIArgument::v_ulong>(arg);
131 : : }
132 : :
133 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
134 : 87930 : constexpr inline void gjs_arg_set(GIArgument* arg, T v) {
135 : : if constexpr (std::is_pointer_v<T>) {
136 : : using NonconstPtrT =
137 : : std::add_pointer_t<std::remove_const_t<std::remove_pointer_t<T>>>;
138 : 76010 : gjs_arg_member<NonconstPtrT, TAG>(arg) = const_cast<NonconstPtrT>(v);
139 : : } else {
140 : : if constexpr (std::is_same_v<T, bool> || (std::is_same_v<T, gboolean> &&
141 : : TAG == GI_TYPE_TAG_BOOLEAN))
142 : 399 : v = !!v;
143 : :
144 : 11920 : gjs_arg_member<T, TAG>(arg) = v;
145 : : }
146 : 87930 : }
147 : :
148 : : template <typename T, ExtraTag TAG>
149 : 0 : constexpr inline void gjs_arg_set(GIArgument* arg, T v) {
150 : 0 : gjs_arg_member<T, TAG>(arg) = v;
151 : 0 : }
152 : :
153 : : // Store function pointers as void*. It is a requirement of GLib that your
154 : : // compiler can do this
155 : : template <typename ReturnT, typename... Args>
156 : 88 : constexpr inline void gjs_arg_set(GIArgument* arg, ReturnT (*v)(Args...)) {
157 : 88 : gjs_arg_member<void*>(arg) = reinterpret_cast<void*>(v);
158 : 88 : }
159 : :
160 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
161 : : constexpr inline std::enable_if_t<std::is_integral_v<T>> gjs_arg_set(
162 : : GIArgument* arg, void* v) {
163 : : gjs_arg_set<T, TAG>(arg, gjs_pointer_to_int<T>(v));
164 : : }
165 : :
166 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
167 : 141967 : [[nodiscard]] constexpr inline T gjs_arg_get(GIArgument* arg) {
168 : : if constexpr (std::is_same_v<T, bool> ||
169 : : (std::is_same_v<T, gboolean> && TAG == GI_TYPE_TAG_BOOLEAN))
170 : 221 : return T(!!gjs_arg_member<T, TAG>(arg));
171 : :
172 : 141746 : return gjs_arg_member<T, TAG>(arg);
173 : : }
174 : :
175 : : template <typename T, ExtraTag TAG>
176 : : [[nodiscard]] constexpr inline T gjs_arg_get(GIArgument* arg) {
177 : : return gjs_arg_member<T, TAG>(arg);
178 : : }
179 : :
180 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
181 : : [[nodiscard]] constexpr inline void* gjs_arg_get_as_pointer(GIArgument* arg) {
182 : : return gjs_int_to_pointer(gjs_arg_get<T, TAG>(arg));
183 : : }
184 : :
185 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
186 : 20740 : constexpr inline void gjs_arg_unset(GIArgument* arg) {
187 : : if constexpr (std::is_pointer_v<T>)
188 : 20492 : gjs_arg_set<T, TAG>(arg, nullptr);
189 : : else
190 : 248 : gjs_arg_set<T, TAG>(arg, static_cast<T>(0));
191 : 20740 : }
192 : :
193 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
194 : 10732 : [[nodiscard]] constexpr inline T gjs_arg_steal(GIArgument* arg) {
195 : 10732 : auto val = gjs_arg_get<T, TAG>(arg);
196 : 10732 : gjs_arg_unset<T, TAG>(arg);
197 : 10732 : return val;
198 : : }
199 : :
200 : : // Implementation to store rounded (u)int64_t numbers into double
201 : :
202 : : template <typename BigT>
203 : : [[nodiscard]] inline constexpr std::enable_if_t<
204 : : std::is_integral_v<BigT> && (std::numeric_limits<BigT>::max() >
205 : : std::numeric_limits<int32_t>::max()),
206 : : double>
207 : 34 : gjs_arg_get_maybe_rounded(GIArgument* arg) {
208 : 34 : BigT val = gjs_arg_get<BigT>(arg);
209 : :
210 [ + + + + : 66 : if (val < Gjs::min_safe_big_number<BigT>() ||
+ + ]
211 : 32 : val > Gjs::max_safe_big_number<BigT>()) {
212 : 7 : g_warning(
213 : : "Value %s cannot be safely stored in a JS Number "
214 : : "and may be rounded",
215 : : std::to_string(val).c_str());
216 : : }
217 : :
218 : 34 : return static_cast<double>(val);
219 : : }
220 : :
221 : : template <typename T, GITypeTag TAG = GI_TYPE_TAG_VOID>
222 : 19749 : GJS_JSAPI_RETURN_CONVENTION inline bool gjs_arg_set_from_js_value(
223 : : JSContext* cx, const JS::HandleValue& value, GIArgument* arg,
224 : : bool* out_of_range) {
225 : : if constexpr (Gjs::type_has_js_getter<T>())
226 : 9338 : return Gjs::js_value_to_c<TAG>(cx, value, &gjs_arg_member<T, TAG>(arg));
227 : :
228 : 10411 : Gjs::JsValueHolder::Relaxed<T> val{};
229 : :
230 [ - + ]: 10411 : if (!Gjs::js_value_to_c_checked<T, TAG>(cx, value, &val, out_of_range))
231 : 0 : return false;
232 : :
233 [ - + ]: 10411 : if (*out_of_range)
234 : 0 : return false;
235 : :
236 : 10411 : gjs_arg_set<T, TAG>(arg, val);
237 : :
238 : 10411 : return true;
239 : : }
240 : :
241 : : // A helper function to retrieve array lengths from a GIArgument (letting the
242 : : // compiler generate good instructions in case of big endian machines)
243 : 353 : [[nodiscard]] constexpr size_t gjs_gi_argument_get_array_length(
244 : : GITypeTag tag, GIArgument* arg) {
245 [ - + - - : 353 : switch (tag) {
+ + + +
- ]
246 : 0 : case GI_TYPE_TAG_INT8:
247 : 0 : return gjs_arg_get<int8_t>(arg);
248 : 1 : case GI_TYPE_TAG_UINT8:
249 : 1 : return gjs_arg_get<uint8_t>(arg);
250 : 0 : case GI_TYPE_TAG_INT16:
251 : 0 : return gjs_arg_get<int16_t>(arg);
252 : 0 : case GI_TYPE_TAG_UINT16:
253 : 0 : return gjs_arg_get<uint16_t>(arg);
254 : 88 : case GI_TYPE_TAG_INT32:
255 : 88 : return gjs_arg_get<int32_t>(arg);
256 : 5 : case GI_TYPE_TAG_UINT32:
257 : 5 : return gjs_arg_get<uint32_t>(arg);
258 : 17 : case GI_TYPE_TAG_INT64:
259 : 17 : return gjs_arg_get<int64_t>(arg);
260 : 242 : case GI_TYPE_TAG_UINT64:
261 : 242 : return gjs_arg_get<uint64_t>(arg);
262 : 0 : default:
263 : : g_assert_not_reached();
264 : : }
265 : : }
|