diff options
author | Tomeu Vizoso <tomeu.vizoso@collabora.co.uk> | 2010-12-13 13:53:55 +0100 |
---|---|---|
committer | Tomeu Vizoso <tomeu.vizoso@collabora.co.uk> | 2011-01-12 20:05:09 +0100 |
commit | 0c3a1d82f2543b74468d1806f87aa0cb258a925d (patch) | |
tree | d7919bfab9e8e03599468939852bff69ed50f97d /girepository/gicallableinfo.c | |
parent | 36e79f8d485bb670ac53be52ff3962f9e157dc42 (diff) | |
download | gobject-introspection-0c3a1d82f2543b74468d1806f87aa0cb258a925d.tar.gz |
Add g_vfunc_info_invoke and g_vfunc_info_get_address
for calling the native implementation of a virtual function. Refactors
the code common with g_function_info_invoke in _g_callable_info_invoke.
https://bugzilla.gnome.org/show_bug.cgi?id=637145
Diffstat (limited to 'girepository/gicallableinfo.c')
-rw-r--r-- | girepository/gicallableinfo.c | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/girepository/gicallableinfo.c b/girepository/gicallableinfo.c index 6b79e62c..1d439683 100644 --- a/girepository/gicallableinfo.c +++ b/girepository/gicallableinfo.c @@ -26,6 +26,7 @@ #include <girepository.h> #include "girepository-private.h" #include "gitypelib-internal.h" +#include "girffi.h" /* GICallableInfo functions */ @@ -335,3 +336,174 @@ g_callable_info_iterate_return_attributes (GICallableInfo *info, return TRUE; } + +gboolean +_g_callable_info_invoke (GIFunctionInfo *info, + gpointer function, + const GIArgument *in_args, + int n_in_args, + const GIArgument *out_args, + int n_out_args, + GIArgument *return_value, + gboolean is_method, + gboolean throws, + GError **error) +{ + ffi_cif cif; + ffi_type *rtype; + ffi_type **atypes; + GITypeInfo *tinfo; + GIArgInfo *ainfo; + gint n_args, n_invoke_args, in_pos, out_pos, i; + gpointer *args; + gboolean success = FALSE; + GError *local_error = NULL; + gpointer error_address = &local_error; + + tinfo = g_callable_info_get_return_type ((GICallableInfo *)info); + rtype = g_type_info_get_ffi_type (tinfo); + g_base_info_unref ((GIBaseInfo *)tinfo); + + in_pos = 0; + out_pos = 0; + + n_args = g_callable_info_get_n_args ((GICallableInfo *)info); + if (is_method) + { + if (n_in_args == 0) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling this)"); + goto out; + } + n_invoke_args = n_args+1; + in_pos++; + } + else + n_invoke_args = n_args; + + if (throws) + /* Add an argument for the GError */ + n_invoke_args ++; + + atypes = g_alloca (sizeof (ffi_type*) * n_invoke_args); + args = g_alloca (sizeof (gpointer) * n_invoke_args); + + if (is_method) + { + atypes[0] = &ffi_type_pointer; + args[0] = (gpointer) &in_args[0]; + } + for (i = 0; i < n_args; i++) + { + int offset = (is_method ? 1 : 0); + ainfo = g_callable_info_get_arg ((GICallableInfo *)info, i); + switch (g_arg_info_get_direction (ainfo)) + { + case GI_DIRECTION_IN: + tinfo = g_arg_info_get_type (ainfo); + atypes[i+offset] = g_type_info_get_ffi_type (tinfo); + g_base_info_unref ((GIBaseInfo *)tinfo); + + if (in_pos >= n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling in)"); + goto out; + } + + args[i+offset] = (gpointer)&in_args[in_pos]; + in_pos++; + + break; + case GI_DIRECTION_OUT: + atypes[i+offset] = &ffi_type_pointer; + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"out\" arguments (handling out)"); + goto out; + } + + args[i+offset] = (gpointer)&out_args[out_pos]; + out_pos++; + break; + case GI_DIRECTION_INOUT: + atypes[i+offset] = &ffi_type_pointer; + + if (in_pos >= n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments (handling inout)"); + goto out; + } + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"out\" arguments (handling inout)"); + goto out; + } + + args[i+offset] = (gpointer)&in_args[in_pos]; + in_pos++; + out_pos++; + break; + default: + g_assert_not_reached (); + } + g_base_info_unref ((GIBaseInfo *)ainfo); + } + + if (throws) + { + args[n_invoke_args - 1] = &error_address; + atypes[n_invoke_args - 1] = &ffi_type_pointer; + } + + if (in_pos < n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"in\" arguments (at end)"); + goto out; + } + if (out_pos < n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"out\" arguments (at end)"); + goto out; + } + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_invoke_args, rtype, atypes) != FFI_OK) + goto out; + + g_return_val_if_fail (return_value, FALSE); + ffi_call (&cif, function, return_value, args); + + if (local_error) + { + g_propagate_error (error, local_error); + success = FALSE; + } + else + { + success = TRUE; + } + out: + return success; +} |