1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 * GObject introspection: Helper functions for ffi integration
3 *
4 * Copyright (C) 2008 Red Hat, Inc
5 * Copyright (C) 2005 Matthias Clasen
6 *
7 * SPDX-License-Identifier: LGPL-2.1-or-later
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 */
24
25 #include "config.h"
26
27 #include <sys/types.h>
28
29 #include <errno.h>
30 #include <string.h>
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34 #include "girffi.h"
35 #include "girepository.h"
36 #include "girepository-private.h"
37
38 static ffi_type *
39 gi_type_tag_get_ffi_type_internal (GITypeTag tag,
40 gboolean is_pointer,
41 gboolean is_enum)
42 {
43 switch (tag)
44 {
45 case GI_TYPE_TAG_BOOLEAN:
46 return &ffi_type_uint;
47 case GI_TYPE_TAG_INT8:
48 return &ffi_type_sint8;
49 case GI_TYPE_TAG_UINT8:
50 return &ffi_type_uint8;
51 case GI_TYPE_TAG_INT16:
52 return &ffi_type_sint16;
53 case GI_TYPE_TAG_UINT16:
54 return &ffi_type_uint16;
55 case GI_TYPE_TAG_INT32:
56 return &ffi_type_sint32;
57 case GI_TYPE_TAG_UINT32:
58 case GI_TYPE_TAG_UNICHAR:
59 return &ffi_type_uint32;
60 case GI_TYPE_TAG_INT64:
61 return &ffi_type_sint64;
62 case GI_TYPE_TAG_UINT64:
63 return &ffi_type_uint64;
64 case GI_TYPE_TAG_GTYPE:
65 #if GLIB_SIZEOF_SIZE_T == 4
66 return &ffi_type_uint32;
67 #elif GLIB_SIZEOF_SIZE_T == 8
68 return &ffi_type_uint64;
69 #else
70 # error "Unexpected size for size_t: not 4 or 8"
71 #endif
72 case GI_TYPE_TAG_FLOAT:
73 return &ffi_type_float;
74 case GI_TYPE_TAG_DOUBLE:
75 return &ffi_type_double;
76 case GI_TYPE_TAG_UTF8:
77 case GI_TYPE_TAG_FILENAME:
78 case GI_TYPE_TAG_ARRAY:
79 case GI_TYPE_TAG_GLIST:
80 case GI_TYPE_TAG_GSLIST:
81 case GI_TYPE_TAG_GHASH:
82 case GI_TYPE_TAG_ERROR:
83 return &ffi_type_pointer;
84 case GI_TYPE_TAG_INTERFACE:
85 {
86 /* We need to handle enums specially:
87 * https://bugzilla.gnome.org/show_bug.cgi?id=665150
88 */
89 if (!is_enum)
90 return &ffi_type_pointer;
91 else
92 return &ffi_type_sint32;
93 }
94 case GI_TYPE_TAG_VOID:
95 if (is_pointer)
96 return &ffi_type_pointer;
97 else
98 return &ffi_type_void;
99 default:
100 break;
101 }
102
103 g_assert_not_reached ();
104
105 return NULL;
106 }
107
108 /**
109 * gi_type_tag_get_ffi_type:
110 * @type_tag: a #GITypeTag
111 * @is_pointer: whether this is a pointer type
112 *
113 * Get the `ffi_type` corresponding to @type_tag.
114 *
115 * Returns: (transfer none): an `ffi_type` corresponding to the platform default
116 * C ABI for @tag and @is_pointer.
117 * Since: 2.80
118 */
119 ffi_type *
120 gi_type_tag_get_ffi_type (GITypeTag type_tag,
121 gboolean is_pointer)
122 {
123 return gi_type_tag_get_ffi_type_internal (type_tag, is_pointer, FALSE);
124 }
125
126 /**
127 * gi_type_info_get_ffi_type:
128 * @info: a #GITypeInfo
129 *
130 * Get the `ffi_type` corresponding to @info.
131 *
132 * Returns: (transfer none): a `ffi_type` corresponding to the platform default
133 * C ABI for @info.
134 * Since: 2.80
135 */
136 ffi_type *
137 gi_type_info_get_ffi_type (GITypeInfo *info)
138 {
139 gboolean is_enum = FALSE;
140 GIBaseInfo *iinfo;
141
142 if (gi_type_info_get_tag (info) == GI_TYPE_TAG_INTERFACE)
143 {
144 iinfo = gi_type_info_get_interface (info);
145 switch (gi_base_info_get_info_type (iinfo))
146 {
147 case GI_INFO_TYPE_ENUM:
148 case GI_INFO_TYPE_FLAGS:
149 is_enum = TRUE;
150 break;
151 default:
152 break;
153 }
154 gi_base_info_unref (iinfo);
155 }
156
157 return gi_type_tag_get_ffi_type_internal (gi_type_info_get_tag (info), gi_type_info_is_pointer (info), is_enum);
158 }
159
160 /**
161 * gi_callable_info_get_ffi_arg_types:
162 * @callable_info: a callable info from a typelib
163 * @n_args_p: (out) (optional): the number of arguments returned
164 *
165 * Get the `ffi_type`s for the arguments of @callable_info.
166 *
167 * Returns: (transfer container) (array length=n_args_p): an array of
168 * `ffi_type*`. The array itself should be freed using [func@GLib.free] after
169 * use.
170 * Since: 2.80
171 */
172 static ffi_type **
173 gi_callable_info_get_ffi_arg_types (GICallableInfo *callable_info,
174 int *n_args_p)
175 {
176 ffi_type **arg_types;
177 gboolean is_method, throws;
178 gint n_args, n_invoke_args, i, offset;
179
180 g_return_val_if_fail (callable_info != NULL, NULL);
181
182 n_args = gi_callable_info_get_n_args (callable_info);
183 is_method = gi_callable_info_is_method (callable_info);
184 throws = gi_callable_info_can_throw_gerror (callable_info);
185 offset = is_method ? 1 : 0;
186
187 n_invoke_args = n_args;
188
189 if (is_method)
190 n_invoke_args++;
191 if (throws)
192 n_invoke_args++;
193
194 if (n_args_p)
195 *n_args_p = n_invoke_args;
196
197 arg_types = (ffi_type **) g_new0 (ffi_type *, n_invoke_args + 1);
198
199 if (is_method)
200 arg_types[0] = &ffi_type_pointer;
201 if (throws)
202 arg_types[n_invoke_args - 1] = &ffi_type_pointer;
203
204 for (i = 0; i < n_args; ++i)
205 {
206 GIArgInfo arg_info;
207 GITypeInfo arg_type;
208
209 gi_callable_info_load_arg (callable_info, i, &arg_info);
210 gi_arg_info_load_type (&arg_info, &arg_type);
211 switch (gi_arg_info_get_direction (&arg_info))
212 {
213 case GI_DIRECTION_IN:
214 arg_types[i + offset] = gi_type_info_get_ffi_type (&arg_type);
215 break;
216 case GI_DIRECTION_OUT:
217 case GI_DIRECTION_INOUT:
218 arg_types[i + offset] = &ffi_type_pointer;
219 break;
220 default:
221 g_assert_not_reached ();
222 }
223 }
224
225 arg_types[n_invoke_args] = NULL;
226
227 return arg_types;
228 }
229
230 /**
231 * gi_callable_info_get_ffi_return_type:
232 * @callable_info: a callable info from a typelib
233 *
234 * Fetches the `ffi_type` for a corresponding return value of
235 * a [class@GIRepository.CallableInfo].
236 *
237 * Returns: (transfer none): the `ffi_type` for the return value
238 * Since: 2.80
239 */
240 static ffi_type *
241 gi_callable_info_get_ffi_return_type (GICallableInfo *callable_info)
242 {
243 GITypeInfo *return_type;
244 ffi_type *return_ffi_type;
245
246 g_return_val_if_fail (callable_info != NULL, NULL);
247
248 return_type = gi_callable_info_get_return_type (callable_info);
249 return_ffi_type = gi_type_info_get_ffi_type (return_type);
250 gi_base_info_unref((GIBaseInfo*)return_type);
251
252 return return_ffi_type;
253 }
254
255 /**
256 * gi_function_info_prep_invoker:
257 * @info: A #GIFunctionInfo
258 * @invoker: (out caller-allocates): Output invoker structure
259 * @error: A #GError
260 *
261 * Initialize the caller-allocated @invoker structure with a cache
262 * of information needed to invoke the C function corresponding to
263 * @info with the platform’s default ABI.
264 *
265 * A primary intent of this function is that a dynamic structure allocated
266 * by a language binding could contain a [type@GIRepository.FunctionInvoker]
267 * structure inside the binding’s function mapping.
268 *
269 * Returns: `TRUE` on success, `FALSE` otherwise with @error set.
270 * Since: 2.80
271 */
272 gboolean
273 gi_function_info_prep_invoker (GIFunctionInfo *info,
274 GIFunctionInvoker *invoker,
275 GError **error)
276 {
277 const char *symbol;
278 gpointer addr;
279
280 g_return_val_if_fail (info != NULL, FALSE);
281 g_return_val_if_fail (invoker != NULL, FALSE);
282
283 symbol = gi_function_info_get_symbol ((GIFunctionInfo*) info);
284
285 if (!gi_typelib_symbol (gi_base_info_get_typelib ((GIBaseInfo *) info),
286 symbol, &addr))
287 {
288 g_set_error (error,
289 GI_INVOKE_ERROR,
290 GI_INVOKE_ERROR_SYMBOL_NOT_FOUND,
291 "Could not locate %s: %s", symbol, g_module_error ());
292
293 return FALSE;
294 }
295
296 return gi_function_invoker_new_for_address (addr, (GICallableInfo *) info, invoker, error);
297 }
298
299 /**
300 * gi_function_invoker_new_for_address:
301 * @addr: The address
302 * @info: A #GICallableInfo
303 * @invoker: (out caller-allocates): Output invoker structure
304 * @error: A #GError
305 *
306 * Initialize the caller-allocated @invoker structure with a cache
307 * of information needed to invoke the C function corresponding to
308 * @info with the platform’s default ABI.
309 *
310 * A primary intent of this function is that a dynamic structure allocated
311 * by a language binding could contain a [type@GIRepository.FunctionInvoker]
312 * structure inside the binding’s function mapping.
313 *
314 * Returns: `TRUE` on success, `FALSE` otherwise with @error set.
315 * Since: 2.80
316 */
317 gboolean
318 gi_function_invoker_new_for_address (gpointer addr,
319 GICallableInfo *info,
320 GIFunctionInvoker *invoker,
321 GError **error)
322 {
323 ffi_type **atypes;
324 gint n_args;
325
326 g_return_val_if_fail (info != NULL, FALSE);
327 g_return_val_if_fail (invoker != NULL, FALSE);
328
329 invoker->native_address = addr;
330
331 atypes = gi_callable_info_get_ffi_arg_types (info, &n_args);
332
333 return ffi_prep_cif (&(invoker->cif), FFI_DEFAULT_ABI, n_args,
334 gi_callable_info_get_ffi_return_type (info),
335 atypes) == FFI_OK;
336 }
337
338 /**
339 * gi_function_invoker_destroy:
340 * @invoker: (transfer none): A #GIFunctionInvoker
341 *
342 * Release all resources allocated for the internals of @invoker.
343 *
344 * Callers are responsible for freeing any resources allocated for the structure
345 * itself however.
346 *
347 * Since: 2.80
348 */
349 void
350 gi_function_invoker_destroy (GIFunctionInvoker *invoker)
351 {
352 g_free (invoker->cif.arg_types);
353 }
354
355 typedef struct {
356 ffi_closure ffi_closure;
357 gpointer writable_self;
358 gpointer native_address;
359 } GIClosureWrapper;
360
361 /**
362 * gi_callable_info_create_closure:
363 * @callable_info: a callable info from a typelib
364 * @cif: a `ffi_cif` structure
365 * @callback: the ffi callback
366 * @user_data: data to be passed into the callback
367 *
368 * Prepares a callback for ffi invocation.
369 *
370 * Returns: (transfer full) (nullable): the `ffi_closure`, or `NULL` on error.
371 * The return value should be freed by calling
372 * [method@GIRepository.CallableInfo.destroy_closure].
373 * Since: 2.80
374 */
375 ffi_closure *
376 gi_callable_info_create_closure (GICallableInfo *callable_info,
377 ffi_cif *cif,
378 GIFFIClosureCallback callback,
379 gpointer user_data)
380 {
381 gpointer exec_ptr;
382 int n_args;
383 ffi_type **atypes;
384 GIClosureWrapper *closure;
385 ffi_status status;
386
387 g_return_val_if_fail (callable_info != NULL, FALSE);
388 g_return_val_if_fail (cif != NULL, FALSE);
389 g_return_val_if_fail (callback != NULL, FALSE);
390
391 closure = ffi_closure_alloc (sizeof (GIClosureWrapper), &exec_ptr);
392 if (!closure)
393 {
394 g_warning ("could not allocate closure\n");
395 return NULL;
396 }
397 closure->writable_self = closure;
398 closure->native_address = exec_ptr;
399
400
401 atypes = gi_callable_info_get_ffi_arg_types (callable_info, &n_args);
402 status = ffi_prep_cif (cif, FFI_DEFAULT_ABI, n_args,
403 gi_callable_info_get_ffi_return_type (callable_info),
404 atypes);
405 if (status != FFI_OK)
406 {
407 g_warning ("ffi_prep_cif failed: %d\n", status);
408 ffi_closure_free (closure);
409 return NULL;
410 }
411
412 status = ffi_prep_closure_loc (&closure->ffi_closure, cif, callback, user_data, exec_ptr);
413 if (status != FFI_OK)
414 {
415 g_warning ("ffi_prep_closure failed: %d\n", status);
416 ffi_closure_free (closure);
417 return NULL;
418 }
419
420 return &closure->ffi_closure;
421 }
422
423 /**
424 * gi_callable_info_get_closure_native_address:
425 * @callable_info: a callable info from a typelib
426 * @closure: ffi closure
427 *
428 * Gets callable code from `ffi_closure` prepared by
429 * [method@GIRepository.CallableInfo.create_closure].
430 *
431 * Returns: (transfer none): native address
432 * Since: 2.80
433 */
434 gpointer *
435 gi_callable_info_get_closure_native_address (GICallableInfo *callable_info,
436 ffi_closure *closure)
437 {
438 GIClosureWrapper *wrapper = (GIClosureWrapper *)closure;
439 return wrapper->native_address;
440 }
441
442 /**
443 * gi_callable_info_destroy_closure:
444 * @callable_info: a callable info from a typelib
445 * @closure: (transfer full): ffi closure
446 *
447 * Frees a `ffi_closure` returned from
448 * [method@GIRepository.CallableInfo.create_closure].
449 *
450 * Since: 2.80
451 */
452 void
453 gi_callable_info_destroy_closure (GICallableInfo *callable_info,
454 ffi_closure *closure)
455 {
456 GIClosureWrapper *wrapper = (GIClosureWrapper *)closure;
457
458 g_free (wrapper->ffi_closure.cif->arg_types);
459 ffi_closure_free (wrapper->writable_self);
460 }