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: 2008 litl, LLC
4 : :
5 : : #ifndef GI_VALUE_H_
6 : : #define GI_VALUE_H_
7 : :
8 : : #include <config.h>
9 : :
10 : : #include <stdint.h>
11 : :
12 : : #include <sstream> // for ostringstream
13 : : #include <string> // for string
14 : : #include <type_traits>
15 : : #include <utility> // for move, swap
16 : : #include <vector> // for vector
17 : :
18 : : #include <glib-object.h>
19 : : #include <glib.h> // for FALSE, g_clear_pointer, g_free, g_variant_...
20 : :
21 : : #include <js/TypeDecls.h>
22 : :
23 : : #include "gi/arg-types-inl.h"
24 : : #include "gi/utils-inl.h"
25 : : #include "gjs/auto.h"
26 : : #include "gjs/macros.h"
27 : :
28 : : namespace Gjs {
29 : : struct AutoGValue : GValue {
30 : 1667 : AutoGValue() : GValue(G_VALUE_INIT) {
31 : : static_assert(sizeof(AutoGValue) == sizeof(GValue));
32 : 1667 : }
33 : 1590 : explicit AutoGValue(GType gtype) : AutoGValue() {
34 : 1590 : g_value_init(this, gtype);
35 : 1590 : }
36 : 0 : AutoGValue(AutoGValue const& src) : AutoGValue(G_VALUE_TYPE(&src)) {
37 : 0 : g_value_copy(&src, this);
38 : 0 : }
39 : : AutoGValue& operator=(AutoGValue other) {
40 : : // We need to cast to GValue here not to make swap to recurse here
41 : : std::swap(*static_cast<GValue*>(this), *static_cast<GValue*>(&other));
42 : : return *this;
43 : : }
44 : : AutoGValue(AutoGValue&& src) {
45 : : switch (G_VALUE_TYPE(&src)) {
46 : : case G_TYPE_NONE:
47 : : case G_TYPE_CHAR:
48 : : case G_TYPE_UCHAR:
49 : : case G_TYPE_BOOLEAN:
50 : : case G_TYPE_INT:
51 : : case G_TYPE_UINT:
52 : : case G_TYPE_LONG:
53 : : case G_TYPE_ULONG:
54 : : case G_TYPE_INT64:
55 : : case G_TYPE_UINT64:
56 : : case G_TYPE_FLOAT:
57 : : case G_TYPE_DOUBLE:
58 : : *static_cast<GValue*>(this) =
59 : : std::move(static_cast<GValue const&&>(src));
60 : : break;
61 : : default:
62 : : // We can't safely move in complex cases, so let's just copy
63 : : this->steal();
64 : : *this = src;
65 : : g_value_unset(&src);
66 : : }
67 : : }
68 : 3 : void steal() { *static_cast<GValue*>(this) = G_VALUE_INIT; }
69 : 1666 : ~AutoGValue() { g_value_unset(this); }
70 : : };
71 : :
72 : : /* This is based on what GMarshalling does, it is an unsupported API but
73 : : * gjs can be considered a glib implementation for JS, so it is fine
74 : : * to do this, but we need to be in sync with gmarshal.c in GLib.
75 : : * https://gitlab.gnome.org/GNOME/glib/-/blob/main/gobject/gmarshal.c
76 : : */
77 : :
78 : : template <typename TAG>
79 : 1768 : inline constexpr Tag::RealT<TAG> gvalue_get(const GValue* gvalue) {
80 : : if constexpr (std::is_same_v<TAG, Tag::GBoolean>)
81 : 11 : return gvalue->data[0].v_int != FALSE;
82 : : else if constexpr (std::is_same_v<TAG, bool>)
83 : 42 : return gvalue->data[0].v_int != FALSE;
84 : : else if constexpr (std::is_same_v<TAG, char>)
85 : : return gvalue->data[0].v_int;
86 : : else if constexpr (std::is_same_v<TAG, signed char>)
87 : 35 : return gvalue->data[0].v_int;
88 : : else if constexpr (std::is_same_v<TAG, unsigned char>)
89 : 19 : return gvalue->data[0].v_uint;
90 : : else if constexpr (std::is_same_v<TAG, int>)
91 : 184 : return gvalue->data[0].v_int;
92 : : else if constexpr (std::is_same_v<TAG, unsigned int>)
93 : 20 : return gvalue->data[0].v_uint;
94 : : else if constexpr (std::is_same_v<TAG, Tag::Long>)
95 : 56 : return gvalue->data[0].v_long;
96 : : else if constexpr (std::is_same_v<TAG, Tag::UnsignedLong>)
97 : 21 : return gvalue->data[0].v_ulong;
98 : : else if constexpr (std::is_same_v<TAG, int64_t>)
99 : 39 : return gvalue->data[0].v_int64;
100 : : else if constexpr (std::is_same_v<TAG, uint64_t>)
101 : 29 : return gvalue->data[0].v_uint64;
102 : : else if constexpr (std::is_same_v<TAG, Tag::Enum>)
103 : : return gvalue->data[0].v_long;
104 : : else if constexpr (std::is_same_v<TAG, Tag::UnsignedEnum>)
105 : : return gvalue->data[0].v_ulong;
106 : : else if constexpr (std::is_same_v<TAG, float>)
107 : 23 : return gvalue->data[0].v_float;
108 : : else if constexpr (std::is_same_v<TAG, double>)
109 : 36 : return gvalue->data[0].v_double;
110 : : else if constexpr (std::is_same_v<TAG, Tag::GType>)
111 : 5 : return gjs_pointer_to_int<GType>(gvalue->data[0].v_pointer);
112 : : else if constexpr (!std::is_pointer_v<TAG>)
113 : : static_assert(std::is_pointer_v<TAG>,
114 : : "Scalar type not properly handled");
115 : : else
116 : 1248 : return static_cast<TAG>(gvalue->data[0].v_pointer);
117 : : }
118 : :
119 : : template <typename TAG>
120 : 9880 : void gvalue_set(GValue* gvalue, Tag::RealT<TAG> value) {
121 : : if constexpr (std::is_same_v<TAG, Tag::GBoolean>)
122 : 8 : gvalue->data[0].v_int = value != FALSE;
123 : : else if constexpr (std::is_same_v<TAG, bool>)
124 : 9280 : gvalue->data[0].v_int = value != false;
125 : : else if constexpr (std::is_same_v<TAG, char>)
126 : : gvalue->data[0].v_int = value;
127 : : else if constexpr (std::is_same_v<TAG, signed char>)
128 : 24 : gvalue->data[0].v_int = value;
129 : : else if constexpr (std::is_same_v<TAG, unsigned char>)
130 : 24 : gvalue->data[0].v_uint = value;
131 : : else if constexpr (std::is_same_v<TAG, int>)
132 : 239 : gvalue->data[0].v_int = value;
133 : : else if constexpr (std::is_same_v<TAG, unsigned int>)
134 : 25 : gvalue->data[0].v_uint = value;
135 : : else if constexpr (std::is_same_v<TAG, Tag::Long>)
136 : 6 : gvalue->data[0].v_long = value;
137 : : else if constexpr (std::is_same_v<TAG, Tag::UnsignedLong>)
138 : 6 : gvalue->data[0].v_ulong = value;
139 : : else if constexpr (std::is_same_v<TAG, int64_t>)
140 : 39 : gvalue->data[0].v_int64 = value;
141 : : else if constexpr (std::is_same_v<TAG, uint64_t>)
142 : 31 : gvalue->data[0].v_uint64 = value;
143 : : else if constexpr (std::is_same_v<TAG, Tag::Enum>)
144 : : gvalue->data[0].v_long = value;
145 : : else if constexpr (std::is_same_v<TAG, Tag::UnsignedEnum>)
146 : : gvalue->data[0].v_ulong = value;
147 : : else if constexpr (std::is_same_v<TAG, float>)
148 : 91 : gvalue->data[0].v_float = value;
149 : : else if constexpr (std::is_same_v<TAG, double>)
150 : 97 : gvalue->data[0].v_double = value;
151 : : else if constexpr (std::is_same_v<TAG, Tag::GType>)
152 : 10 : gvalue->data[0].v_pointer = gjs_int_to_pointer(value);
153 : : else
154 : : static_assert(!std::is_scalar_v<Tag::RealT<TAG>>,
155 : : "Scalar type not properly handled");
156 : 9880 : }
157 : :
158 : : // Specialization for types where TAG and RealT<TAG> are the same type, to allow
159 : : // inferring template parameter
160 : : template <typename T,
161 : : typename = std::enable_if_t<std::is_same_v<Gjs::Tag::RealT<T>, T>>>
162 : 9663 : inline void gvalue_set(GValue* gvalue, T value) {
163 : 9663 : gvalue_set<T>(gvalue, value);
164 : 9663 : }
165 : :
166 : : template <typename T>
167 : : void gvalue_set(GValue* gvalue, T* value) = delete;
168 : :
169 : : template <>
170 : 108 : inline void gvalue_set(GValue* gvalue, char* value) {
171 [ - + ]: 108 : g_clear_pointer(&gvalue->data[0].v_pointer, g_free);
172 : 108 : gvalue->data[0].v_pointer = g_strdup(value);
173 : 108 : }
174 : :
175 : : template <>
176 : 49 : inline void gvalue_set(GValue* gvalue, GObject* value) {
177 : 49 : g_set_object(&gvalue->data[0].v_pointer, value);
178 : 49 : }
179 : :
180 : : template <>
181 : 53 : inline void gvalue_set(GValue* gvalue, GVariant* value) {
182 [ - + ]: 53 : g_clear_pointer(reinterpret_cast<GVariant**>(&gvalue->data[0].v_pointer),
183 : : g_variant_unref);
184 [ + + ]: 53 : gvalue->data[0].v_pointer = value ? g_variant_ref(value) : nullptr;
185 : 53 : }
186 : :
187 : : template <typename T>
188 : 0 : void gvalue_set(GValue* gvalue, std::nullptr_t) {
189 : : if constexpr (std::is_same_v<T, char*>) {
190 [ # # ]: 0 : g_clear_pointer(&gvalue->data[0].v_pointer, g_free);
191 : : } else if constexpr (std::is_same_v<T, GObject*>) {
192 : : g_set_object(&gvalue->data[0].v_pointer, nullptr);
193 : : } else if constexpr (std::is_same_v<T, GVariant*>) {
194 : : g_clear_pointer(reinterpret_cast<T*>(&gvalue->data[0].v_pointer),
195 : : g_variant_unref);
196 : : gvalue->data[0].v_pointer = nullptr;
197 : : } else {
198 : : static_assert(!std::is_pointer_v<T>, "Not a known pointer type");
199 : : }
200 : 0 : }
201 : :
202 : : template <typename TAG>
203 : 16 : void gvalue_take(GValue* gvalue, Tag::RealT<TAG> value) {
204 : : using T = Tag::RealT<TAG>;
205 : : if constexpr (!std::is_pointer_v<T>) {
206 : : return gvalue_set<TAG>(gvalue, value);
207 : : }
208 : :
209 : : if constexpr (std::is_same_v<T, char*>) {
210 [ - + ]: 16 : g_clear_pointer(&gvalue->data[0].v_pointer, g_free);
211 : 16 : gvalue->data[0].v_pointer = g_steal_pointer(&value);
212 : : } else if constexpr (std::is_same_v<T, GObject*>) {
213 : : g_clear_object(&gvalue->data[0].v_pointer);
214 : : gvalue->data[0].v_pointer = g_steal_pointer(&value);
215 : : } else if constexpr (std::is_same_v<T, GVariant*>) {
216 : : g_clear_pointer(reinterpret_cast<T*>(&gvalue->data[0].v_pointer),
217 : : g_variant_unref);
218 : : gvalue->data[0].v_pointer = value;
219 : : } else {
220 : : static_assert(!std::is_pointer_v<T>, "Not a known pointer type");
221 : : }
222 : 16 : }
223 : :
224 : : template <typename TAG>
225 : 0 : std::string gvalue_to_string(GValue* gvalue) {
226 : 0 : auto str =
227 : 0 : std::string("GValue of type ") + G_VALUE_TYPE_NAME(gvalue) + ": ";
228 : :
229 : : if constexpr (std::is_same_v<TAG, char*>) {
230 : 0 : str += std::string("\"") + Gjs::gvalue_get<TAG>(gvalue) + '"';
231 : : } else if constexpr (std::is_same_v<TAG, GVariant*>) {
232 : : AutoChar variant{g_variant_print(Gjs::gvalue_get<TAG>(gvalue), true)};
233 : : str += std::string("<") + variant.get() + '>';
234 : : } else if constexpr (std::is_arithmetic_v<TAG>) {
235 : 0 : str += std::to_string(Gjs::gvalue_get<TAG>(gvalue));
236 : : } else {
237 : 0 : std::ostringstream out;
238 : 0 : out << Gjs::gvalue_get<TAG>(gvalue);
239 : 0 : str += out.str();
240 : 0 : }
241 : 0 : return str;
242 : : }
243 : :
244 : : } // namespace Gjs
245 : :
246 : : using AutoGValueVector = std::vector<Gjs::AutoGValue>;
247 : :
248 : : GJS_JSAPI_RETURN_CONVENTION
249 : : bool gjs_value_to_g_value (JSContext *context,
250 : : JS::HandleValue value,
251 : : GValue *gvalue);
252 : : GJS_JSAPI_RETURN_CONVENTION
253 : : bool gjs_value_to_g_value_no_copy (JSContext *context,
254 : : JS::HandleValue value,
255 : : GValue *gvalue);
256 : :
257 : : GJS_JSAPI_RETURN_CONVENTION
258 : : bool gjs_value_from_g_value(JSContext *context,
259 : : JS::MutableHandleValue value_p,
260 : : const GValue *gvalue);
261 : :
262 : :
263 : : #endif // GI_VALUE_H_
|