Fix build on OpenBSD
[gnome.gobject-introspection] / girepository / girffi.c
1 /* GObject introspection: Helper functions for ffi integration
2  *
3  * Copyright (C) 2008 Red Hat, Inc
4  * Copyright (C) 2005 Matthias Clasen
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include <sys/types.h>
23 #include <sys/mman.h>
24
25 #include <config.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include "girffi.h"
30 #include "girepository.h"
31
32 ffi_type *
33 g_ir_ffi_get_ffi_type (GITypeTag tag)
34 {
35   switch (tag)
36     {
37     case GI_TYPE_TAG_BOOLEAN:
38       return &ffi_type_uint;
39     case GI_TYPE_TAG_INT8:
40       return &ffi_type_sint8;
41     case GI_TYPE_TAG_UINT8:
42       return &ffi_type_uint8;
43     case GI_TYPE_TAG_INT16:
44       return &ffi_type_sint16;
45     case GI_TYPE_TAG_UINT16:
46       return &ffi_type_uint16;
47     case GI_TYPE_TAG_INT32:
48       return &ffi_type_sint32;
49     case GI_TYPE_TAG_UINT32:
50       return &ffi_type_uint32;
51     case GI_TYPE_TAG_INT64:
52       return &ffi_type_sint64;
53     case GI_TYPE_TAG_UINT64:
54       return &ffi_type_uint64;
55     case GI_TYPE_TAG_SHORT:
56       return &ffi_type_sshort;
57     case GI_TYPE_TAG_USHORT:
58       return &ffi_type_ushort;
59     case GI_TYPE_TAG_INT:
60       return &ffi_type_sint;
61     case GI_TYPE_TAG_UINT:
62       return &ffi_type_uint;
63     case GI_TYPE_TAG_SSIZE:
64 #if GLIB_SIZEOF_SIZE_T == 4
65       return &ffi_type_sint32;
66 #elif GLIB_SIZEOF_SIZE_T == 8
67       return &ffi_type_sint64;
68 #else
69 #  error "Unexpected size for size_t: not 4 or 8"
70 #endif
71     case GI_TYPE_TAG_LONG:
72       return &ffi_type_slong;
73     case GI_TYPE_TAG_SIZE:
74     case GI_TYPE_TAG_GTYPE:
75 #if GLIB_SIZEOF_SIZE_T == 4
76       return &ffi_type_uint32;
77 #elif GLIB_SIZEOF_SIZE_T == 8
78       return &ffi_type_uint64;
79 #else
80 #  error "Unexpected size for size_t: not 4 or 8"
81 #endif
82     case GI_TYPE_TAG_TIME_T:
83 #if SIZEOF_TIME_T == 4
84       return &ffi_type_sint32;
85 #elif SIZEOF_TIME_T == 8
86       return &ffi_type_sint64;
87 #else
88 #  error "Unexpected size for time_t: not 4 or 8"
89 #endif
90     case GI_TYPE_TAG_ULONG:
91       return &ffi_type_ulong;
92     case GI_TYPE_TAG_FLOAT:
93       return &ffi_type_float;
94     case GI_TYPE_TAG_DOUBLE:
95       return &ffi_type_double;
96     case GI_TYPE_TAG_UTF8:
97     case GI_TYPE_TAG_FILENAME:
98     case GI_TYPE_TAG_ARRAY:
99     case GI_TYPE_TAG_INTERFACE:
100     case GI_TYPE_TAG_GLIST:
101     case GI_TYPE_TAG_GSLIST:
102     case GI_TYPE_TAG_GHASH:
103     case GI_TYPE_TAG_ERROR:
104     case GI_TYPE_TAG_VOID:
105       return &ffi_type_pointer;
106     }
107
108   g_assert_not_reached ();
109
110   return NULL;
111 }
112
113 GArgument *
114 g_ir_ffi_convert_arguments(GICallableInfo *callable_info, void **args)
115 {
116   gint num_args, i;
117   GIArgInfo *arg_info;
118   GITypeInfo *arg_type;
119   GITypeTag tag;
120   GArgument *g_args;
121
122   num_args = g_callable_info_get_n_args (callable_info);
123   g_args = g_new0 (GArgument, num_args);
124
125   for (i = 0; i < num_args; i++)
126     {
127       arg_info = g_callable_info_get_arg (callable_info, i);
128       arg_type = g_arg_info_get_type (arg_info);
129       tag = g_type_info_get_tag (arg_type);
130
131       switch (tag)
132         {
133         case GI_TYPE_TAG_BOOLEAN:
134           g_args[i].v_boolean = *(gboolean *) args[i];
135           break;
136         case GI_TYPE_TAG_INT8:
137           g_args[i].v_int8 = *(gint8 *) args[i];
138           break;
139         case GI_TYPE_TAG_UINT8:
140           g_args[i].v_uint8 = *(guint8 *) args[i];
141           break;
142         case GI_TYPE_TAG_INT16:
143           g_args[i].v_int16 = *(gint16 *) args[i];
144           break;
145         case GI_TYPE_TAG_UINT16:
146           g_args[i].v_uint16 = *(guint16 *) args[i];
147           break;
148         case GI_TYPE_TAG_INT32:
149           g_args[i].v_int32 = *(gint32 *) args[i];
150           break;
151         case GI_TYPE_TAG_UINT32:
152           g_args[i].v_uint32 = *(guint32 *) args[i];
153           break;
154         case GI_TYPE_TAG_LONG:
155         case GI_TYPE_TAG_INT64:
156           g_args[i].v_int64 = *(glong *) args[i];
157           break;
158         case GI_TYPE_TAG_ULONG:
159         case GI_TYPE_TAG_UINT64:
160           g_args[i].v_uint64 = *(glong *) args[i];
161           break;
162         case GI_TYPE_TAG_INT:
163         case GI_TYPE_TAG_SSIZE:
164         case GI_TYPE_TAG_SIZE:
165           g_args[i].v_int32 = *(gint *) args[i];
166           break;
167         case GI_TYPE_TAG_UINT:
168           g_args[i].v_uint32 = *(guint *) args[i];
169           break;
170         case GI_TYPE_TAG_FLOAT:
171           g_args[i].v_float = *(gfloat *) args[i];
172           break;
173         case GI_TYPE_TAG_DOUBLE:
174           g_args[i].v_double = *(gdouble *) args[i];
175           break;
176         case GI_TYPE_TAG_UTF8:
177           g_args[i].v_string = *(gchar **) args[i];
178           break;
179         case GI_TYPE_TAG_INTERFACE:
180           {
181             GIBaseInfo *interface;
182             GIInfoType interface_type;
183
184             interface = g_type_info_get_interface (arg_type);
185             interface_type = g_base_info_get_type (interface);
186
187             if (interface_type == GI_INFO_TYPE_OBJECT ||
188                 interface_type == GI_INFO_TYPE_INTERFACE)
189               {
190                 g_args[i].v_pointer = *(gpointer *) args[i];
191                 g_base_info_unref (interface);
192                 break;
193               }
194
195             else if (interface_type == GI_INFO_TYPE_ENUM ||
196                      interface_type == GI_INFO_TYPE_FLAGS)
197               {
198                 g_args[i].v_double = *(double *) args[i];
199                 g_base_info_unref (interface);
200                 break;
201               }
202             else if (interface_type == GI_INFO_TYPE_STRUCT)
203               {
204                 g_args[i].v_pointer = *(gpointer *) args[i];
205                 g_base_info_unref (interface);
206                 break;
207               }
208
209             g_base_info_unref (interface);
210           }
211         case GI_TYPE_TAG_GLIST:
212         case GI_TYPE_TAG_GSLIST:
213           g_args[i].v_pointer = *(gpointer *) args[i];
214           break;
215         default:
216           g_args[i].v_pointer = 0;
217         }
218
219       g_base_info_unref ((GIBaseInfo *) arg_info);
220       g_base_info_unref ((GIBaseInfo *) arg_type);
221     }
222   return g_args;
223 }
224
225 /**
226  * g_callable_info_get_ffi_arg_types:
227  * @callable_info: a callable info from a typelib
228  *
229  * Return value: an array of ffi_type*. The array itself
230  * should be freed using g_free() after use.
231  */
232 ffi_type **
233 g_callable_info_get_ffi_arg_types (GICallableInfo *callable_info)
234 {
235     ffi_type **arg_types;
236     gint n_args, i;
237     
238     g_return_val_if_fail (callable_info != NULL, NULL);
239
240     n_args = g_callable_info_get_n_args (callable_info);
241     
242     arg_types = (ffi_type **) g_new0 (ffi_type *, n_args + 1);
243     
244     for (i = 0; i < n_args; ++i)
245       {
246         GIArgInfo *arg_info = g_callable_info_get_arg (callable_info, i);
247         GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
248         GITypeTag type_tag = g_type_info_get_tag (arg_type);
249         arg_types[i] = g_ir_ffi_get_ffi_type (type_tag);
250         g_base_info_unref ((GIBaseInfo *)arg_info);
251         g_base_info_unref ((GIBaseInfo *)arg_type);
252       }
253     arg_types[n_args] = NULL;
254
255     return arg_types;
256 }
257
258 /**
259  * g_callable_info_get_ffi_return_type:
260  * @callable_info: a callable info from a typelib
261  *
262  * Fetches the ffi_type for a corresponding return value of
263  * a #GICallableInfo
264  * Return value: the ffi_type for the return value
265  */
266 ffi_type *
267 g_callable_info_get_ffi_return_type (GICallableInfo *callable_info)
268 {
269   GITypeInfo *return_type;
270   GITypeTag type_tag;
271
272   g_return_val_if_fail (callable_info != NULL, NULL);
273
274   return_type = g_callable_info_get_return_type (callable_info);
275   type_tag = g_type_info_get_tag (return_type);
276
277   g_base_info_unref((GIBaseInfo*)return_type);
278
279   return g_ir_ffi_get_ffi_type (type_tag);
280 }
281
282 /**
283  * g_callable_info_prepare_closure:
284  * @callable_info: a callable info from a typelib
285  * @cif: a ffi_cif structure
286  * @callback: the ffi callback
287  * @user_data: data to be passed into the callback
288  *
289  * Prepares a callback for ffi invocation.
290  *
291  * Note: this function requires the heap to be executable, which
292  *       might not function properly on systems with SELinux enabled.
293  *
294  * Return value: the ffi_closure or NULL on error.
295  * The return value should be freed by calling g_callable_info_prepare_closure().
296  */
297 ffi_closure *
298 g_callable_info_prepare_closure (GICallableInfo       *callable_info,
299                                  ffi_cif              *cif,
300                                  GIFFIClosureCallback  callback,
301                                  gpointer              user_data)
302 {
303   ffi_closure *closure;
304   ffi_status status;
305     
306   g_return_val_if_fail (callable_info != NULL, FALSE);
307   g_return_val_if_fail (cif != NULL, FALSE);
308   g_return_val_if_fail (callback != NULL, FALSE);
309     
310   closure = mmap (NULL, sizeof (ffi_closure),
311                   PROT_EXEC | PROT_READ | PROT_WRITE,
312                   MAP_ANON | MAP_PRIVATE, -1, sysconf (_SC_PAGE_SIZE));
313   if (!closure)
314     {
315       g_warning("mmap failed: %s\n", strerror(errno));
316       return NULL;
317     }
318
319   status = ffi_prep_cif (cif, FFI_DEFAULT_ABI,
320                          g_callable_info_get_n_args (callable_info),
321                          g_callable_info_get_ffi_return_type (callable_info),
322                          g_callable_info_get_ffi_arg_types (callable_info));
323   if (status != FFI_OK)
324     {
325       g_warning("ffi_prep_cif failed: %d\n", status);
326       munmap(closure, sizeof (closure));
327       return NULL;
328     }
329
330   status = ffi_prep_closure (closure, cif, callback, user_data);
331   if (status != FFI_OK)
332     {
333       g_warning ("ffi_prep_closure failed: %d\n", status);
334       munmap(closure, sizeof (closure));
335       return NULL;
336     }
337
338   if (mprotect(closure, sizeof (closure), PROT_READ | PROT_EXEC) == -1)
339     {
340       g_warning ("ffi_prep_closure failed: %s\n", strerror(errno));
341       munmap(closure, sizeof (closure));
342       return NULL;
343     }
344   
345   return closure;
346 }
347
348 /**
349  * g_callable_info_free_closure:
350  * @callable_info: a callable info from a typelib
351  * @closure: ffi closure
352  *
353  * Frees a ffi_closure returned from g_callable_info_prepare_closure()
354  */
355 void
356 g_callable_info_free_closure (GICallableInfo *callable_info,
357                               ffi_closure    *closure)
358 {
359   munmap(closure, sizeof (closure));
360 }