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