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: 2013 Giovanni Campagna <scampa.giovanni@gmail.com>
4 : : // SPDX-FileCopyrightText: 2020 Marco Trevisan <marco.trevisan@canonical.com>
5 : :
6 : : #pragma once
7 : :
8 : : #include <config.h>
9 : :
10 : : #include <stdint.h>
11 : :
12 : : #include <limits>
13 : :
14 : : #include <girepository/girepository.h>
15 : : #include <glib-object.h>
16 : :
17 : : #include <js/TypeDecls.h>
18 : : #include <mozilla/Maybe.h>
19 : :
20 : : #include "gi/arg.h"
21 : : #include "gi/info.h"
22 : : #include "gjs/auto.h"
23 : : #include "gjs/enum-utils.h"
24 : : #include "gjs/macros.h"
25 : :
26 : : class GjsFunctionCallState;
27 : :
28 : : enum NotIntrospectableReason : uint8_t {
29 : : CALLBACK_OUT,
30 : : DESTROY_NOTIFY_NO_CALLBACK,
31 : : DESTROY_NOTIFY_NO_USER_DATA,
32 : : INTERFACE_TRANSFER_CONTAINER,
33 : : OUT_CALLER_ALLOCATES_NON_STRUCT,
34 : : UNREGISTERED_BOXED_WITH_TRANSFER,
35 : : UNREGISTERED_UNION,
36 : : UNSUPPORTED_TYPE,
37 : : LAST_REASON
38 : : };
39 : :
40 : : namespace Gjs {
41 : : namespace Arg {
42 : :
43 : : struct Instance;
44 : :
45 : : enum class Kind {
46 : : NORMAL,
47 : : INSTANCE,
48 : : RETURN_VALUE,
49 : : };
50 : :
51 : : class ReturnTag {
52 : : GITypeTag m_tag : 5;
53 : : bool m_is_enum_or_flags_interface : 1;
54 : : bool m_is_pointer : 1;
55 : :
56 : : public:
57 : 18771 : constexpr explicit ReturnTag(GITypeTag tag)
58 : 18771 : : m_tag(tag),
59 : 18771 : m_is_enum_or_flags_interface(false),
60 : 18771 : m_is_pointer(false) {}
61 : : constexpr explicit ReturnTag(GITypeTag tag, bool is_enum_or_flags_interface,
62 : : bool is_pointer)
63 : : : m_tag(tag),
64 : : m_is_enum_or_flags_interface(is_enum_or_flags_interface),
65 : : m_is_pointer(is_pointer) {}
66 : 22766 : explicit ReturnTag(const GI::TypeInfo type_info)
67 : 22766 : : m_tag(type_info.tag()),
68 : 22766 : m_is_pointer(type_info.is_pointer()) {
69 [ + + ]: 22766 : if (m_tag == GI_TYPE_TAG_INTERFACE) {
70 : 22639 : GI::AutoBaseInfo interface_info{type_info.interface()};
71 : 22639 : m_is_enum_or_flags_interface = interface_info.is_enum_or_flags();
72 : 22639 : }
73 : 22766 : }
74 : :
75 : 62495 : constexpr GITypeTag tag() const { return m_tag; }
76 : : [[nodiscard]]
77 : 43724 : constexpr bool is_enum_or_flags_interface() const {
78 [ + + + + ]: 43724 : return m_tag == GI_TYPE_TAG_INTERFACE && m_is_enum_or_flags_interface;
79 : : }
80 : : [[nodiscard]]
81 : 41537 : constexpr GType interface_gtype() const {
82 [ + + ]: 41537 : return is_enum_or_flags_interface() ? GI_TYPE_ENUM_INFO : G_TYPE_NONE;
83 : : }
84 : 41537 : constexpr bool is_pointer() const { return m_is_pointer; }
85 : : };
86 : :
87 : : } // namespace Arg
88 : :
89 : : // When creating an Argument, pass it directly to ArgsCache::set_argument() or
90 : : // one of the similar methods, which will call init_common() on it and store it
91 : : // in the appropriate place in the arguments cache.
92 : : struct Argument {
93 : : // Convenience struct to prevent long argument lists to make() and the
94 : : // functions that call it
95 : : struct Init {
96 : : const char* name;
97 : : uint8_t index;
98 : : GITransfer transfer : 2;
99 : : GjsArgumentFlags flags : 6;
100 : : };
101 : :
102 : 40122 : virtual ~Argument() = default;
103 : :
104 : : GJS_JSAPI_RETURN_CONVENTION
105 : : virtual bool in(JSContext*, GjsFunctionCallState*, GIArgument* in_argument,
106 : : JS::HandleValue);
107 : :
108 : : GJS_JSAPI_RETURN_CONVENTION
109 : : virtual bool out(JSContext*, GjsFunctionCallState*,
110 : : GIArgument* out_argument, JS::MutableHandleValue);
111 : :
112 : : GJS_JSAPI_RETURN_CONVENTION
113 : : virtual bool release(JSContext*, GjsFunctionCallState*,
114 : : GIArgument* in_argument, GIArgument* out_argument);
115 : :
116 : 2260 : virtual GjsArgumentFlags flags() const {
117 : 2260 : GjsArgumentFlags flags = GjsArgumentFlags::NONE;
118 [ + + ]: 2260 : if (m_skip_in)
119 : 1565 : flags |= GjsArgumentFlags::SKIP_IN;
120 : : else
121 : 695 : flags |= GjsArgumentFlags::ARG_IN;
122 [ + + ]: 2260 : if (m_skip_out)
123 : 633 : flags |= GjsArgumentFlags::SKIP_OUT;
124 : : else
125 : 1627 : flags |= GjsArgumentFlags::ARG_OUT;
126 : :
127 : 2260 : return flags;
128 : : }
129 : :
130 : : // Introspected functions can have up to 253 arguments. The callback
131 : : // closure or destroy notify parameter may have a value of 255 to indicate
132 : : // that it is absent.
133 : : static constexpr uint8_t MAX_ARGS = std::numeric_limits<uint8_t>::max() - 2;
134 : : static constexpr uint8_t ABSENT = std::numeric_limits<uint8_t>::max();
135 : :
136 : 80 : constexpr const char* arg_name() const { return m_arg_name; }
137 : :
138 : 88104 : constexpr bool skip_in() const { return m_skip_in; }
139 : :
140 : 127500 : constexpr bool skip_out() const { return m_skip_out; }
141 : :
142 : : protected:
143 : 40692 : constexpr Argument() : m_skip_in(false), m_skip_out(false) {}
144 : :
145 : 0 : virtual mozilla::Maybe<Arg::ReturnTag> return_tag() const { return {}; }
146 : 0 : virtual mozilla::Maybe<const Arg::Instance*> as_instance() const {
147 : 0 : return {};
148 : : }
149 : :
150 : 4000 : constexpr void set_instance_parameter() {
151 : 4000 : m_arg_name = "instance parameter";
152 : 4000 : m_skip_out = true;
153 : 4000 : }
154 : :
155 : 11000 : constexpr void set_return_value() { m_arg_name = "return value"; }
156 : :
157 : : bool invalid(JSContext*, const char* func = nullptr) const;
158 : :
159 : : const char* m_arg_name = nullptr;
160 : : bool m_skip_in : 1;
161 : : bool m_skip_out : 1;
162 : :
163 : : private:
164 : : friend struct ArgsCache;
165 : :
166 : : template <typename T, Arg::Kind ArgKind>
167 : : static void init_common(const Init&, T* arg);
168 : : };
169 : :
170 : : using ArgumentPtr = AutoCppPointer<Argument>;
171 : :
172 : : // This is a trick to print out the sizes of the structs at compile time, in
173 : : // an error message:
174 : : // template <int s> struct Measure;
175 : : // Measure<sizeof(Argument)> arg_cache_size;
176 : :
177 : : #if defined(__x86_64__) && defined(__clang__) && !defined(_MSC_VER)
178 : : # define GJS_DO_ARGUMENTS_SIZE_CHECK 1
179 : : // This isn't meant to be comprehensive, but should trip on at least one CI job
180 : : // if sizeof(Gjs::Argument) is increased.
181 : : // Note that this check is not applicable for clang-cl builds, as Windows is
182 : : // an LLP64 system
183 : : static_assert(sizeof(Argument) <= 24,
184 : : "Think very hard before increasing the size of Gjs::Argument. "
185 : : "One is allocated for every argument to every introspected "
186 : : "function.");
187 : : #endif // x86-64 clang
188 : :
189 : : struct ArgsCache {
190 : : GJS_JSAPI_RETURN_CONVENTION
191 : : bool initialize(JSContext*, const GI::CallableInfo);
192 : :
193 : : // COMPAT: in C++20, use default initializers for these bitfields
194 : 13798 : ArgsCache() : m_is_method(false), m_has_return(false) {}
195 : :
196 : : constexpr bool initialized() { return m_args != nullptr; }
197 : : constexpr void clear() { m_args.reset(); }
198 : :
199 : : void build_arg(uint8_t gi_index, GIDirection, const GI::ArgInfo,
200 : : const GI::CallableInfo, bool* inc_counter_out);
201 : :
202 : : void build_return(const GI::CallableInfo, bool* inc_counter_out);
203 : :
204 : : void build_instance(const GI::CallableInfo);
205 : :
206 : : mozilla::Maybe<GType> instance_type() const;
207 : : mozilla::Maybe<Arg::ReturnTag> return_tag() const;
208 : :
209 : : private:
210 : : void build_normal_in_arg(uint8_t gi_index, const GI::TypeInfo,
211 : : const GI::ArgInfo, GjsArgumentFlags);
212 : : void build_normal_out_arg(uint8_t gi_index, const GI::TypeInfo,
213 : : const GI::ArgInfo, GjsArgumentFlags);
214 : : void build_normal_inout_arg(uint8_t gi_index, const GI::TypeInfo,
215 : : const GI::ArgInfo, GjsArgumentFlags);
216 : :
217 : : // GITypeInfo is not available for instance parameters (see
218 : : // https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/334) but
219 : : // for other parameters, this function additionally takes a GITypeInfo.
220 : : template <Arg::Kind ArgKind = Arg::Kind::NORMAL>
221 : : void build_interface_in_arg(const Argument::Init&,
222 : : const GI::BaseInfo interface_info);
223 : :
224 : : template <Arg::Kind ArgKind = Arg::Kind::NORMAL, typename T>
225 : : constexpr void set_argument(T* arg, const Argument::Init&);
226 : :
227 : : void set_array_argument(const GI::CallableInfo, uint8_t gi_index,
228 : : const GI::TypeInfo, GIDirection, const GI::ArgInfo,
229 : : GjsArgumentFlags, unsigned length_pos);
230 : :
231 : : void set_array_return(const GI::CallableInfo, const GI::TypeInfo,
232 : : GjsArgumentFlags, unsigned length_pos);
233 : :
234 : : void init_out_array_length_argument(const GI::ArgInfo, GjsArgumentFlags,
235 : : unsigned length_pos);
236 : :
237 : : template <typename T>
238 : : constexpr void set_return(T* arg, GITransfer, GjsArgumentFlags);
239 : :
240 : : template <typename T>
241 : : constexpr void set_instance(
242 : : T* arg, GITransfer, GjsArgumentFlags flags = GjsArgumentFlags::NONE);
243 : :
244 : : void set_skip_all(uint8_t index, const char* name = nullptr);
245 : :
246 : : template <Arg::Kind ArgKind = Arg::Kind::NORMAL>
247 : 575283 : constexpr uint8_t arg_index(uint8_t index
248 : : [[maybe_unused]] = Argument::MAX_ARGS) const {
249 : : if constexpr (ArgKind == Arg::Kind::RETURN_VALUE)
250 : 135664 : return 0;
251 : : else if constexpr (ArgKind == Arg::Kind::INSTANCE)
252 [ + + ]: 129540 : return (m_has_return ? 1 : 0);
253 : : else if constexpr (ArgKind == Arg::Kind::NORMAL)
254 [ + + + + ]: 310079 : return (m_has_return ? 1 : 0) + (m_is_method ? 1 : 0) + index;
255 : : }
256 : :
257 : : template <Arg::Kind ArgKind = Arg::Kind::NORMAL>
258 : 575283 : constexpr ArgumentPtr& arg_get(uint8_t index = Argument::MAX_ARGS) const {
259 : 575283 : return m_args[arg_index<ArgKind>(index)];
260 : : }
261 : :
262 : : public:
263 : 284387 : constexpr Argument* argument(uint8_t index) const {
264 : 284387 : return arg_get(index).get();
265 : : }
266 : :
267 : 125540 : constexpr mozilla::Maybe<Argument*> instance() const {
268 [ - + ]: 125540 : if (!m_is_method)
269 : 0 : return {};
270 : :
271 : 125540 : return mozilla::Some(arg_get<Arg::Kind::INSTANCE>().get());
272 : : }
273 : :
274 : 230567 : constexpr mozilla::Maybe<Argument*> return_value() const {
275 [ + + ]: 230567 : if (!m_has_return)
276 : 105903 : return {};
277 : :
278 : 124664 : return mozilla::Some(arg_get<Arg::Kind::RETURN_VALUE>().get());
279 : : }
280 : :
281 : : private:
282 : : AutoCppPointer<ArgumentPtr[]> m_args;
283 : :
284 : : bool m_is_method : 1;
285 : : bool m_has_return : 1;
286 : : };
287 : :
288 : : } // namespace Gjs
|