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: 2024 Philip Chimento <philip.chimento@gmail.com>
4 : :
5 : : #pragma once
6 : :
7 : : #include <config.h>
8 : :
9 : : #include <limits.h> // for INT_MAX
10 : : #include <stdint.h>
11 : : #include <string.h>
12 : :
13 : : #include <cstddef> // for nullptr_t
14 : : #include <iterator>
15 : : #include <utility> // for pair, make_pair, move
16 : :
17 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
18 : : # include <sstream>
19 : : # include <string>
20 : : #endif
21 : :
22 : : #include <ffi.h>
23 : : #include <girepository/girepository.h>
24 : : #include <girepository/girffi.h>
25 : : #include <glib-object.h>
26 : : #include <glib.h>
27 : :
28 : : #include <js/GCPolicyAPI.h> // for IgnoreGCPolicy
29 : : #include <mozilla/Maybe.h>
30 : : #include <mozilla/Result.h>
31 : : #include <mozilla/ResultVariant.h>
32 : : #include <mozilla/Span.h>
33 : :
34 : : #include "gjs/auto.h"
35 : : #include "gjs/gerror-result.h"
36 : : #include "util/log.h"
37 : :
38 : : // This file is a C++ wrapper for libgirepository that attempts to be more
39 : : // null-safe and type-safe.
40 : : // Each introspection info type has the methods of the C API's GIFooInfo, but
41 : : // indicates whether the return value is owned by the caller (GI::AutoFooInfo)
42 : : // or unowned (GI::FooInfo), and uses Maybe to indicate when it is nullable.
43 : : // There are also GI::StackArgInfo and GI::StackTypeInfo for use with the
44 : : // CallableInfo.load_arg(), CallableInfo.load_return_type(), and
45 : : // ArgInfo.load_type() methods, for performance.
46 : :
47 : : // COMPAT: We use Mozilla's Maybe, Result, and Span types because they are more
48 : : // complete than the C++ standard library types.
49 : : // std::optional does not have transform(), and_then(), etc., until C++23.
50 : : // std::expected does not appear until C++23.
51 : : // std::span does not appear until C++20.
52 : :
53 : : // Note, only the methods actually needed in GJS are wrapped here. So if one is
54 : : // missing, that's not for any particular reason unless noted otherwise; it just
55 : : // was never needed yet.
56 : :
57 : : using BoolResult = mozilla::Result<mozilla::Ok, mozilla::Nothing>;
58 : :
59 : : namespace GI {
60 : :
61 : : enum class InfoTag : unsigned {
62 : : ARG,
63 : : BASE,
64 : : CALLABLE,
65 : : CALLBACK,
66 : : CONSTANT,
67 : : ENUM,
68 : : FIELD,
69 : : FLAGS,
70 : : FUNCTION,
71 : : INTERFACE,
72 : : OBJECT,
73 : : PROPERTY,
74 : : REGISTERED_TYPE,
75 : : SIGNAL,
76 : : STRUCT,
77 : : TYPE,
78 : : UNION,
79 : : VALUE,
80 : : VFUNC,
81 : : };
82 : :
83 : : namespace detail {
84 : : template <InfoTag TAG>
85 : : struct InfoTraits {};
86 : : template <>
87 : : struct InfoTraits<InfoTag::ARG> {
88 : : using CStruct = GIArgInfo;
89 : : };
90 : : template <>
91 : : struct InfoTraits<InfoTag::BASE> {
92 : : using CStruct = GIBaseInfo;
93 : : };
94 : : template <>
95 : : struct InfoTraits<InfoTag::CALLABLE> {
96 : : using CStruct = GICallableInfo;
97 : : };
98 : : template <>
99 : : struct InfoTraits<InfoTag::CALLBACK> {
100 : : using CStruct = GICallbackInfo;
101 : : };
102 : : template <>
103 : : struct InfoTraits<InfoTag::CONSTANT> {
104 : : using CStruct = GIConstantInfo;
105 : : };
106 : : template <>
107 : : struct InfoTraits<InfoTag::ENUM> {
108 : : using CStruct = GIEnumInfo;
109 : : };
110 : : template <>
111 : : struct InfoTraits<InfoTag::FIELD> {
112 : : using CStruct = GIFieldInfo;
113 : : };
114 : : template <>
115 : : struct InfoTraits<InfoTag::FLAGS> {
116 : : using CStruct = GIFlagsInfo;
117 : : };
118 : : template <>
119 : : struct InfoTraits<InfoTag::FUNCTION> {
120 : : using CStruct = GIFunctionInfo;
121 : : };
122 : : template <>
123 : : struct InfoTraits<InfoTag::INTERFACE> {
124 : : using CStruct = GIInterfaceInfo;
125 : : };
126 : : template <>
127 : : struct InfoTraits<InfoTag::OBJECT> {
128 : : using CStruct = GIObjectInfo;
129 : : };
130 : : template <>
131 : : struct InfoTraits<InfoTag::PROPERTY> {
132 : : using CStruct = GIPropertyInfo;
133 : : };
134 : : template <>
135 : : struct InfoTraits<InfoTag::REGISTERED_TYPE> {
136 : : using CStruct = GIRegisteredTypeInfo;
137 : : };
138 : : template <>
139 : : struct InfoTraits<InfoTag::SIGNAL> {
140 : : using CStruct = GISignalInfo;
141 : : };
142 : : template <>
143 : : struct InfoTraits<InfoTag::STRUCT> {
144 : : using CStruct = GIStructInfo;
145 : : };
146 : : template <>
147 : : struct InfoTraits<InfoTag::TYPE> {
148 : : using CStruct = GITypeInfo;
149 : : };
150 : : template <>
151 : : struct InfoTraits<InfoTag::UNION> {
152 : : using CStruct = GIUnionInfo;
153 : : };
154 : : template <>
155 : : struct InfoTraits<InfoTag::VALUE> {
156 : : using CStruct = GIValueInfo;
157 : : };
158 : : template <>
159 : : struct InfoTraits<InfoTag::VFUNC> {
160 : : using CStruct = GIVFuncInfo;
161 : : };
162 : :
163 : : using GTypeFunc = GType (*)();
164 : : static constexpr const GTypeFunc gtype_funcs[] = {
165 : : gi_arg_info_get_type,
166 : : gi_base_info_get_type,
167 : : gi_callable_info_get_type,
168 : : gi_callback_info_get_type,
169 : : gi_constant_info_get_type,
170 : : gi_enum_info_get_type,
171 : : gi_field_info_get_type,
172 : : gi_flags_info_get_type,
173 : : gi_function_info_get_type,
174 : : gi_interface_info_get_type,
175 : : gi_object_info_get_type,
176 : : gi_property_info_get_type,
177 : : gi_registered_type_info_get_type,
178 : : gi_signal_info_get_type,
179 : : gi_struct_info_get_type,
180 : : gi_type_info_get_type,
181 : : gi_union_info_get_type,
182 : : gi_value_info_get_type,
183 : : gi_vfunc_info_get_type,
184 : : };
185 : :
186 : 1392329 : constexpr GTypeFunc gtype_func(InfoTag tag) { return gtype_funcs[size_t(tag)]; }
187 : :
188 : : } // namespace detail
189 : :
190 : : template <typename Wrapper, InfoTag TAG>
191 : : class InfoOperations {};
192 : :
193 : : class StackArgInfo;
194 : : class StackTypeInfo;
195 : :
196 : : template <InfoTag TAG>
197 : : class OwnedInfo;
198 : :
199 : : template <InfoTag TAG>
200 : : class UnownedInfo;
201 : :
202 : : namespace detail {
203 : : // We want the underlying pointer to be inaccessible. However, the three storage
204 : : // classes sometimes have to interact with each others' pointers. It's easier to
205 : : // put all of those operations into detail::Pointer and have the classes be
206 : : // friends of it, than it is to expose all the pointer operations via friend
207 : : // declarations individually.
208 : : struct Pointer {
209 : : template <InfoTag TAG>
210 : : using CStruct = typename InfoTraits<TAG>::CStruct;
211 : :
212 : : template <InfoTag TAG>
213 : : [[nodiscard]]
214 : : static constexpr
215 : 198891 : typename detail::InfoTraits<TAG>::CStruct* cast(GIBaseInfo* ptr) {
216 : : // (the following is a GI_TAG_INFO() cast but written out)
217 : : return reinterpret_cast<typename detail::InfoTraits<TAG>::CStruct*>(
218 : 198891 : g_type_check_instance_cast(reinterpret_cast<GTypeInstance*>(ptr),
219 : 397782 : gtype_func(TAG)()));
220 : : }
221 : :
222 : : template <InfoTag TAG>
223 : 966065 : static constexpr CStruct<TAG>* get_from(const OwnedInfo<TAG>& owned) {
224 : 966065 : return const_cast<CStruct<TAG>*>(owned.m_info);
225 : : }
226 : :
227 : : template <InfoTag TAG>
228 : 1378815 : static constexpr CStruct<TAG>* get_from(const UnownedInfo<TAG>& unowned) {
229 : 1378815 : return const_cast<CStruct<TAG>*>(unowned.m_info);
230 : : }
231 : :
232 : : // Defined out-of-line because they are not templates and so StackArgInfo
233 : : // and StackTypeInfo need to be complete types.
234 : : static constexpr GIArgInfo* get_from(const StackArgInfo& stack);
235 : : static constexpr GITypeInfo* get_from(const StackTypeInfo& stack);
236 : :
237 : : template <InfoTag TAG>
238 : 273588 : static constexpr OwnedInfo<TAG> to_owned(CStruct<TAG>* ptr) {
239 : 273588 : return OwnedInfo<TAG>{ptr};
240 : : }
241 : :
242 : : template <InfoTag TAG>
243 : 212537 : static constexpr UnownedInfo<TAG> to_unowned(CStruct<TAG>* ptr) {
244 : 212537 : return UnownedInfo<TAG>{ptr};
245 : : }
246 : :
247 : : // Same, defined out of line so StackTypeInfo is not incomplete.
248 : : static void to_stack(GITypeInfo* ptr, StackTypeInfo* stack);
249 : :
250 : : template <InfoTag TAG>
251 : 37969 : static constexpr mozilla::Maybe<OwnedInfo<TAG>> nullable(
252 : : CStruct<TAG>* ptr) {
253 [ + + + + ]: 75938 : return ptr ? mozilla::Some(OwnedInfo<TAG>{ptr}) : mozilla::Nothing{};
254 : : }
255 : :
256 : : template <InfoTag TAG>
257 : 4071 : static constexpr mozilla::Maybe<UnownedInfo<TAG>> nullable_unowned(
258 : : CStruct<TAG>* ptr) {
259 [ + + ]: 4071 : return ptr ? mozilla::Some(UnownedInfo<TAG>{ptr}) : mozilla::Nothing{};
260 : : }
261 : :
262 : : template <InfoTag TAG>
263 : : [[nodiscard]]
264 : 1193438 : static constexpr bool typecheck(GIBaseInfo* ptr) {
265 [ - + + - : 1193438 : return G_TYPE_CHECK_INSTANCE_TYPE(ptr, gtype_func(TAG)());
+ + ]
266 : : }
267 : : };
268 : : } // namespace detail
269 : :
270 : : ///// UNOWNED INTROSPECTION INFO ///////////////////////////////////////////////
271 : :
272 : : template <InfoTag TAG>
273 : : class UnownedInfo : public InfoOperations<UnownedInfo<TAG>, TAG> {
274 : : friend struct detail::Pointer;
275 : :
276 : : using CStruct = typename detail::InfoTraits<TAG>::CStruct;
277 : : CStruct* m_info;
278 : : UnownedInfo() = delete;
279 : : UnownedInfo(std::nullptr_t) = delete; // NOLINT(runtime/explicit)
280 : : // https://github.com/cpplint/cpplint/issues/386
281 : : // No need to delete move constructor; declaring a copy constructor prevents
282 : : // it from being generated.
283 : :
284 : 572324 : explicit UnownedInfo(CStruct* info) : m_info(info) { validate(); }
285 : : [[nodiscard]] CStruct* ptr() const { return m_info; }
286 : :
287 : 572324 : void validate() const {
288 : : static_assert(sizeof(CStruct*) == sizeof(UnownedInfo<TAG>),
289 : : "UnownedInfo<T> should be byte-compatible with T*");
290 : :
291 : : #ifndef G_DISABLE_CAST_CHECKS
292 : 572324 : g_assert(m_info && "Info pointer cannot be null");
293 : 572324 : g_assert(detail::Pointer::typecheck<TAG>(GI_BASE_INFO(m_info)) &&
294 : : "Info type must match");
295 : : #endif // G_DISABLE_CAST_CHECKS
296 : 572324 : }
297 : :
298 : : public:
299 : : // Copying is cheap, UnownedInfo just consists of a pointer.
300 : 700782 : constexpr UnownedInfo(const UnownedInfo& other) : m_info(other.m_info) {}
301 : : UnownedInfo& operator=(const UnownedInfo& other) {
302 : : m_info = other.m_info;
303 : : return *this;
304 : : }
305 : :
306 : : // Caller must take care that the lifetime of UnownedInfo does not exceed
307 : : // the lifetime of the StackInfo. Do not store the UnownedInfo, or try to
308 : : // take ownership.
309 : 24405 : UnownedInfo(const StackArgInfo& other) // NOLINT(runtime/explicit)
310 : 24405 : : UnownedInfo(detail::Pointer::get_from(other)) {
311 : : static_assert(TAG == InfoTag::ARG);
312 : 24405 : }
313 : 100628 : UnownedInfo(const StackTypeInfo& other) // NOLINT(runtime/explicit)
314 : 100628 : : UnownedInfo(detail::Pointer::get_from(other)) {
315 : : static_assert(TAG == InfoTag::TYPE);
316 : 100628 : }
317 : :
318 : : // Caller must take care that the lifetime of UnownedInfo does not exceed
319 : : // the lifetime of the originating OwnedInfo. That means, if you store it,
320 : : // only store it as an OwnedInfo, adding another reference.
321 : 230685 : UnownedInfo(const OwnedInfo<TAG>& other) // NOLINT(runtime/explicit)
322 : 230685 : : UnownedInfo(detail::Pointer::get_from(other)) {}
323 : : };
324 : :
325 : : using ArgInfo = UnownedInfo<InfoTag::ARG>;
326 : : using BaseInfo = UnownedInfo<InfoTag::BASE>;
327 : : using CallableInfo = UnownedInfo<InfoTag::CALLABLE>;
328 : : using CallbackInfo = UnownedInfo<InfoTag::CALLBACK>;
329 : : using ConstantInfo = UnownedInfo<InfoTag::CONSTANT>;
330 : : using EnumInfo = UnownedInfo<InfoTag::ENUM>;
331 : : using FieldInfo = UnownedInfo<InfoTag::FIELD>;
332 : : using FlagsInfo = UnownedInfo<InfoTag::FLAGS>;
333 : : using FunctionInfo = UnownedInfo<InfoTag::FUNCTION>;
334 : : using InterfaceInfo = UnownedInfo<InfoTag::INTERFACE>;
335 : : using ObjectInfo = UnownedInfo<InfoTag::OBJECT>;
336 : : using RegisteredTypeInfo = UnownedInfo<InfoTag::REGISTERED_TYPE>;
337 : : using StructInfo = UnownedInfo<InfoTag::STRUCT>;
338 : : using TypeInfo = UnownedInfo<InfoTag::TYPE>;
339 : : using UnionInfo = UnownedInfo<InfoTag::UNION>;
340 : : using ValueInfo = UnownedInfo<InfoTag::VALUE>;
341 : : using VFuncInfo = UnownedInfo<InfoTag::VFUNC>;
342 : :
343 : : ///// OWNED INTROSPECTION INFO /////////////////////////////////////////////////
344 : :
345 : : template <InfoTag TAG>
346 : : class OwnedInfo : public InfoOperations<OwnedInfo<TAG>, TAG> {
347 : : friend struct detail::Pointer;
348 : :
349 : : using CStruct = typename detail::InfoTraits<TAG>::CStruct;
350 : : CStruct* m_info;
351 : :
352 : : OwnedInfo() = delete;
353 : : OwnedInfo(std::nullptr_t) = delete; // NOLINT(runtime/explicit)
354 : : // https://github.com/cpplint/cpplint/issues/386
355 : 352033 : explicit OwnedInfo(CStruct* info) : m_info(info) {
356 : : static_assert(sizeof(CStruct*) == sizeof(OwnedInfo<TAG>),
357 : : "OwnedInfo<T> should be byte-compatible with T*");
358 : : #ifndef G_DISABLE_CAST_CHECKS
359 : 352033 : g_assert(m_info && "Info pointer cannot be null");
360 : 352033 : g_assert(detail::Pointer::typecheck<TAG>(GI_BASE_INFO(m_info)) &&
361 : : "Info type must match");
362 : : #endif // G_DISABLE_CAST_CHECKS
363 : 352033 : }
364 : :
365 : : [[nodiscard]] CStruct* ptr() const { return m_info; }
366 : :
367 : : public:
368 : : // Copy OwnedInfo from another OwnedInfo. Explicit because it takes a
369 : : // reference.
370 : 10282 : explicit OwnedInfo(const OwnedInfo& other) : OwnedInfo(other.m_info) {
371 : 10282 : gi_base_info_ref(m_info);
372 : 10282 : }
373 : : // Move another OwnedInfo into this one
374 : 29887 : OwnedInfo(OwnedInfo&& other) : OwnedInfo(other.m_info) {
375 : 29887 : other.m_info = nullptr;
376 : 29887 : }
377 : : OwnedInfo& operator=(const OwnedInfo& other) {
378 : : m_info = other.m_info;
379 : : gi_base_info_ref(m_info);
380 : : return *this;
381 : : }
382 : 31 : OwnedInfo& operator=(OwnedInfo&& other) {
383 : 31 : std::swap(m_info, other.m_info);
384 : 31 : return *this;
385 : : }
386 [ + + ]: 351794 : ~OwnedInfo() { g_clear_pointer(&m_info, gi_base_info_unref); }
387 : :
388 : : // Copy OwnedInfo from UnownedInfo, which also comes down to just taking a
389 : : // reference. Explicit because it takes a reference. However, make sure the
390 : : // UnownedInfo is not borrowed from a StackInfo!
391 : 17929 : explicit OwnedInfo(const UnownedInfo<TAG>& other)
392 : 17929 : : OwnedInfo(detail::Pointer::get_from(other)) {
393 : 17929 : gi_base_info_ref(m_info);
394 : 17929 : }
395 : :
396 : : // Do not try to take ownership of a StackInfo.
397 : : // (cpplint false positive: https://github.com/cpplint/cpplint/issues/386)
398 : : OwnedInfo(const StackArgInfo& other) = delete; // NOLINT(runtime/explicit)
399 : : OwnedInfo(const StackTypeInfo& other) = delete; // NOLINT(runtime/explicit)
400 : : };
401 : :
402 : : using AutoArgInfo = OwnedInfo<InfoTag::ARG>;
403 : : using AutoBaseInfo = OwnedInfo<InfoTag::BASE>;
404 : : using AutoCallableInfo = OwnedInfo<InfoTag::CALLABLE>;
405 : : using AutoCallbackInfo = OwnedInfo<InfoTag::CALLBACK>;
406 : : using AutoEnumInfo = OwnedInfo<InfoTag::ENUM>;
407 : : using AutoFieldInfo = OwnedInfo<InfoTag::FIELD>;
408 : : using AutoFunctionInfo = OwnedInfo<InfoTag::FUNCTION>;
409 : : using AutoInterfaceInfo = OwnedInfo<InfoTag::INTERFACE>;
410 : : using AutoObjectInfo = OwnedInfo<InfoTag::OBJECT>;
411 : : using AutoPropertyInfo = OwnedInfo<InfoTag::PROPERTY>;
412 : : using AutoRegisteredTypeInfo = OwnedInfo<InfoTag::REGISTERED_TYPE>;
413 : : using AutoSignalInfo = OwnedInfo<InfoTag::SIGNAL>;
414 : : using AutoStructInfo = OwnedInfo<InfoTag::STRUCT>;
415 : : using AutoTypeInfo = OwnedInfo<InfoTag::TYPE>;
416 : : using AutoUnionInfo = OwnedInfo<InfoTag::UNION>;
417 : : using AutoValueInfo = OwnedInfo<InfoTag::VALUE>;
418 : : using AutoVFuncInfo = OwnedInfo<InfoTag::VFUNC>;
419 : :
420 : : // The various specializations of InfoOperations are used to ensure that the
421 : : // OwnedInfo and UnownedInfo specializations for a particular GIFooInfo type
422 : : // (and the stack-allocated class, if applicable) have the same methods. So, for
423 : : // example, AutoTypeInfo, TypeInfo, and StackTypeInfo all inherit from
424 : : // InfoOperations<T, InfoTag::TYPE>.
425 : :
426 : : template <class Wrapper>
427 : : class InfoOperations<Wrapper, InfoTag::BASE> {
428 : : protected:
429 : : [[nodiscard]]
430 : 833838 : GIBaseInfo* ptr() const {
431 : 833838 : return GI_BASE_INFO(
432 : : detail::Pointer::get_from(*static_cast<const Wrapper*>(this)));
433 : : }
434 : :
435 : : // Helper for adapting GLib-style error reporting into GErrorResult
436 : : [[nodiscard]]
437 : 13981 : static Gjs::GErrorResult<> bool_gerror(bool ok, GError* error) {
438 [ - + ]: 13981 : if (!ok)
439 : 0 : return mozilla::Err(error);
440 : 13981 : return mozilla::Ok{};
441 : : }
442 : :
443 : : // Helper for adapting C-style success/failure result into mozilla::Result.
444 : : // Used when there is no GError out parameter.
445 : : [[nodiscard]]
446 : 2782 : static BoolResult bool_to_result(bool ok) {
447 [ + + ]: 2782 : if (!ok)
448 : 23 : return Err(mozilla::Nothing{});
449 : 2759 : return mozilla::Ok{};
450 : : }
451 : :
452 : : public:
453 : : template <InfoTag TAG>
454 : 22 : bool operator==(const OwnedInfo<TAG>& other) const {
455 : 22 : return gi_base_info_equal(
456 : 44 : ptr(), GI_BASE_INFO(detail::Pointer::get_from(other)));
457 : : }
458 : : template <InfoTag TAG>
459 : 567 : bool operator==(const UnownedInfo<TAG>& other) const {
460 : 567 : return gi_base_info_equal(
461 : 1134 : ptr(), GI_BASE_INFO(detail::Pointer::get_from(other)));
462 : : }
463 : : template <InfoTag TAG>
464 : : bool operator!=(const OwnedInfo<TAG>& other) const {
465 : : return !(*this == other);
466 : : }
467 : : template <InfoTag TAG>
468 : 30 : bool operator!=(const UnownedInfo<TAG>& other) const {
469 : 30 : return !(*this == other);
470 : : }
471 : :
472 : : template <InfoTag TAG = InfoTag::BASE>
473 : : [[nodiscard]]
474 : 4071 : mozilla::Maybe<const UnownedInfo<TAG>> container() const {
475 : 8142 : return detail::Pointer::nullable_unowned<TAG>(
476 : 4071 : detail::Pointer::cast<TAG>(gi_base_info_get_container(ptr())));
477 : : }
478 : : [[nodiscard]]
479 : 248 : bool is_deprecated() const {
480 : 248 : return gi_base_info_is_deprecated(ptr());
481 : : }
482 : : [[nodiscard]]
483 : 193390 : const char* name() const {
484 : 193390 : return gi_base_info_get_name(ptr());
485 : : }
486 : : [[nodiscard]]
487 : 41359 : const char* ns() const {
488 : 41359 : return gi_base_info_get_namespace(ptr());
489 : : }
490 : : [[nodiscard]]
491 : 10850 : const char* type_string() const {
492 : 10850 : return g_type_name_from_instance(
493 : 21700 : reinterpret_cast<GTypeInstance*>(ptr()));
494 : : }
495 : :
496 : : // Type-checking methods
497 : :
498 : : [[nodiscard]]
499 : 356 : bool is_callback() const {
500 [ - + + - : 356 : return GI_IS_CALLBACK_INFO(ptr());
+ + ]
501 : : }
502 : : [[nodiscard]]
503 : 51619 : bool is_enum_or_flags() const {
504 [ - + + - : 51619 : return GI_IS_ENUM_INFO(ptr());
+ + ]
505 : : }
506 [ - + + - : 2876 : [[nodiscard]] bool is_flags() const { return GI_IS_FLAGS_INFO(ptr()); }
+ + ]
507 : : [[nodiscard]]
508 : 13778 : bool is_function() const {
509 [ - + + - : 13778 : return GI_IS_FUNCTION_INFO(ptr());
+ + ]
510 : : }
511 : : [[nodiscard]]
512 : 3730 : bool is_interface() const {
513 [ - + + - : 3730 : return GI_IS_INTERFACE_INFO(ptr());
+ - ]
514 : : }
515 [ - + + - : 9591 : [[nodiscard]] bool is_object() const { return GI_IS_OBJECT_INFO(ptr()); }
+ + ]
516 : : [[nodiscard]]
517 : : bool is_registered_type() const {
518 : : return GI_IS_REGISTERED_TYPE_INFO(ptr());
519 : : }
520 [ - + + - : 540 : [[nodiscard]] bool is_struct() const { return GI_IS_STRUCT_INFO(ptr()); }
+ + ]
521 [ - + + - : 2691 : [[nodiscard]] bool is_union() const { return GI_IS_UNION_INFO(ptr()); }
+ + ]
522 : : [[nodiscard]]
523 : 24685 : bool is_unresolved() const {
524 : : // We don't have a wrapper for GIUnresolvedInfo because it has no
525 : : // methods, but you can check whether a BaseInfo is one.
526 [ - + + - : 24685 : return GI_IS_UNRESOLVED_INFO(ptr());
- + ]
527 : : }
528 [ - + + - : 2 : [[nodiscard]] bool is_vfunc() const { return GI_IS_VFUNC_INFO(ptr()); }
+ - ]
529 : : // Don't enumerate types which GJS doesn't define on namespaces.
530 : : // See gjs_define_info().
531 : : [[nodiscard]]
532 : 11251 : bool is_enumerable() const {
533 [ - + + - : 11251 : return GI_IS_REGISTERED_TYPE_INFO(ptr()) ||
- + ]
534 [ + + - + : 11251 : GI_IS_FUNCTION_INFO(ptr()) || GI_IS_CONSTANT_INFO(ptr());
+ - + + +
+ - + + -
+ + + + ]
535 : : }
536 : :
537 : : // Having this casting function be a template is slightly inconsistent with
538 : : // all the is_X() type-checking methods above. But if we were to make
539 : : // separate as_X() methods, C++ can't easily deal with all the forward decls
540 : : // of UnownedInfo<T> instantiating the template.
541 : : template <InfoTag TAG2>
542 : : [[nodiscard]]
543 : 269081 : mozilla::Maybe<const UnownedInfo<TAG2>> as() const {
544 [ + + ]: 269081 : if (!detail::Pointer::typecheck<TAG2>(ptr()))
545 : 95562 : return {};
546 : 173519 : auto* checked_ptr = detail::Pointer::cast<TAG2>(ptr());
547 : 173519 : return mozilla::Some(detail::Pointer::to_unowned<TAG2>(checked_ptr));
548 : : }
549 : :
550 : 10842 : void log_usage() const {
551 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
552 : : mozilla::Maybe<GI::BaseInfo> parent = container();
553 : : gjs_debug_gi_usage(
554 : : "{ GIInfoType %s, \"%s\", \"%s\", \"%s\" }", type_string(), ns(),
555 : : parent.map(std::mem_fn(&GI::BaseInfo::name)).valueOr(""), name());
556 : : #endif // GJS_VERBOSE_ENABLE_GI_USAGE
557 : 10842 : }
558 : : };
559 : :
560 : : template <typename Wrapper>
561 : : using BaseInfoOperations = InfoOperations<Wrapper, InfoTag::BASE>;
562 : :
563 : : // The following InfoIterator class is a C++ iterator implementation that's used
564 : : // to implement the C iteration pattern:
565 : : //
566 : : // unsigned n_bars = gi_foo_info_get_n_bars(info);
567 : : // for (unsigned ix = 0; ix < n_bars; ix++) {
568 : : // GIBarInfo* bar = gi_foo_info_get_bar(info, ix);
569 : : // do_stuff(bar);
570 : : // gi_base_info_unref(bar);
571 : : // }
572 : : //
573 : : // as a more idiomatic C++ pattern:
574 : : //
575 : : // for (AutoBarInfo bar : info.bars())
576 : : // do_stuff(bar);
577 : :
578 : : template <typename T>
579 : : using NInfosFunc = unsigned (*)(T);
580 : :
581 : : template <typename T, InfoTag TAG>
582 : : using GetInfoFunc = typename detail::InfoTraits<TAG>::CStruct* (*)(T, unsigned);
583 : :
584 : : template <typename T, InfoTag TAG, NInfosFunc<T> get_n_infos,
585 : : GetInfoFunc<T, TAG> get_info>
586 : : class InfoIterator {
587 : : T m_obj;
588 : : int m_ix;
589 : :
590 : 78370 : InfoIterator(T obj, int ix) : m_obj(obj), m_ix(ix) {}
591 : :
592 : : public:
593 : : using iterator_category = std::forward_iterator_tag;
594 : : using difference_type = int;
595 : : using value_type = OwnedInfo<TAG>;
596 : : using pointer = value_type*;
597 : : using reference = value_type&;
598 : :
599 : 28486 : explicit InfoIterator(T info) : InfoIterator(info, 0) {}
600 : :
601 : 163986 : OwnedInfo<TAG> operator*() const {
602 : 163986 : return detail::Pointer::to_owned<TAG>(get_info(m_obj, m_ix));
603 : : }
604 : 160875 : InfoIterator& operator++() {
605 : 160875 : m_ix++;
606 : 160875 : return *this;
607 : : }
608 : : InfoIterator operator++(int) {
609 : : InfoIterator tmp = *this;
610 : : m_ix++;
611 : : return tmp;
612 : : }
613 : 3867 : bool operator==(const InfoIterator& other) const {
614 [ + - + + ]: 3867 : return m_obj == other.m_obj && m_ix == other.m_ix;
615 : : }
616 : 185817 : bool operator!=(const InfoIterator& other) const {
617 [ + - + + ]: 185817 : return m_obj != other.m_obj || m_ix != other.m_ix;
618 : : }
619 : :
620 : : [[nodiscard]]
621 : 2843 : mozilla::Maybe<OwnedInfo<TAG>> operator[](size_t ix) const {
622 : 2843 : return detail::Pointer::nullable<TAG>(get_info(m_obj, ix));
623 : : }
624 : :
625 : 24942 : [[nodiscard]] InfoIterator begin() const { return InfoIterator{m_obj, 0}; }
626 : : [[nodiscard]]
627 : 24942 : InfoIterator end() const {
628 : 49857 : int n_fields = get_n_infos(m_obj);
629 : 24942 : return InfoIterator{m_obj, n_fields};
630 : : }
631 : 2071 : [[nodiscard]] size_t size() const { return get_n_infos(m_obj); }
632 : : };
633 : :
634 : : // These are used to delete the type-checking and casting methods from
635 : : // InfoOperations specializations for subtypes of GIBaseInfo, as appropriate.
636 : : // So, for example, if you have AutoCallableInfo, you still want to be able to
637 : : // check is_callback, is_function, and is_vfunc, but not is_boxed etc.
638 : :
639 : : #define DELETE_CALLABLE_TYPECHECK_METHODS \
640 : : bool is_callback() const = delete; \
641 : : bool is_function() const = delete; \
642 : : bool is_vfunc() const = delete;
643 : :
644 : : #define DELETE_REGISTERED_TYPE_TYPECHECK_METHODS \
645 : : bool is_boxed() const = delete; \
646 : : bool is_enum_or_flags() const = delete; \
647 : : bool is_flags() const = delete; \
648 : : bool is_interface() const = delete; \
649 : : bool is_object() const = delete; \
650 : : bool is_struct() const = delete; \
651 : : bool is_union() const = delete;
652 : :
653 : : #define DELETE_SUPERCLASS_TYPECHECK_METHODS \
654 : : bool is_registered_type() const = delete; \
655 : : bool is_unresolved() const = delete;
656 : :
657 : : #define DELETE_CAST_METHOD \
658 : : template <InfoTag TAG2> \
659 : : mozilla::Maybe<const UnownedInfo<TAG2>> as() const = delete;
660 : :
661 : : #define DELETE_ALL_TYPECHECK_METHODS \
662 : : DELETE_SUPERCLASS_TYPECHECK_METHODS \
663 : : DELETE_CALLABLE_TYPECHECK_METHODS \
664 : : DELETE_REGISTERED_TYPE_TYPECHECK_METHODS \
665 : : DELETE_CAST_METHOD
666 : :
667 : : // Needs to come first, because InfoOperations<ARG> and InfoOperations<CALLABLE>
668 : : // instantiate the template by having methods with GI::StackTypeInfo* parameters
669 : : template <class Wrapper>
670 : : class InfoOperations<Wrapper, InfoTag::TYPE>
671 : : : public BaseInfoOperations<Wrapper> {
672 : : DELETE_ALL_TYPECHECK_METHODS;
673 : :
674 : : [[nodiscard]]
675 : 453093 : GITypeInfo* ptr() const {
676 : 453093 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
677 : : }
678 : : // Private, because we don't use this directly. Use the more semantic
679 : : // versions below (element_type() for GSLIST, GLIST, and ARRAY type tags;
680 : : // key_type() and value_type() for GHASH.)
681 : : [[nodiscard]]
682 : 5252 : AutoTypeInfo param_type(int n) const {
683 : : return detail::Pointer::to_owned<InfoTag::TYPE>(
684 : 5252 : gi_type_info_get_param_type(ptr(), n));
685 : : }
686 : :
687 : : public:
688 : : [[nodiscard]]
689 : 2681 : mozilla::Maybe<unsigned> array_length_index() const {
690 : : unsigned out;
691 [ + + ]: 2681 : if (!gi_type_info_get_array_length_index(ptr(), &out))
692 : 1426 : return {};
693 : 1255 : return mozilla::Some(out);
694 : : }
695 : : [[nodiscard]]
696 : 97 : mozilla::Maybe<size_t> array_fixed_size() const {
697 : : size_t out;
698 [ + + ]: 97 : if (!gi_type_info_get_array_fixed_size(ptr(), &out))
699 : 10 : return {};
700 : 87 : return mozilla::Some(out);
701 : : }
702 : : [[nodiscard]]
703 : 4174 : GIArrayType array_type() const {
704 : 4174 : return gi_type_info_get_array_type(ptr());
705 : : }
706 : 248 : void argument_from_hash_pointer(void* hash_pointer, GIArgument* arg) const {
707 : 248 : gi_type_info_argument_from_hash_pointer(ptr(), hash_pointer, arg);
708 : 248 : }
709 : : [[nodiscard]]
710 : 3 : void* hash_pointer_from_argument(GIArgument* arg) const {
711 : 3 : return gi_type_info_hash_pointer_from_argument(ptr(), arg);
712 : : }
713 : : // Unlike the libgirepository API, this doesn't return null. Only call it on
714 : : // TypeInfo with GI_TYPE_TAG_INTERFACE tag.
715 : : [[nodiscard]]
716 : 84681 : AutoBaseInfo interface() const {
717 : 84681 : g_assert(tag() == GI_TYPE_TAG_INTERFACE);
718 : : return detail::Pointer::to_owned<InfoTag::BASE>(
719 : 84681 : gi_type_info_get_interface(ptr()));
720 : : }
721 : : [[nodiscard]]
722 : 37812 : bool is_pointer() const {
723 : 37812 : return gi_type_info_is_pointer(ptr());
724 : : }
725 : : [[nodiscard]]
726 : 1041 : bool is_zero_terminated() const {
727 : 1041 : return gi_type_info_is_zero_terminated(ptr());
728 : : }
729 : : [[nodiscard]]
730 : 347 : GITypeTag storage_type() const {
731 : 347 : return gi_type_info_get_storage_type(ptr());
732 : : }
733 : 316757 : [[nodiscard]] GITypeTag tag() const { return gi_type_info_get_tag(ptr()); }
734 : : void extract_ffi_return_value(GIFFIReturnValue* ffi_value,
735 : : GIArgument* arg) const {
736 : : gi_type_info_extract_ffi_return_value(ptr(), ffi_value, arg);
737 : : }
738 : :
739 : : // Methods not present in GIRepository
740 : :
741 : : [[nodiscard]] bool can_be_allocated_directly() const;
742 : : [[nodiscard]] bool direct_allocation_has_pointers() const;
743 : : [[nodiscard]]
744 : 2 : const char* display_string() const {
745 : 2 : GITypeTag type_tag = tag();
746 [ + - ]: 2 : if (type_tag == GI_TYPE_TAG_INTERFACE)
747 : 2 : return interface().type_string();
748 : 0 : return gi_type_tag_to_string(type_tag);
749 : : }
750 : :
751 : : [[nodiscard]]
752 : : bool is_string_type() const {
753 : : GITypeTag t = tag();
754 : : return t == GI_TYPE_TAG_FILENAME || t == GI_TYPE_TAG_UTF8;
755 : : }
756 : :
757 : : [[nodiscard]]
758 : 62232 : bool is_basic() const {
759 : 62232 : GITypeTag t = tag();
760 [ + + + - : 62232 : if (t == GI_TYPE_TAG_VOID && is_pointer())
+ + ]
761 : 280 : return false; // void* is not a basic type
762 [ + + + + ]: 61952 : return GI_TYPE_TAG_IS_BASIC(t);
763 : : }
764 : :
765 : : // More semantic versions of param_type(), that are only intended to be
766 : : // called on TypeInfos where the result is known not to be null
767 : :
768 : : [[nodiscard]]
769 : 5056 : AutoTypeInfo element_type() const {
770 : 5056 : g_assert(tag() == GI_TYPE_TAG_ARRAY || tag() == GI_TYPE_TAG_GLIST ||
771 : : tag() == GI_TYPE_TAG_GSLIST);
772 : 5056 : return param_type(0);
773 : : }
774 : :
775 : : [[nodiscard]]
776 : 98 : AutoTypeInfo key_type() const {
777 : 98 : g_assert(tag() == GI_TYPE_TAG_GHASH);
778 : 98 : return param_type(0);
779 : : }
780 : :
781 : : [[nodiscard]]
782 : 98 : AutoTypeInfo value_type() const {
783 : 98 : g_assert(tag() == GI_TYPE_TAG_GHASH);
784 : 98 : return param_type(1);
785 : : }
786 : : };
787 : :
788 : : // Needs to come after InfoOperations<TYPE> but before InfoOperations<CALLABLE>
789 : : // since this class instantiates the GI::StackTypeInfo template, but
790 : : // InfoOperations<CALLABLE> instantiates this one.
791 : : template <class Wrapper>
792 : : class InfoOperations<Wrapper, InfoTag::ARG>
793 : : : public BaseInfoOperations<Wrapper> {
794 : : DELETE_ALL_TYPECHECK_METHODS;
795 : :
796 : : [[nodiscard]]
797 : 155384 : GIArgInfo* ptr() const {
798 : 155384 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
799 : : }
800 : :
801 : : public:
802 : : [[nodiscard]]
803 : 23623 : bool caller_allocates() const {
804 : 23623 : return gi_arg_info_is_caller_allocates(ptr());
805 : : }
806 : : [[nodiscard]]
807 : 1078 : mozilla::Maybe<unsigned> closure_index() const {
808 : : unsigned out;
809 [ + + ]: 1078 : if (!gi_arg_info_get_closure_index(ptr(), &out))
810 : 416 : return {};
811 : 662 : return mozilla::Some(out);
812 : : }
813 : : [[nodiscard]]
814 : 1078 : mozilla::Maybe<unsigned> destroy_index() const {
815 : : unsigned out;
816 [ + + ]: 1078 : if (!gi_arg_info_get_destroy_index(ptr(), &out))
817 : 913 : return {};
818 : 165 : return mozilla::Some(out);
819 : : }
820 : : [[nodiscard]]
821 : 28557 : GIDirection direction() const {
822 : 28557 : return gi_arg_info_get_direction(ptr());
823 : : }
824 : 27459 : void load_type(StackTypeInfo* type) const {
825 : 27459 : gi_arg_info_load_type_info(ptr(), detail::Pointer::get_from(*type));
826 : 27459 : }
827 : : [[nodiscard]]
828 : 1330 : bool is_optional() const {
829 : 1330 : return gi_arg_info_is_optional(ptr());
830 : : }
831 : : [[nodiscard]]
832 : 35 : bool is_return_value() const {
833 : 35 : return gi_arg_info_is_return_value(ptr());
834 : : }
835 : : [[nodiscard]]
836 : 23582 : bool may_be_null() const {
837 : 23582 : return gi_arg_info_may_be_null(ptr());
838 : : }
839 : : [[nodiscard]]
840 : 47565 : GITransfer ownership_transfer() const {
841 : 47565 : return gi_arg_info_get_ownership_transfer(ptr());
842 : : }
843 : : [[nodiscard]]
844 : 1077 : GIScopeType scope() const {
845 : 1077 : return gi_arg_info_get_scope(ptr());
846 : : }
847 : : };
848 : :
849 : : template <class Wrapper>
850 : : class InfoOperations<Wrapper, InfoTag::CALLABLE>
851 : : : public BaseInfoOperations<Wrapper> {
852 : : DELETE_SUPERCLASS_TYPECHECK_METHODS;
853 : : DELETE_REGISTERED_TYPE_TYPECHECK_METHODS;
854 : :
855 : : [[nodiscard]]
856 : 384109 : GICallableInfo* ptr() const {
857 : 384109 : return GI_CALLABLE_INFO(
858 : : detail::Pointer::get_from(*static_cast<const Wrapper*>(this)));
859 : : }
860 : :
861 : : public:
862 : : using ArgsIterator =
863 : : InfoIterator<GICallableInfo*, InfoTag::ARG, gi_callable_info_get_n_args,
864 : : gi_callable_info_get_arg>;
865 : : [[nodiscard]]
866 : : ArgsIterator args() const {
867 : : return ArgsIterator{ptr()};
868 : : }
869 : : [[nodiscard]]
870 : 11 : AutoArgInfo arg(unsigned n) const {
871 : 11 : g_assert(n < n_args());
872 : : return detail::Pointer::to_owned<InfoTag::ARG>(
873 : 11 : gi_callable_info_get_arg(ptr(), n));
874 : : }
875 : : [[nodiscard]]
876 : 135712 : unsigned n_args() const {
877 : 135712 : return gi_callable_info_get_n_args(ptr());
878 : : }
879 : :
880 : : [[nodiscard]]
881 : 11655 : GITransfer caller_owns() const {
882 : 11655 : return gi_callable_info_get_caller_owns(ptr());
883 : : }
884 : : [[nodiscard]]
885 : 76930 : bool can_throw_gerror() const {
886 : 76930 : return gi_callable_info_can_throw_gerror(ptr());
887 : : }
888 : : [[nodiscard]]
889 : 343 : void* closure_native_address(ffi_closure* closure) const {
890 : 343 : return gi_callable_info_get_closure_native_address(ptr(), closure);
891 : : }
892 : : [[nodiscard]]
893 : 343 : ffi_closure* create_closure(ffi_cif* cif, GIFFIClosureCallback callback,
894 : : void* user_data) const {
895 : 343 : return gi_callable_info_create_closure(ptr(), cif, callback, user_data);
896 : : }
897 : 331 : void destroy_closure(ffi_closure* closure) const {
898 : 331 : gi_callable_info_destroy_closure(ptr(), closure);
899 : 331 : }
900 : : [[nodiscard]]
901 : 2 : Gjs::GErrorResult<> init_function_invoker(
902 : : void* address, GIFunctionInvoker* invoker) const {
903 : 2 : GError* error = nullptr;
904 : 2 : return this->bool_gerror(gi_function_invoker_new_for_address(
905 : : address, ptr(), invoker, &error),
906 : 4 : error);
907 : : }
908 : : [[nodiscard]]
909 : 4000 : GITransfer instance_ownership_transfer() const {
910 : 4000 : return gi_callable_info_get_instance_ownership_transfer(ptr());
911 : : }
912 : : [[nodiscard]]
913 : 90778 : bool is_method() const {
914 : 90778 : return gi_callable_info_is_method(ptr());
915 : : }
916 : 27479 : void load_arg(unsigned n, StackArgInfo* arg) const {
917 : 27479 : g_assert(n < n_args());
918 : 27479 : gi_callable_info_load_arg(ptr(), n, detail::Pointer::get_from(*arg));
919 : 27479 : }
920 : 25468 : void load_return_type(StackTypeInfo* type) const {
921 : 25468 : gi_callable_info_load_return_type(ptr(),
922 : : detail::Pointer::get_from(*type));
923 : 25468 : }
924 : : [[nodiscard]]
925 : 11000 : bool may_return_null() const {
926 : 11000 : return gi_callable_info_may_return_null(ptr());
927 : : }
928 : : [[nodiscard]]
929 : 57 : bool skip_return() const {
930 : 57 : return gi_callable_info_skip_return(ptr());
931 : : }
932 : :
933 : : // Methods not in GIRepository
934 : :
935 : 3287 : void log_usage() {
936 : : #if GJS_VERBOSE_ENABLE_GI_USAGE
937 : : std::ostringstream out;
938 : :
939 : : # define DIRECTION_STRING(d) \
940 : : (((d) == GI_DIRECTION_IN) ? "IN" \
941 : : : ((d) == GI_DIRECTION_OUT) ? "OUT" \
942 : : : "INOUT")
943 : : # define TRANSFER_STRING(t) \
944 : : (((t) == GI_TRANSFER_NOTHING) ? "NOTHING" \
945 : : : ((t) == GI_TRANSFER_CONTAINER) ? "CONTAINER" \
946 : : : "EVERYTHING")
947 : :
948 : : out << ".details = { .func = { .retval_transfer = GI_TRANSFER_"
949 : : << TRANSFER_STRING(caller_owns()) << ", .n_args = " << n_args()
950 : : << ", .args = { ";
951 : :
952 : : ArgsIterator iter = args();
953 : : std::for_each(iter.begin(), iter.end(), [&out](AutoArgInfo arg_info) {
954 : : out << "{ GI_DIRECTION_" << DIRECTION_STRING(arg_info.direction())
955 : : << ", GI_TRANSFER_"
956 : : << TRANSFER_STRING(arg_info.ownership_transfer()) << " }, ";
957 : : });
958 : : out.seekp(-2, std::ios_base::end); // Erase trailing comma
959 : :
960 : : # undef DIRECTION_STRING
961 : : # undef TRANSFER_STRING
962 : :
963 : : out << " } } }";
964 : : std::string details{out.str()};
965 : :
966 : : using Base = BaseInfoOperations<Wrapper>;
967 : : mozilla::Maybe<GI::BaseInfo> parent = Base::container();
968 : : gjs_debug_gi_usage(
969 : : "{ GIInfoType %s, \"%s\", \"%s\", \"%s\", %s }",
970 : : Base::type_string(), Base::ns(),
971 : : parent.map(std::mem_fn(&GI::BaseInfo::name)).valueOr(""),
972 : : Base::name(), details.c_str());
973 : : #endif // GJS_VERBOSE_ENABLE_GI_USAGE
974 : 3287 : }
975 : : };
976 : :
977 : : template <class Wrapper>
978 : : using CallableInfoOperations = InfoOperations<Wrapper, InfoTag::CALLABLE>;
979 : :
980 : : template <class Wrapper>
981 : : class InfoOperations<Wrapper, InfoTag::REGISTERED_TYPE>
982 : : : public BaseInfoOperations<Wrapper> {
983 : : DELETE_SUPERCLASS_TYPECHECK_METHODS;
984 : : DELETE_CALLABLE_TYPECHECK_METHODS;
985 : :
986 : : [[nodiscard]]
987 : 69271 : GIRegisteredTypeInfo* ptr() const {
988 : 69271 : return GI_REGISTERED_TYPE_INFO(
989 : : detail::Pointer::get_from(*static_cast<const Wrapper*>(this)));
990 : : }
991 : :
992 : : public:
993 : : [[nodiscard]]
994 : 67224 : GType gtype() const {
995 : 67224 : return gi_registered_type_info_get_g_type(ptr());
996 : : }
997 : :
998 : : // Methods not in GIRepository
999 : :
1000 : : [[nodiscard]]
1001 : 29987 : bool is_gdk_atom() const {
1002 [ + + ]: 30022 : return strcmp("Atom", this->name()) == 0 &&
1003 [ + - ]: 30022 : strcmp("Gdk", this->ns()) == 0;
1004 : : }
1005 : : [[nodiscard]]
1006 : 9 : bool is_g_value() const {
1007 [ + + - + ]: 9 : return g_type_is_a(gtype(), G_TYPE_VALUE);
1008 : : }
1009 : :
1010 : 2047 : operator const BaseInfo() const {
1011 : 2047 : return detail::Pointer::to_unowned<InfoTag::BASE>(GI_BASE_INFO(ptr()));
1012 : : }
1013 : : };
1014 : :
1015 : : template <class Wrapper>
1016 : : using RegisteredTypeInfoOperations =
1017 : : InfoOperations<Wrapper, InfoTag::REGISTERED_TYPE>;
1018 : :
1019 : : template <class Wrapper>
1020 : : class InfoOperations<Wrapper, InfoTag::CALLBACK>
1021 : : : public CallableInfoOperations<Wrapper> {
1022 : : DELETE_ALL_TYPECHECK_METHODS;
1023 : :
1024 : : [[nodiscard]]
1025 : 277 : GICallbackInfo* ptr() const {
1026 : 277 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1027 : : }
1028 : :
1029 : : public:
1030 : : operator const BaseInfo() const {
1031 : : return detail::Pointer::to_unowned<InfoTag::BASE>(GI_BASE_INFO(ptr()));
1032 : : }
1033 : :
1034 : 277 : operator const CallableInfo() const {
1035 : : return detail::Pointer::to_unowned<InfoTag::CALLABLE>(
1036 : 277 : GI_CALLABLE_INFO(ptr()));
1037 : : }
1038 : : };
1039 : :
1040 : : template <class Wrapper>
1041 : : class InfoOperations<Wrapper, InfoTag::CONSTANT>
1042 : : : public BaseInfoOperations<Wrapper> {
1043 : : DELETE_ALL_TYPECHECK_METHODS;
1044 : :
1045 : : [[nodiscard]]
1046 : 14235 : GIConstantInfo* ptr() const {
1047 : 14235 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1048 : : }
1049 : :
1050 : : public:
1051 : 4745 : void free_value(GIArgument* arg) const {
1052 : 4745 : gi_constant_info_free_value(ptr(), arg);
1053 : 4745 : }
1054 : 4745 : int load_value(GIArgument* arg) const {
1055 : 4745 : return gi_constant_info_get_value(ptr(), arg);
1056 : : }
1057 : : [[nodiscard]]
1058 : 4745 : AutoTypeInfo type_info() const {
1059 : : return detail::Pointer::to_owned<InfoTag::TYPE>(
1060 : 4745 : gi_constant_info_get_type_info(ptr()));
1061 : : }
1062 : : };
1063 : :
1064 : : // Must come before any use of MethodsIterator
1065 : : template <class Wrapper>
1066 : : class InfoOperations<Wrapper, InfoTag::FUNCTION>
1067 : : : public CallableInfoOperations<Wrapper> {
1068 : : DELETE_ALL_TYPECHECK_METHODS;
1069 : :
1070 : : [[nodiscard]]
1071 : 93806 : GIFunctionInfo* ptr() const {
1072 : 93806 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1073 : : }
1074 : :
1075 : : [[nodiscard]]
1076 : 64941 : GIFunctionInfoFlags flags() const {
1077 : 64941 : return gi_function_info_get_flags(ptr());
1078 : : }
1079 : :
1080 : : public:
1081 : : [[nodiscard]]
1082 : 93 : Gjs::GErrorResult<> invoke(const mozilla::Span<const GIArgument>& in_args,
1083 : : const mozilla::Span<GIArgument>& out_args,
1084 : : GIArgument* return_value) const {
1085 : 93 : g_assert(in_args.size() <= INT_MAX);
1086 : 93 : g_assert(out_args.size() <= INT_MAX);
1087 : 93 : GError* error = nullptr;
1088 : : return this->bool_gerror(
1089 : 93 : gi_function_info_invoke(ptr(), in_args.data(), in_args.size(),
1090 : : out_args.data(), out_args.size(),
1091 : : return_value, &error),
1092 : 186 : error);
1093 : : }
1094 : : [[nodiscard]]
1095 : 13886 : Gjs::GErrorResult<> prep_invoker(GIFunctionInvoker* invoker) const {
1096 : 13886 : GError* error = nullptr;
1097 : : return this->bool_gerror(
1098 : 13886 : gi_function_info_prep_invoker(ptr(), invoker, &error), error);
1099 : : }
1100 : : [[nodiscard]]
1101 : 844 : const char* symbol() const {
1102 : 844 : return gi_function_info_get_symbol(ptr());
1103 : : }
1104 : :
1105 : : // Has to be defined later because there's a chicken-and-egg loop between
1106 : : // AutoPropertyInfo and AutoFunctionInfo
1107 : : [[nodiscard]]
1108 : : mozilla::Maybe<GI::AutoPropertyInfo> property() const;
1109 : :
1110 : : // Methods not in GIRepository
1111 : :
1112 : : [[nodiscard]]
1113 : 46161 : bool is_method() const {
1114 : 46161 : return flags() & GI_FUNCTION_IS_METHOD;
1115 : : }
1116 : : [[nodiscard]]
1117 : 18780 : bool is_constructor() const {
1118 : 18780 : return flags() & GI_FUNCTION_IS_CONSTRUCTOR;
1119 : : }
1120 : :
1121 : 13796 : operator const CallableInfo() const {
1122 : : return detail::Pointer::to_unowned<InfoTag::CALLABLE>(
1123 : 13796 : GI_CALLABLE_INFO(ptr()));
1124 : : }
1125 : : };
1126 : :
1127 : : template <class Wrapper>
1128 : : class InfoOperations<Wrapper, InfoTag::ENUM>
1129 : : : public RegisteredTypeInfoOperations<Wrapper> {
1130 : : DELETE_REGISTERED_TYPE_TYPECHECK_METHODS;
1131 : :
1132 : : [[nodiscard]]
1133 : 7292 : GIEnumInfo* ptr() const {
1134 : 7292 : return GI_ENUM_INFO(
1135 : : detail::Pointer::get_from(*static_cast<const Wrapper*>(this)));
1136 : : }
1137 : :
1138 : : public:
1139 : : using ValuesIterator =
1140 : : InfoIterator<GIEnumInfo*, InfoTag::VALUE, gi_enum_info_get_n_values,
1141 : 4392 : gi_enum_info_get_value>;
1142 : : [[nodiscard]]
1143 : 4392 : ValuesIterator values() const {
1144 : 4392 : return ValuesIterator{ptr()};
1145 : : }
1146 : :
1147 : : using MethodsIterator =
1148 : : InfoIterator<GIEnumInfo*, InfoTag::FUNCTION, gi_enum_info_get_n_methods,
1149 : 250 : gi_enum_info_get_method>;
1150 : : [[nodiscard]]
1151 : 250 : MethodsIterator methods() const {
1152 : 250 : return MethodsIterator{ptr()};
1153 : : }
1154 : : [[nodiscard]]
1155 : : mozilla::Maybe<AutoFunctionInfo> method(const char* name) const {
1156 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1157 : : gi_enum_info_find_method(ptr(), name));
1158 : : }
1159 : :
1160 : : [[nodiscard]]
1161 : 157 : const char* error_domain() const {
1162 : 157 : return gi_enum_info_get_error_domain(ptr());
1163 : : }
1164 : : [[nodiscard]]
1165 : 2493 : GITypeTag storage_type() const {
1166 : 2493 : return gi_enum_info_get_storage_type(ptr());
1167 : : }
1168 : :
1169 : : // Methods not in GIRepository
1170 : :
1171 : : [[nodiscard]]
1172 : 2492 : bool uses_signed_type() const {
1173 [ + + ]: 2492 : switch (storage_type()) {
1174 : 104 : case GI_TYPE_TAG_INT8:
1175 : : case GI_TYPE_TAG_INT16:
1176 : : case GI_TYPE_TAG_INT32:
1177 : : case GI_TYPE_TAG_INT64:
1178 : 104 : return true;
1179 : 2388 : default:
1180 : 2388 : return false;
1181 : : }
1182 : : }
1183 : :
1184 : : // This is hacky - gi_function_info_invoke() and
1185 : : // gi_field_info_get/set_field() expect the enum value in
1186 : : // gjs_arg_member<int>(arg) and depend on all flags and enumerations being
1187 : : // passed on the stack in a 32-bit field. See FIXME comment in
1188 : : // gi_field_info_get_field(). The same assumption of enums cast to 32-bit
1189 : : // signed integers is found in g_value_set_enum() / g_value_set_flags().
1190 : : [[nodiscard]]
1191 : 2492 : int64_t enum_from_int(int int_value) const {
1192 [ + + ]: 2492 : if (uses_signed_type())
1193 : 104 : return int64_t{int_value};
1194 : : else
1195 : 2388 : return int64_t{static_cast<uint32_t>(int_value)};
1196 : : }
1197 : :
1198 : : // Here for symmetry, but result is the same for the two cases
1199 : : [[nodiscard]]
1200 : 146 : int enum_to_int(int64_t value) const {
1201 : 146 : return static_cast<int>(value);
1202 : : }
1203 : : };
1204 : :
1205 : : template <class Wrapper>
1206 : : using EnumInfoOperations = InfoOperations<Wrapper, InfoTag::ENUM>;
1207 : :
1208 : : template <class Wrapper>
1209 : : class InfoOperations<Wrapper, InfoTag::FLAGS>
1210 : : : public EnumInfoOperations<Wrapper> {
1211 : : DELETE_ALL_TYPECHECK_METHODS;
1212 : : };
1213 : :
1214 : : template <class Wrapper>
1215 : : class InfoOperations<Wrapper, InfoTag::FIELD>
1216 : : : public BaseInfoOperations<Wrapper> {
1217 : : DELETE_ALL_TYPECHECK_METHODS;
1218 : :
1219 : : [[nodiscard]]
1220 : 13242 : GIFieldInfo* ptr() const {
1221 : 13242 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1222 : : }
1223 : :
1224 : : // Use the various is_FLAG() methods instead.
1225 : : [[nodiscard]]
1226 : 6 : GIFieldInfoFlags flags() const {
1227 : 6 : return gi_field_info_get_flags(ptr());
1228 : : }
1229 : :
1230 : : public:
1231 : 201 : [[nodiscard]] size_t offset() const {
1232 : 201 : return gi_field_info_get_offset(ptr());
1233 : : }
1234 : : [[nodiscard]]
1235 : 2535 : BoolResult read(void* blob, GIArgument* value_out) const {
1236 : 2535 : return this->bool_to_result(
1237 : 5070 : gi_field_info_get_field(ptr(), blob, value_out));
1238 : : }
1239 : : [[nodiscard]]
1240 : 10253 : AutoTypeInfo type_info() const {
1241 : : return detail::Pointer::to_owned<InfoTag::TYPE>(
1242 : 10253 : gi_field_info_get_type_info(ptr()));
1243 : : }
1244 : : [[nodiscard]]
1245 : 247 : BoolResult write(void* blob, const GIArgument* value) const {
1246 : 247 : return this->bool_to_result(
1247 : 494 : gi_field_info_set_field(ptr(), blob, value));
1248 : : }
1249 : :
1250 : : // Methods not in GIRepository
1251 : :
1252 : : [[nodiscard]]
1253 : : bool is_readable() const {
1254 : : return flags() & GI_FIELD_IS_READABLE;
1255 : : }
1256 : : [[nodiscard]]
1257 : 6 : bool is_writable() const {
1258 : 6 : return flags() & GI_FIELD_IS_WRITABLE;
1259 : : }
1260 : : };
1261 : :
1262 : : template <class Wrapper>
1263 : : class InfoOperations<Wrapper, InfoTag::SIGNAL>
1264 : : : public CallableInfoOperations<Wrapper> {
1265 : : DELETE_ALL_TYPECHECK_METHODS;
1266 : :
1267 : : [[nodiscard]]
1268 : : GISignalInfo* ptr() const {
1269 : : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1270 : : }
1271 : : };
1272 : :
1273 : : template <class Wrapper>
1274 : : class InfoOperations<Wrapper, InfoTag::STRUCT>
1275 : : : public RegisteredTypeInfoOperations<Wrapper> {
1276 : : DELETE_ALL_TYPECHECK_METHODS;
1277 : :
1278 : : [[nodiscard]]
1279 : 103045 : GIStructInfo* ptr() const {
1280 : 103045 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1281 : : }
1282 : :
1283 : : public:
1284 : : using FieldsIterator =
1285 : : InfoIterator<GIStructInfo*, InfoTag::FIELD, gi_struct_info_get_n_fields,
1286 : 4580 : gi_struct_info_get_field>;
1287 : : [[nodiscard]]
1288 : 6022 : FieldsIterator fields() const {
1289 : 6022 : return FieldsIterator{ptr()};
1290 : : }
1291 : :
1292 : : using MethodsIterator =
1293 : : InfoIterator<GIStructInfo*, InfoTag::FUNCTION,
1294 : 2879 : gi_struct_info_get_n_methods, gi_struct_info_get_method>;
1295 : : [[nodiscard]]
1296 : 2966 : MethodsIterator methods() const {
1297 : 2966 : return MethodsIterator{ptr()};
1298 : : }
1299 : : [[nodiscard]]
1300 : 6836 : mozilla::Maybe<AutoFunctionInfo> method(const char* name) const {
1301 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1302 : 6836 : gi_struct_info_find_method(ptr(), name));
1303 : : }
1304 : :
1305 : : [[nodiscard]]
1306 : 42594 : bool is_foreign() const {
1307 : 42594 : return gi_struct_info_is_foreign(ptr());
1308 : : }
1309 : : [[nodiscard]]
1310 : 23822 : bool is_gtype_struct() const {
1311 : 23822 : return gi_struct_info_is_gtype_struct(ptr());
1312 : : }
1313 : 519 : [[nodiscard]] size_t size() const { return gi_struct_info_get_size(ptr()); }
1314 : :
1315 : 20286 : operator const BaseInfo() const {
1316 : 20286 : return detail::Pointer::to_unowned<InfoTag::BASE>(GI_BASE_INFO(ptr()));
1317 : : }
1318 : : };
1319 : :
1320 : : template <class Wrapper>
1321 : : class InfoOperations<Wrapper, InfoTag::UNION>
1322 : : : public RegisteredTypeInfoOperations<Wrapper> {
1323 : : DELETE_ALL_TYPECHECK_METHODS;
1324 : :
1325 : : [[nodiscard]]
1326 : 532 : GIUnionInfo* ptr() const {
1327 : 532 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1328 : : }
1329 : :
1330 : : public:
1331 : : using FieldsIterator =
1332 : : InfoIterator<GIUnionInfo*, InfoTag::FIELD, gi_union_info_get_n_fields,
1333 : 114 : gi_union_info_get_field>;
1334 : : [[nodiscard]]
1335 : 205 : FieldsIterator fields() const {
1336 : 205 : return FieldsIterator{ptr()};
1337 : : }
1338 : :
1339 : : using MethodsIterator =
1340 : : InfoIterator<GIUnionInfo*, InfoTag::FUNCTION,
1341 : 14 : gi_union_info_get_n_methods, gi_union_info_get_method>;
1342 : : [[nodiscard]]
1343 : 14 : MethodsIterator methods() const {
1344 : 14 : return MethodsIterator{ptr()};
1345 : : }
1346 : : [[nodiscard]]
1347 : 271 : mozilla::Maybe<AutoFunctionInfo> method(const char* name) const {
1348 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1349 : 271 : gi_union_info_find_method(ptr(), name));
1350 : : }
1351 : :
1352 : 42 : [[nodiscard]] size_t size() const { return gi_union_info_get_size(ptr()); }
1353 : : };
1354 : :
1355 : : template <class Wrapper>
1356 : : class InfoOperations<Wrapper, InfoTag::VFUNC>
1357 : : : public CallableInfoOperations<Wrapper> {
1358 : : DELETE_ALL_TYPECHECK_METHODS;
1359 : :
1360 : : [[nodiscard]]
1361 : 75 : GIVFuncInfo* ptr() const {
1362 : 75 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1363 : : }
1364 : :
1365 : : public:
1366 : : [[nodiscard]]
1367 : 6 : Gjs::GErrorResult<void*> address(GType implementor_gtype) const {
1368 : 6 : Gjs::AutoError error; // Cannot use GError*, distinguish from void*
1369 : 6 : void* address =
1370 : 6 : gi_vfunc_info_get_address(ptr(), implementor_gtype, error.out());
1371 [ + + ]: 6 : if (!address)
1372 : 1 : return mozilla::Err(std::move(error));
1373 : 5 : return address;
1374 : 6 : }
1375 : :
1376 : 69 : [[nodiscard]] operator const CallableInfo() const {
1377 : : return detail::Pointer::to_unowned<InfoTag::CALLABLE>(
1378 : 69 : GI_CALLABLE_INFO(ptr()));
1379 : : }
1380 : : };
1381 : :
1382 : : template <class Wrapper>
1383 : : class InfoOperations<Wrapper, InfoTag::INTERFACE>
1384 : : : public RegisteredTypeInfoOperations<Wrapper> {
1385 : : DELETE_ALL_TYPECHECK_METHODS;
1386 : :
1387 : : [[nodiscard]]
1388 : 5883 : GIInterfaceInfo* ptr() const {
1389 : 5883 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1390 : : }
1391 : :
1392 : : public:
1393 : : using MethodsIterator = InfoIterator<GIInterfaceInfo*, InfoTag::FUNCTION,
1394 : : gi_interface_info_get_n_methods,
1395 : 311 : gi_interface_info_get_method>;
1396 : : [[nodiscard]]
1397 : 283 : MethodsIterator methods() const {
1398 : 283 : return MethodsIterator{ptr()};
1399 : : }
1400 : : [[nodiscard]]
1401 : 3777 : mozilla::Maybe<AutoFunctionInfo> method(const char* name) const {
1402 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1403 : 3777 : gi_interface_info_find_method(ptr(), name));
1404 : : }
1405 : :
1406 : : using PropertiesIterator = InfoIterator<GIInterfaceInfo*, InfoTag::PROPERTY,
1407 : : gi_interface_info_get_n_properties,
1408 : 1534 : gi_interface_info_get_property>;
1409 : : [[nodiscard]]
1410 : 1534 : PropertiesIterator properties() const {
1411 : 1534 : return PropertiesIterator{ptr()};
1412 : : }
1413 : :
1414 : : [[nodiscard]]
1415 : 268 : mozilla::Maybe<AutoStructInfo> iface_struct() const {
1416 : : return detail::Pointer::nullable<InfoTag::STRUCT>(
1417 : 268 : gi_interface_info_get_iface_struct(ptr()));
1418 : : }
1419 : : [[nodiscard]]
1420 : 2 : mozilla::Maybe<AutoSignalInfo> signal(const char* name) const {
1421 : : return detail::Pointer::nullable<InfoTag::SIGNAL>(
1422 : 2 : gi_interface_info_find_signal(ptr(), name));
1423 : : }
1424 : : [[nodiscard]]
1425 : 19 : mozilla::Maybe<AutoVFuncInfo> vfunc(const char* name) const {
1426 : : return detail::Pointer::nullable<InfoTag::VFUNC>(
1427 : 19 : gi_interface_info_find_vfunc(ptr(), name));
1428 : : }
1429 : : };
1430 : :
1431 : : template <class Wrapper>
1432 : : class InfoOperations<Wrapper, InfoTag::OBJECT>
1433 : : : public RegisteredTypeInfoOperations<Wrapper> {
1434 : : DELETE_ALL_TYPECHECK_METHODS;
1435 : :
1436 : : [[nodiscard]]
1437 : 21561 : GIObjectInfo* ptr() const {
1438 : 21561 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1439 : : }
1440 : :
1441 : : public:
1442 : : using FieldsIterator =
1443 : : InfoIterator<GIObjectInfo*, InfoTag::FIELD, gi_object_info_get_n_fields,
1444 : 4274 : gi_object_info_get_field>;
1445 : : [[nodiscard]]
1446 : 4274 : FieldsIterator fields() const {
1447 : 4274 : return FieldsIterator{ptr()};
1448 : : }
1449 : :
1450 : : using InterfacesIterator = InfoIterator<GIObjectInfo*, InfoTag::INTERFACE,
1451 : : gi_object_info_get_n_interfaces,
1452 : 3600 : gi_object_info_get_interface>;
1453 : : [[nodiscard]]
1454 : 3600 : InterfacesIterator interfaces() const {
1455 : 3600 : return InterfacesIterator{ptr()};
1456 : : }
1457 : :
1458 : : using MethodsIterator =
1459 : : InfoIterator<GIObjectInfo*, InfoTag::FUNCTION,
1460 : 899 : gi_object_info_get_n_methods, gi_object_info_get_method>;
1461 : : [[nodiscard]]
1462 : 853 : MethodsIterator methods() const {
1463 : 853 : return MethodsIterator{ptr()};
1464 : : }
1465 : : [[nodiscard]]
1466 : 584 : mozilla::Maybe<AutoFunctionInfo> method(const char* name) const {
1467 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1468 : 584 : gi_object_info_find_method(ptr(), name));
1469 : : }
1470 : :
1471 : : using PropertiesIterator = InfoIterator<GIObjectInfo*, InfoTag::PROPERTY,
1472 : : gi_object_info_get_n_properties,
1473 : 4112 : gi_object_info_get_property>;
1474 : : [[nodiscard]]
1475 : 4066 : PropertiesIterator properties() const {
1476 : 4066 : return PropertiesIterator{ptr()};
1477 : : }
1478 : :
1479 : : [[nodiscard]]
1480 : 851 : mozilla::Maybe<AutoStructInfo> class_struct() const {
1481 : : return detail::Pointer::nullable<InfoTag::STRUCT>(
1482 : 851 : gi_object_info_get_class_struct(ptr()));
1483 : : }
1484 : : [[nodiscard]]
1485 : : mozilla::Maybe<std::pair<AutoFunctionInfo, AutoRegisteredTypeInfo>>
1486 : 4259 : find_method_using_interfaces(const char* name) const {
1487 : 4259 : GIBaseInfo* declarer_ptr = nullptr;
1488 : : GIFunctionInfo* method_ptr =
1489 : 4259 : gi_object_info_find_method_using_interfaces(ptr(), name,
1490 : : &declarer_ptr);
1491 : :
1492 [ + + ]: 4259 : if (!method_ptr) {
1493 : 1975 : g_assert(!declarer_ptr);
1494 : 1975 : return {};
1495 : : }
1496 : :
1497 : 2284 : AutoFunctionInfo method{
1498 : : detail::Pointer::to_owned<InfoTag::FUNCTION>(method_ptr)};
1499 : 2284 : AutoRegisteredTypeInfo declarer{
1500 : : detail::Pointer::to_owned<InfoTag::REGISTERED_TYPE>(
1501 : 2284 : GI_REGISTERED_TYPE_INFO(declarer_ptr))};
1502 : 2284 : g_assert(declarer.is_object() || declarer.is_interface());
1503 : 2284 : return mozilla::Some(std::make_pair(method, declarer));
1504 : 2284 : }
1505 : : [[nodiscard]]
1506 : : mozilla::Maybe<std::pair<AutoVFuncInfo, AutoRegisteredTypeInfo>>
1507 : 20 : find_vfunc_using_interfaces(const char* name) const {
1508 : 20 : GIBaseInfo* declarer_ptr = nullptr;
1509 : 20 : GIVFuncInfo* vfunc_ptr = gi_object_info_find_vfunc_using_interfaces(
1510 : : ptr(), name, &declarer_ptr);
1511 : :
1512 [ + + ]: 20 : if (!vfunc_ptr) {
1513 : 19 : g_assert(!declarer_ptr);
1514 : 19 : return {};
1515 : : }
1516 : :
1517 : 1 : AutoVFuncInfo vfunc{
1518 : : detail::Pointer::to_owned<InfoTag::VFUNC>(vfunc_ptr)};
1519 : 1 : AutoRegisteredTypeInfo declarer{
1520 : : detail::Pointer::to_owned<InfoTag::REGISTERED_TYPE>(
1521 : 1 : GI_REGISTERED_TYPE_INFO(declarer_ptr))};
1522 : 1 : g_assert(declarer.is_object() || declarer.is_interface());
1523 : 1 : return mozilla::Some(std::make_pair(vfunc, declarer));
1524 : 1 : }
1525 : : [[nodiscard]]
1526 : 10 : GIObjectInfoGetValueFunction get_value_function_pointer() const {
1527 : 10 : return gi_object_info_get_get_value_function_pointer(ptr());
1528 : : }
1529 : : [[nodiscard]]
1530 : 36 : mozilla::Maybe<AutoObjectInfo> parent() const {
1531 : : return detail::Pointer::nullable<InfoTag::OBJECT>(
1532 : 36 : gi_object_info_get_parent(ptr()));
1533 : : }
1534 : : [[nodiscard]]
1535 : 10 : GIObjectInfoRefFunction ref_function_pointer() const {
1536 : 10 : return gi_object_info_get_ref_function_pointer(ptr());
1537 : : }
1538 : : [[nodiscard]]
1539 : 10 : GIObjectInfoSetValueFunction set_value_function_pointer() const {
1540 : 10 : return gi_object_info_get_set_value_function_pointer(ptr());
1541 : : }
1542 : : [[nodiscard]]
1543 : 331 : mozilla::Maybe<AutoSignalInfo> signal(const char* name) const {
1544 : : return detail::Pointer::nullable<InfoTag::SIGNAL>(
1545 : 331 : gi_object_info_find_signal(ptr(), name));
1546 : : }
1547 : : [[nodiscard]]
1548 : 10 : GIObjectInfoUnrefFunction unref_function_pointer() const {
1549 : 10 : return gi_object_info_get_unref_function_pointer(ptr());
1550 : : }
1551 : : [[nodiscard]]
1552 : 104 : mozilla::Maybe<AutoVFuncInfo> vfunc(const char* name) const {
1553 : : return detail::Pointer::nullable<InfoTag::VFUNC>(
1554 : 104 : gi_object_info_find_vfunc(ptr(), name));
1555 : : }
1556 : :
1557 : 2543 : [[nodiscard]] operator const BaseInfo() const {
1558 : 2543 : return detail::Pointer::to_unowned<InfoTag::BASE>(GI_BASE_INFO(ptr()));
1559 : : }
1560 : : };
1561 : :
1562 : : template <class Wrapper>
1563 : : class InfoOperations<Wrapper, InfoTag::PROPERTY>
1564 : : : public BaseInfoOperations<Wrapper> {
1565 : : DELETE_ALL_TYPECHECK_METHODS;
1566 : :
1567 : : [[nodiscard]]
1568 : 642 : GIPropertyInfo* ptr() const {
1569 : 642 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1570 : : }
1571 : :
1572 : : [[nodiscard]]
1573 : 123 : GParamFlags flags() const {
1574 : 123 : return gi_property_info_get_flags(ptr());
1575 : : }
1576 : :
1577 : : public:
1578 : : [[nodiscard]]
1579 : 239 : mozilla::Maybe<AutoFunctionInfo> getter() const {
1580 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1581 : 239 : gi_property_info_get_getter(ptr()));
1582 : : }
1583 : : [[nodiscard]]
1584 : 190 : mozilla::Maybe<AutoFunctionInfo> setter() const {
1585 : : return detail::Pointer::nullable<InfoTag::FUNCTION>(
1586 : 190 : gi_property_info_get_setter(ptr()));
1587 : : }
1588 : : [[nodiscard]]
1589 : 90 : AutoTypeInfo type_info() const {
1590 : : return detail::Pointer::to_owned<InfoTag::TYPE>(
1591 : 90 : gi_property_info_get_type_info(ptr()));
1592 : : }
1593 : :
1594 : : // Methods not in GIRepository
1595 : :
1596 : : [[nodiscard]]
1597 : 123 : bool has_deprecated_param_flag() const {
1598 : : // Note, different from is_deprecated(). It's possible that the property
1599 : : // has the deprecated GParamSpec flag, but is not marked deprecated in
1600 : : // the GIR doc comment.
1601 : 123 : return flags() & G_PARAM_DEPRECATED;
1602 : : }
1603 : : };
1604 : :
1605 : : // Out-of-line definition to avoid chicken-and-egg loop between AutoFunctionInfo
1606 : : // and AutoPropertyInfo
1607 : : template <class Wrapper>
1608 : : inline mozilla::Maybe<AutoPropertyInfo>
1609 : 246 : InfoOperations<Wrapper, InfoTag::FUNCTION>::property() const {
1610 : : return detail::Pointer::nullable<InfoTag::PROPERTY>(
1611 : 246 : gi_function_info_get_property(ptr()));
1612 : : }
1613 : :
1614 : : template <class Wrapper>
1615 : : class InfoOperations<Wrapper, InfoTag::VALUE>
1616 : : : public BaseInfoOperations<Wrapper> {
1617 : : DELETE_ALL_TYPECHECK_METHODS;
1618 : :
1619 : : [[nodiscard]]
1620 : 51517 : GIValueInfo* ptr() const {
1621 : 51517 : return detail::Pointer::get_from(*static_cast<const Wrapper*>(this));
1622 : : }
1623 : :
1624 : : public:
1625 : : [[nodiscard]]
1626 : 51517 : int64_t value() const {
1627 : 51517 : return gi_value_info_get_value(ptr());
1628 : : }
1629 : : };
1630 : :
1631 : : // In order to avoid having to create an OwnedInfo or UnownedInfo from a pointer
1632 : : // anywhere except in these wrappers, we also wrap GIRepository.
1633 : : // (ArgCache::HasTypeInfo is the one exception.)
1634 : : class Repository {
1635 : : Gjs::AutoUnref<GIRepository> m_ptr = gi_repository_dup_default();
1636 : :
1637 : : // Helper object for iterating the introspection info objects of a
1638 : : // namespace. Unlike the other introspection info iterators, this requires
1639 : : // two parameters, the GIRepository* and the namespace string, so we need
1640 : : // this helper object to adapt InfoIterator.
1641 : : struct IterableNamespace {
1642 : : GIRepository* repo;
1643 : : const char* ns;
1644 : :
1645 : 54 : static unsigned get_n_infos(const IterableNamespace obj) {
1646 : 54 : return gi_repository_get_n_infos(obj.repo, obj.ns);
1647 : : }
1648 : :
1649 : 11251 : static GIBaseInfo* get_info(const IterableNamespace obj, unsigned ix) {
1650 : 11251 : return gi_repository_get_info(obj.repo, obj.ns, ix);
1651 : : }
1652 : :
1653 : 11278 : bool operator==(const IterableNamespace& other) const {
1654 [ + - + - ]: 11278 : return repo == other.repo && strcmp(ns, other.ns) == 0;
1655 : : }
1656 : :
1657 : 11278 : bool operator!=(const IterableNamespace& other) const {
1658 : 11278 : return !(*this == other);
1659 : : }
1660 : : };
1661 : :
1662 : : public:
1663 : : using Iterator = InfoIterator<IterableNamespace, InfoTag::BASE,
1664 : 54 : &IterableNamespace::get_n_infos,
1665 : : &IterableNamespace::get_info>;
1666 : : [[nodiscard]]
1667 : 27 : Iterator infos(const char* ns) const {
1668 : 27 : return Iterator{{m_ptr, ns}};
1669 : : }
1670 : :
1671 : : [[nodiscard]]
1672 : 290 : Gjs::AutoStrv enumerate_versions(const char* ns, size_t* n_versions) const {
1673 : 290 : return gi_repository_enumerate_versions(m_ptr, ns, n_versions);
1674 : : }
1675 : : [[nodiscard]]
1676 : 71 : mozilla::Maybe<AutoEnumInfo> find_by_error_domain(GQuark domain) const {
1677 : : return detail::Pointer::nullable<InfoTag::ENUM>(
1678 : 71 : gi_repository_find_by_error_domain(m_ptr, domain));
1679 : : }
1680 : : template <InfoTag TAG = InfoTag::REGISTERED_TYPE>
1681 : : [[nodiscard]]
1682 : 3762 : mozilla::Maybe<OwnedInfo<TAG>> find_by_gtype(GType gtype) const {
1683 : : return detail::Pointer::nullable<TAG>(detail::Pointer::cast<TAG>(
1684 : 3762 : gi_repository_find_by_gtype(m_ptr, gtype)));
1685 : : }
1686 : : template <InfoTag TAG = InfoTag::BASE>
1687 : : [[nodiscard]]
1688 : 17539 : mozilla::Maybe<OwnedInfo<TAG>> find_by_name(const char* ns,
1689 : : const char* name) const {
1690 : : return detail::Pointer::nullable<TAG>(detail::Pointer::cast<TAG>(
1691 : 17539 : gi_repository_find_by_name(m_ptr, ns, name)));
1692 : : }
1693 : : [[nodiscard]]
1694 : 15 : const char* get_version(const char* ns) const {
1695 : 15 : return gi_repository_get_version(m_ptr, ns);
1696 : : }
1697 : : [[nodiscard]]
1698 : 0 : bool is_registered(const char* ns, const char* version) const {
1699 : 0 : return gi_repository_is_registered(m_ptr, ns, version);
1700 : : }
1701 : : [[nodiscard]]
1702 : 2345 : mozilla::Span<const InterfaceInfo> object_get_gtype_interfaces(
1703 : : GType gtype) const {
1704 : : InterfaceInfo* interfaces;
1705 : : size_t n_interfaces;
1706 : 2345 : gi_repository_get_object_gtype_interfaces(
1707 : : m_ptr, gtype, &n_interfaces,
1708 : : reinterpret_cast<GIInterfaceInfo***>(&interfaces));
1709 : 2345 : return {interfaces, n_interfaces};
1710 : : }
1711 : 0 : void prepend_search_path(const char* path) {
1712 : 0 : gi_repository_prepend_search_path(m_ptr, path);
1713 : 0 : }
1714 : : [[nodiscard]]
1715 : 365 : Gjs::GErrorResult<GITypelib*> require(
1716 : : const char* ns, const char* version,
1717 : : GIRepositoryLoadFlags flags = {}) const {
1718 : 365 : GError* error = nullptr;
1719 : : GITypelib* typelib =
1720 : 365 : gi_repository_require(m_ptr, ns, version, flags, &error);
1721 [ + + ]: 365 : if (!typelib)
1722 : 3 : return mozilla::Err(error);
1723 : 362 : return typelib;
1724 : : }
1725 : : };
1726 : :
1727 : : ///// STACK-ALLOCATED INTROSPECTION INFO ///////////////////////////////////////
1728 : :
1729 : : // Introspection info allocated directly on the stack. This is used only in a
1730 : : // few cases, for performance reasons. In C, the stack-allocated struct is
1731 : : // filled in by a function such as gi_arg_info_load_type_info().
1732 : : // Needs to appear at the end, due to FIXME.
1733 : :
1734 : : class StackArgInfo : public InfoOperations<StackArgInfo, InfoTag::ARG> {
1735 : : friend struct detail::Pointer;
1736 : :
1737 : : GIArgInfo m_info = {};
1738 : :
1739 : : [[nodiscard]]
1740 : : constexpr GIArgInfo* ptr() const {
1741 : : return detail::Pointer::get_from(*this);
1742 : : }
1743 : :
1744 : : public:
1745 : 429 : constexpr StackArgInfo() {}
1746 : 29345 : ~StackArgInfo() { gi_base_info_clear(&m_info); }
1747 : : // Moving is okay, we copy the contents of the GIArgInfo struct and reset
1748 : : // the existing one
1749 : 1 : StackArgInfo(StackArgInfo&& other) : m_info(other.m_info) {
1750 : 1 : gi_base_info_clear(&other.m_info);
1751 : 1 : }
1752 : : StackArgInfo& operator=(StackArgInfo&& other) {
1753 : : m_info = other.m_info;
1754 : : gi_base_info_clear(&other.m_info);
1755 : : return *this;
1756 : : }
1757 : : // Prefer moving to copying
1758 : : StackArgInfo(const StackArgInfo&) = delete;
1759 : : StackArgInfo& operator=(const StackArgInfo&) = delete;
1760 : : };
1761 : :
1762 : : class StackTypeInfo : public InfoOperations<StackTypeInfo, InfoTag::TYPE> {
1763 : : friend struct detail::Pointer;
1764 : :
1765 : : GITypeInfo m_info = {};
1766 : :
1767 : : [[nodiscard]]
1768 : : constexpr GITypeInfo* ptr() const {
1769 : : return detail::Pointer::get_from(*this);
1770 : : }
1771 : :
1772 : : public:
1773 : 7054 : constexpr StackTypeInfo() {}
1774 : 59452 : ~StackTypeInfo() { gi_base_info_clear(&m_info); }
1775 : : // Moving is okay, we copy the contents of the GITypeInfo struct and reset
1776 : : // the existing one
1777 : : StackTypeInfo(StackTypeInfo&& other) : m_info(other.m_info) {
1778 : : gi_base_info_clear(&other.m_info);
1779 : : }
1780 : : StackTypeInfo& operator=(StackTypeInfo&& other) {
1781 : : m_info = other.m_info;
1782 : : gi_base_info_clear(&other.m_info);
1783 : : return *this;
1784 : : }
1785 : : // Prefer moving to copying
1786 : : StackTypeInfo(const StackTypeInfo&) = delete;
1787 : : StackTypeInfo& operator=(const StackTypeInfo&) = delete;
1788 : : };
1789 : :
1790 : : namespace detail {
1791 : 86887 : constexpr inline GIArgInfo* Pointer::get_from(const StackArgInfo& stack) {
1792 : 86887 : return const_cast<GIArgInfo*>(&stack.m_info);
1793 : : }
1794 : 237302 : constexpr inline GITypeInfo* Pointer::get_from(const StackTypeInfo& stack) {
1795 : 237302 : return const_cast<GITypeInfo*>(&stack.m_info);
1796 : : }
1797 : 6625 : inline void Pointer::to_stack(GITypeInfo* ptr, StackTypeInfo* stack) {
1798 : 6625 : stack->m_info = std::move(*ptr);
1799 : : // Hacky: Reproduce gi_info_init() and mark the copied GITypeInfo as
1800 : : // stack-allocated. Unfortunately, GI_TYPE_TYPE_INFO makes this function
1801 : : // unable to be constexpr.
1802 : 6625 : GIBaseInfoStack* stack_ptr = &stack->m_info.parent;
1803 : 6625 : stack_ptr->parent_instance.g_class =
1804 : 6625 : static_cast<GTypeClass*>(g_type_class_ref(GI_TYPE_TYPE_INFO));
1805 : 6625 : stack_ptr->dummy0 = 0x7fff'ffff;
1806 : 6625 : }
1807 : : } // namespace detail
1808 : :
1809 : : static_assert(sizeof(StackArgInfo) == sizeof(GIArgInfo),
1810 : : "StackArgInfo should be byte-compatible with GIArgInfo");
1811 : : static_assert(sizeof(StackTypeInfo) == sizeof(GITypeInfo),
1812 : : "StackTypeInfo should be byte-compatible with GITypeInfo");
1813 : :
1814 : : } // namespace GI
1815 : :
1816 : : // For use of GI::OwnedInfo<TAG> in GC hash maps
1817 : : namespace JS {
1818 : : template <GI::InfoTag TAG>
1819 : : struct GCPolicy<GI::OwnedInfo<TAG>>
1820 : : : public IgnoreGCPolicy<GI::OwnedInfo<TAG>> {};
1821 : : } // namespace JS
|