summaryrefslogtreecommitdiff
path: root/girepository/gicallableinfo.c
diff options
context:
space:
mode:
Diffstat (limited to 'girepository/gicallableinfo.c')
-rw-r--r--girepository/gicallableinfo.c172
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;
+}