[girffi] Clean up API, add g_function_info_prep_invoker
[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                        gboolean    is_pointer)
35 {
36   switch (tag)
37     {
38     case GI_TYPE_TAG_BOOLEAN:
39       return &ffi_type_uint;
40     case GI_TYPE_TAG_INT8:
41       return &ffi_type_sint8;
42     case GI_TYPE_TAG_UINT8:
43       return &ffi_type_uint8;
44     case GI_TYPE_TAG_INT16:
45       return &ffi_type_sint16;
46     case GI_TYPE_TAG_UINT16:
47       return &ffi_type_uint16;
48     case GI_TYPE_TAG_INT32:
49       return &ffi_type_sint32;
50     case GI_TYPE_TAG_UINT32:
51       return &ffi_type_uint32;
52     case GI_TYPE_TAG_INT64:
53       return &ffi_type_sint64;
54     case GI_TYPE_TAG_UINT64:
55       return &ffi_type_uint64;
56     case GI_TYPE_TAG_SHORT:
57       return &ffi_type_sshort;
58     case GI_TYPE_TAG_USHORT:
59       return &ffi_type_ushort;
60     case GI_TYPE_TAG_INT:
61       return &ffi_type_sint;
62     case GI_TYPE_TAG_UINT:
63       return &ffi_type_uint;
64     case GI_TYPE_TAG_SSIZE:
65 #if GLIB_SIZEOF_SIZE_T == 4
66       return &ffi_type_sint32;
67 #elif GLIB_SIZEOF_SIZE_T == 8
68       return &ffi_type_sint64;
69 #else
70 #  error "Unexpected size for size_t: not 4 or 8"
71 #endif
72     case GI_TYPE_TAG_LONG:
73       return &ffi_type_slong;
74     case GI_TYPE_TAG_SIZE:
75     case GI_TYPE_TAG_GTYPE:
76 #if GLIB_SIZEOF_SIZE_T == 4
77       return &ffi_type_uint32;
78 #elif GLIB_SIZEOF_SIZE_T == 8
79       return &ffi_type_uint64;
80 #else
81 #  error "Unexpected size for size_t: not 4 or 8"
82 #endif
83     case GI_TYPE_TAG_TIME_T:
84 #if SIZEOF_TIME_T == 4
85       return &ffi_type_sint32;
86 #elif SIZEOF_TIME_T == 8
87       return &ffi_type_sint64;
88 #else
89 #  error "Unexpected size for time_t: not 4 or 8"
90 #endif
91     case GI_TYPE_TAG_ULONG:
92       return &ffi_type_ulong;
93     case GI_TYPE_TAG_FLOAT:
94       return &ffi_type_float;
95     case GI_TYPE_TAG_DOUBLE:
96       return &ffi_type_double;
97     case GI_TYPE_TAG_UTF8:
98     case GI_TYPE_TAG_FILENAME:
99     case GI_TYPE_TAG_ARRAY:
100     case GI_TYPE_TAG_INTERFACE:
101     case GI_TYPE_TAG_GLIST:
102     case GI_TYPE_TAG_GSLIST:
103     case GI_TYPE_TAG_GHASH:
104     case GI_TYPE_TAG_ERROR:
105       return &ffi_type_pointer;
106     case GI_TYPE_TAG_VOID:
107       if (is_pointer)
108         return &ffi_type_pointer;
109       else
110         return &ffi_type_void;
111     }
112
113   g_assert_not_reached ();
114
115   return NULL;
116 }  
117
118 /**
119  * g_type_info_get_ffi_type:
120  * @info: A #GITypeInfo
121  *
122  * Returns: A #ffi_type corresponding to the platform default C ABI for @info.
123  */
124 ffi_type *
125 g_type_info_get_ffi_type (GITypeInfo *info)
126 {
127   return g_ir_ffi_get_ffi_type (g_type_info_get_tag (info), g_type_info_is_pointer (info));
128 }
129
130 /**
131  * g_callable_info_get_ffi_arg_types:
132  * @callable_info: a callable info from a typelib
133  *
134  * Return value: an array of ffi_type*. The array itself
135  * should be freed using g_free() after use.
136  */
137 static ffi_type **
138 g_callable_info_get_ffi_arg_types (GICallableInfo *callable_info)
139 {
140     ffi_type **arg_types;
141     gint n_args, i;
142     
143     g_return_val_if_fail (callable_info != NULL, NULL);
144
145     n_args = g_callable_info_get_n_args (callable_info);
146     
147     arg_types = (ffi_type **) g_new0 (ffi_type *, n_args + 1);
148     
149     for (i = 0; i < n_args; ++i)
150       {
151         GIArgInfo *arg_info = g_callable_info_get_arg (callable_info, i);
152         GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
153         arg_types[i] = g_type_info_get_ffi_type (arg_type);
154         g_base_info_unref ((GIBaseInfo *)arg_info);
155         g_base_info_unref ((GIBaseInfo *)arg_type);
156       }
157     arg_types[n_args] = NULL;
158
159     return arg_types;
160 }
161
162 /**
163  * g_callable_info_get_ffi_return_type:
164  * @callable_info: a callable info from a typelib
165  *
166  * Fetches the ffi_type for a corresponding return value of
167  * a #GICallableInfo
168  * Return value: the ffi_type for the return value
169  */
170 static ffi_type *
171 g_callable_info_get_ffi_return_type (GICallableInfo *callable_info)
172 {
173   GITypeInfo *return_type;
174   GITypeTag type_tag;
175   ffi_type *return_ffi_type;
176
177   g_return_val_if_fail (callable_info != NULL, NULL);
178
179   return_type = g_callable_info_get_return_type (callable_info);
180   type_tag = g_type_info_get_tag (return_type);
181   return_ffi_type = g_type_info_get_ffi_type (return_type);
182   g_base_info_unref((GIBaseInfo*)return_type);
183   
184   return return_ffi_type;
185 }
186
187 /**
188  * g_function_info_prep_invoker:
189  * @info: A #GIFunctionInfo
190  * @invoker: Output invoker structure
191  * @error: A #GError
192  *
193  * Initialize the caller-allocated @invoker structure with a cache
194  * of information needed to invoke the C function corresponding to
195  * @info with the platform's default ABI.
196  *
197  * A primary intent of this function is that a dynamic structure allocated
198  * by a language binding could contain a #GIFunctionInvoker structure
199  * inside the binding's function mapping.
200  */
201 gboolean
202 g_function_info_prep_invoker (GIFunctionInfo       *info,
203                               GIFunctionInvoker    *invoker,
204                               GError              **error)
205 {
206   const char *symbol;
207   ffi_type *rtype;
208   ffi_type **atypes;
209   GITypeInfo *tinfo;
210   GIArgInfo *ainfo;
211   gboolean is_method;
212   gboolean throws;
213   gint n_args, n_invoke_args, i;
214   
215   g_return_val_if_fail (info != NULL, FALSE);
216   g_return_val_if_fail (invoker != NULL, FALSE);
217   
218   symbol = g_function_info_get_symbol ((GIFunctionInfo*) info);
219   
220   if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info),
221                          symbol, &(invoker->native_address)))
222     {
223       g_set_error (error,
224                    G_INVOKE_ERROR,
225                    G_INVOKE_ERROR_SYMBOL_NOT_FOUND,
226                    "Could not locate %s: %s", symbol, g_module_error ());
227
228       return FALSE;
229     }
230
231   is_method = (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD) != 0
232     && (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0;
233   throws = g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
234
235   tinfo = g_callable_info_get_return_type ((GICallableInfo *)info);
236   rtype = g_type_info_get_ffi_type (tinfo);
237   g_base_info_unref ((GIBaseInfo *)tinfo);
238
239   n_args = g_callable_info_get_n_args ((GICallableInfo *)info);
240   if (is_method)
241     n_invoke_args = n_args+1;
242   else
243     n_invoke_args = n_args;
244
245   if (throws)
246     /* Add an argument for the GError */
247     n_invoke_args ++;
248
249   /* TODO: avoid malloc here? */
250   atypes = g_malloc0 (sizeof (ffi_type*) * n_invoke_args);
251   
252   if (is_method)
253     {
254       atypes[0] = &ffi_type_pointer;
255     }
256   for (i = 0; i < n_args; i++)
257     {
258       int offset = (is_method ? 1 : 0);
259       ainfo = g_callable_info_get_arg ((GICallableInfo *)info, i);
260       switch (g_arg_info_get_direction (ainfo))
261         {
262           case GI_DIRECTION_IN:
263             tinfo = g_arg_info_get_type (ainfo);
264             atypes[i+offset] = g_type_info_get_ffi_type (tinfo);
265             g_base_info_unref ((GIBaseInfo *)tinfo);
266             break;
267           case GI_DIRECTION_OUT:
268           case GI_DIRECTION_INOUT:
269             atypes[i+offset] = &ffi_type_pointer;
270             break;
271           default:
272             g_assert_not_reached ();
273         }
274       g_base_info_unref ((GIBaseInfo *)ainfo);
275     }
276
277   if (throws)
278     {
279       atypes[n_invoke_args - 1] = &ffi_type_pointer;
280     }
281
282   return ffi_prep_cif (&(invoker->cif), FFI_DEFAULT_ABI, n_invoke_args, rtype, atypes) == FFI_OK;
283 }
284
285 /**
286  * g_function_info_invoker_destroy:
287  * @invoker: A #GIFunctionInvoker
288  * 
289  * Release all resources allocated for the internals of @invoker; callers
290  * are responsible for freeing any resources allocated for the structure
291  * itself however.
292  */
293 void
294 g_function_invoker_destroy (GIFunctionInvoker    *invoker)
295 {
296   g_free (invoker->cif.arg_types);
297 }
298
299 /**
300  * g_callable_info_prepare_closure:
301  * @callable_info: a callable info from a typelib
302  * @cif: a ffi_cif structure
303  * @callback: the ffi callback
304  * @user_data: data to be passed into the callback
305  *
306  * Prepares a callback for ffi invocation.
307  *
308  * Note: this function requires the heap to be executable, which
309  *       might not function properly on systems with SELinux enabled.
310  *
311  * Return value: the ffi_closure or NULL on error.
312  * The return value should be freed by calling g_callable_info_prepare_closure().
313  */
314 ffi_closure *
315 g_callable_info_prepare_closure (GICallableInfo       *callable_info,
316                                  ffi_cif              *cif,
317                                  GIFFIClosureCallback  callback,
318                                  gpointer              user_data)
319 {
320   ffi_closure *closure;
321   ffi_status status;
322     
323   g_return_val_if_fail (callable_info != NULL, FALSE);
324   g_return_val_if_fail (cif != NULL, FALSE);
325   g_return_val_if_fail (callback != NULL, FALSE);
326     
327   closure = mmap (NULL, sizeof (ffi_closure),
328                   PROT_EXEC | PROT_READ | PROT_WRITE,
329                   MAP_ANON | MAP_PRIVATE, -1, sysconf (_SC_PAGE_SIZE));
330   if (!closure)
331     {
332       g_warning("mmap failed: %s\n", strerror(errno));
333       return NULL;
334     }
335
336   status = ffi_prep_cif (cif, FFI_DEFAULT_ABI,
337                          g_callable_info_get_n_args (callable_info),
338                          g_callable_info_get_ffi_return_type (callable_info),
339                          g_callable_info_get_ffi_arg_types (callable_info));
340   if (status != FFI_OK)
341     {
342       g_warning("ffi_prep_cif failed: %d\n", status);
343       munmap(closure, sizeof (closure));
344       return NULL;
345     }
346
347   status = ffi_prep_closure (closure, cif, callback, user_data);
348   if (status != FFI_OK)
349     {
350       g_warning ("ffi_prep_closure failed: %d\n", status);
351       munmap(closure, sizeof (closure));
352       return NULL;
353     }
354
355   if (mprotect(closure, sizeof (closure), PROT_READ | PROT_EXEC) == -1)
356     {
357       g_warning ("ffi_prep_closure failed: %s\n", strerror(errno));
358       munmap(closure, sizeof (closure));
359       return NULL;
360     }
361   
362   return closure;
363 }
364
365 /**
366  * g_callable_info_free_closure:
367  * @callable_info: a callable info from a typelib
368  * @closure: ffi closure
369  *
370  * Frees a ffi_closure returned from g_callable_info_prepare_closure()
371  */
372 void
373 g_callable_info_free_closure (GICallableInfo *callable_info,
374                               ffi_closure    *closure)
375 {
376   munmap(closure, sizeof (closure));
377 }