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