[girffi] Clean up API, add g_function_info_prep_invoker
[gnome.gobject-introspection] / girepository / ginvoke.c
1 /* GObject introspection: Invoke functionality
2  *
3  * Copyright (C) 2005 Matthias Clasen
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include <stdlib.h>
22
23 #include <glib.h>
24 #include <glib-object.h>
25
26 #include "girepository.h"
27 #include "girffi.h"
28 #include "gtypelib.h"
29 #include "config.h"
30
31 GQuark
32 g_invoke_error_quark (void)
33 {
34   static GQuark quark = 0;
35   if (quark == 0)
36     quark = g_quark_from_static_string ("g-invoke-error-quark");
37   return quark;
38 }
39
40 /**
41  * g_function_info_invoke:
42  * @info: a #GIFunctionInfo describing the function to invoke
43  * @in_args: an array of #GArgument<!-- -->s, one for each in 
44  *    parameter of @info. If there are no in parameter, @in_args
45  *    can be %NULL
46  * @n_in_args: the length of the @in_args array
47  * @out_args: an array of #GArgument<!-- -->s, one for each out
48  *    parameter of @info. If there are no out parameters, @out_args
49  *    may be %NULL 
50  * @n_out_args: the length of the @out_args array
51  * @return_value: return location for the return value of the 
52  *    function. If the function returns void, @return_value may be
53  *    %NULL
54  * @error: return location for detailed error information, or %NULL
55  *
56  * Invokes the function described in @info with the given 
57  * arguments. Note that inout parameters must appear in both
58  * argument lists. This function uses dlsym() to obtain a pointer
59  * to the function, so the library or shared object containing the 
60  * described function must either be linked to the caller, or must 
61  * have been dlopen()<!-- -->ed before calling this function.
62  *
63  * Returns: %TRUE if the function has been invoked, %FALSE if an
64  *   error occurred. 
65  */
66 gboolean 
67 g_function_info_invoke (GIFunctionInfo *info, 
68                         const GArgument  *in_args,
69                         int               n_in_args,
70                         const GArgument  *out_args,
71                         int               n_out_args,
72                         GArgument        *return_value,
73                         GError          **error)
74 {
75   ffi_cif cif;
76   ffi_type *rtype;
77   ffi_type **atypes;
78   const gchar *symbol;
79   gpointer func;
80   GITypeInfo *tinfo;
81   GIArgInfo *ainfo;
82   gboolean is_method;
83   gboolean throws;
84   gint n_args, n_invoke_args, in_pos, out_pos, i;
85   gpointer *args;
86   gboolean success = FALSE;
87   GError *local_error = NULL;
88   gpointer error_address = &local_error;
89
90   symbol = g_function_info_get_symbol (info);
91
92   if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info),
93                          symbol, &func))
94     {
95       g_set_error (error,
96                    G_INVOKE_ERROR,
97                    G_INVOKE_ERROR_SYMBOL_NOT_FOUND,
98                    "Could not locate %s: %s", symbol, g_module_error ());
99
100       return FALSE;
101     }
102
103   is_method = (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD) != 0
104     && (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0;
105   throws = g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
106
107   tinfo = g_callable_info_get_return_type ((GICallableInfo *)info);
108   rtype = g_type_info_get_ffi_type (tinfo);
109   g_base_info_unref ((GIBaseInfo *)tinfo);
110
111   in_pos = 0;
112   out_pos = 0;
113
114   n_args = g_callable_info_get_n_args ((GICallableInfo *)info);
115   if (is_method)
116     {
117       if (n_in_args == 0)
118         {
119           g_set_error (error,
120                        G_INVOKE_ERROR,
121                        G_INVOKE_ERROR_ARGUMENT_MISMATCH,
122                        "Too few \"in\" arguments (handling this)");
123           goto out;
124         }
125       n_invoke_args = n_args+1;
126       in_pos++;
127     }
128   else
129     n_invoke_args = n_args;
130
131   if (throws)
132     /* Add an argument for the GError */
133     n_invoke_args ++;
134
135   atypes = g_alloca (sizeof (ffi_type*) * n_invoke_args);
136   args = g_alloca (sizeof (gpointer) * n_invoke_args);
137   
138   if (is_method)
139     {
140       atypes[0] = &ffi_type_pointer;
141       args[0] = (gpointer) &in_args[0];
142     }
143   for (i = 0; i < n_args; i++)
144     {
145       int offset = (is_method ? 1 : 0);
146       ainfo = g_callable_info_get_arg ((GICallableInfo *)info, i);
147       switch (g_arg_info_get_direction (ainfo))
148         {
149         case GI_DIRECTION_IN:
150           tinfo = g_arg_info_get_type (ainfo);
151           atypes[i+offset] = g_type_info_get_ffi_type (tinfo);
152           g_base_info_unref ((GIBaseInfo *)tinfo);
153
154           if (in_pos >= n_in_args)
155             {
156               g_set_error (error,
157                            G_INVOKE_ERROR,
158                            G_INVOKE_ERROR_ARGUMENT_MISMATCH,
159                            "Too few \"in\" arguments (handling in)");
160               goto out;
161             }
162
163           args[i+offset] = (gpointer)&in_args[in_pos];
164           in_pos++;
165           
166           break;
167         case GI_DIRECTION_OUT:
168           atypes[i+offset] = &ffi_type_pointer;
169
170           if (out_pos >= n_out_args)
171             {
172               g_set_error (error,
173                            G_INVOKE_ERROR,
174                            G_INVOKE_ERROR_ARGUMENT_MISMATCH,
175                            "Too few \"out\" arguments (handling out)");       
176               goto out;
177             }
178
179           args[i+offset] = (gpointer)&out_args[out_pos];
180           out_pos++;      
181           break;
182         case GI_DIRECTION_INOUT:
183           atypes[i+offset] = &ffi_type_pointer;
184
185           if (in_pos >= n_in_args)
186             {
187               g_set_error (error,
188                            G_INVOKE_ERROR,
189                            G_INVOKE_ERROR_ARGUMENT_MISMATCH,
190                            "Too few \"in\" arguments (handling inout)");
191               goto out;
192             }
193
194           if (out_pos >= n_out_args)
195             {
196               g_set_error (error,
197                            G_INVOKE_ERROR,
198                            G_INVOKE_ERROR_ARGUMENT_MISMATCH,
199                            "Too few \"out\" arguments (handling inout)");             
200               goto out;
201             }
202           
203           args[i+offset] = (gpointer)&in_args[in_pos];
204           in_pos++;       
205           out_pos++;      
206           break;
207         default:
208           g_assert_not_reached ();
209         }
210       g_base_info_unref ((GIBaseInfo *)ainfo);
211     }
212
213   if (throws)
214     {
215       args[n_invoke_args - 1] = &error_address;
216       atypes[n_invoke_args - 1] = &ffi_type_pointer;
217     }
218
219   if (in_pos < n_in_args)
220     {
221       g_set_error (error,
222                    G_INVOKE_ERROR,
223                    G_INVOKE_ERROR_ARGUMENT_MISMATCH,
224                    "Too many \"in\" arguments (at end)");
225       goto out;
226     }
227   if (out_pos < n_out_args)
228     {
229       g_set_error (error,
230                    G_INVOKE_ERROR,
231                    G_INVOKE_ERROR_ARGUMENT_MISMATCH,
232                    "Too many \"out\" arguments (at end)");            
233       goto out;
234     }
235
236   if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_invoke_args, rtype, atypes) != FFI_OK)
237     goto out;
238
239   g_return_val_if_fail (return_value, FALSE);
240   ffi_call (&cif, func, return_value, args);
241
242   if (local_error)
243     {
244       g_propagate_error (error, local_error);
245       success = FALSE;
246     }
247   else
248     {
249       success = TRUE;
250     }
251  out:
252   return success;
253 }
254
255 static ffi_type *
256 value_to_ffi_type (const GValue *gvalue, gpointer *value)
257 {
258   ffi_type *rettype = NULL;
259   GType type = g_type_fundamental (G_VALUE_TYPE (gvalue));
260   g_assert (type != G_TYPE_INVALID);
261
262   switch (type)
263     {
264     case G_TYPE_BOOLEAN:
265     case G_TYPE_CHAR:
266     case G_TYPE_INT:
267       rettype = &ffi_type_sint;
268       *value = (gpointer)&(gvalue->data[0].v_int);
269       break;
270     case G_TYPE_UCHAR:
271     case G_TYPE_UINT:
272       rettype = &ffi_type_uint;
273       *value = (gpointer)&(gvalue->data[0].v_uint);
274       break;
275     case G_TYPE_STRING:
276     case G_TYPE_OBJECT:
277     case G_TYPE_BOXED:
278     case G_TYPE_POINTER:
279       rettype = &ffi_type_pointer;
280       *value = (gpointer)&(gvalue->data[0].v_pointer);
281       break;
282     case G_TYPE_FLOAT:
283       rettype = &ffi_type_float;
284       *value = (gpointer)&(gvalue->data[0].v_float);
285       break;
286     case G_TYPE_DOUBLE:
287       rettype = &ffi_type_double;
288       *value = (gpointer)&(gvalue->data[0].v_double);
289       break;
290     case G_TYPE_LONG:
291       rettype = &ffi_type_slong;
292       *value = (gpointer)&(gvalue->data[0].v_long);
293       break;
294     case G_TYPE_ULONG:
295       rettype = &ffi_type_ulong;
296       *value = (gpointer)&(gvalue->data[0].v_ulong);
297       break;
298     case G_TYPE_INT64:
299       rettype = &ffi_type_sint64;
300       *value = (gpointer)&(gvalue->data[0].v_int64);
301       break;
302     case G_TYPE_UINT64:
303       rettype = &ffi_type_uint64;
304       *value = (gpointer)&(gvalue->data[0].v_uint64);
305       break;
306     default:
307       rettype = &ffi_type_pointer;
308       *value = NULL;
309       g_warning ("Unsupported fundamental type: %s", g_type_name (type));
310       break;
311     }
312   return rettype;
313 }
314
315 static void
316 value_from_ffi_type (GValue *gvalue, gpointer *value)
317 {
318   switch (g_type_fundamental (G_VALUE_TYPE (gvalue)))
319     {
320     case G_TYPE_INT:
321       g_value_set_int (gvalue, *(gint*)value);
322       break;
323     case G_TYPE_FLOAT:
324       g_value_set_float (gvalue, *(gfloat*)value);
325       break;
326     case G_TYPE_DOUBLE:
327       g_value_set_double (gvalue, *(gdouble*)value);
328       break;
329     case G_TYPE_BOOLEAN:
330       g_value_set_boolean (gvalue, *(gboolean*)value);
331       break;
332     case G_TYPE_STRING:
333       g_value_set_string (gvalue, *(gchar**)value);
334       break;
335     case G_TYPE_CHAR:
336       g_value_set_char (gvalue, *(gchar*)value);
337       break;
338     case G_TYPE_UCHAR:
339       g_value_set_uchar (gvalue, *(guchar*)value);
340       break;
341     case G_TYPE_UINT:
342       g_value_set_uint (gvalue, *(guint*)value);
343       break;
344     case G_TYPE_POINTER:
345       g_value_set_pointer (gvalue, *(gpointer*)value);
346       break;
347     case G_TYPE_LONG:
348       g_value_set_long (gvalue, *(glong*)value);
349       break;
350     case G_TYPE_ULONG:
351       g_value_set_ulong (gvalue, *(gulong*)value);
352       break;
353     case G_TYPE_INT64:
354       g_value_set_int64 (gvalue, *(gint64*)value);
355       break;
356     case G_TYPE_UINT64:
357       g_value_set_uint64 (gvalue, *(guint64*)value);
358       break;
359     case G_TYPE_BOXED:
360       g_value_set_boxed (gvalue, *(gpointer*)value);
361       break;
362     default:
363       g_warning ("Unsupported fundamental type: %s",
364                 g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue))));
365     }
366 }
367
368 void
369 gi_cclosure_marshal_generic (GClosure *closure,
370                              GValue *return_gvalue,
371                              guint n_param_values,
372                              const GValue *param_values,
373                              gpointer invocation_hint,
374                              gpointer marshal_data)
375 {
376   ffi_type *rtype;
377   void *rvalue;
378   int n_args;
379   ffi_type **atypes;
380   void **args;
381   int i;
382   ffi_cif cif;
383   GCClosure *cc = (GCClosure*) closure;
384
385   if (return_gvalue && G_VALUE_TYPE (return_gvalue)) 
386     {
387       rtype = value_to_ffi_type (return_gvalue, &rvalue);
388     }
389   else 
390     {
391       rtype = &ffi_type_void;
392     }
393
394   rvalue = g_alloca (MAX (rtype->size, sizeof (ffi_arg)));
395   
396   n_args = n_param_values + 1;
397   atypes = g_alloca (sizeof (ffi_type *) * n_args);
398   args =  g_alloca (sizeof (gpointer) * n_args);
399
400   if (n_param_values > 0)
401     {
402       if (G_CCLOSURE_SWAP_DATA (closure))
403         {
404           atypes[n_args-1] = value_to_ffi_type (param_values + 0,  
405                                                 &args[n_args-1]);
406           atypes[0] = &ffi_type_pointer;
407           args[0] = &closure->data;
408         }
409       else
410         {
411           atypes[0] = value_to_ffi_type (param_values + 0, &args[0]);
412           atypes[n_args-1] = &ffi_type_pointer;
413           args[n_args-1] = &closure->data;
414         }
415     }
416   else
417     {
418       atypes[0] = &ffi_type_pointer;
419       args[0] = &closure->data;
420     }
421
422   for (i = 1; i < n_args - 1; i++)
423     atypes[i] = value_to_ffi_type (param_values + i, &args[i]);
424
425   if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK)
426     return;
427
428   g_return_if_fail (rvalue != NULL);
429   ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args);
430
431   if (return_gvalue && G_VALUE_TYPE (return_gvalue))
432     value_from_ffi_type (return_gvalue, rvalue);
433 }