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