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
This commit is contained in:
Tomeu Vizoso 2010-12-13 13:53:55 +01:00
parent 66f164c0ea
commit 95cf1ef4d6
5 changed files with 323 additions and 157 deletions

View File

@ -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;
}

View File

@ -26,7 +26,6 @@
#include <girepository.h>
#include "girepository-private.h"
#include "gitypelib-internal.h"
#include "girffi.h"
/**
* SECTION:gifunctioninfo
@ -249,20 +248,10 @@ g_function_info_invoke (GIFunctionInfo *info,
GIArgument *return_value,
GError **error)
{
ffi_cif cif;
ffi_type *rtype;
ffi_type **atypes;
const gchar *symbol;
gpointer func;
GITypeInfo *tinfo;
GIArgInfo *ainfo;
gboolean is_method;
gboolean throws;
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;
symbol = g_function_info_get_symbol (info);
@ -281,150 +270,14 @@ g_function_info_invoke (GIFunctionInfo *info,
&& (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0;
throws = g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
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, func, return_value, args);
if (local_error)
{
g_propagate_error (error, local_error);
success = FALSE;
}
else
{
success = TRUE;
}
out:
return success;
return _g_callable_info_invoke ((GICallableInfo*) info,
func,
in_args,
n_in_args,
out_args,
n_out_args,
return_value,
is_method,
throws,
error);
}

View File

@ -108,6 +108,17 @@ GIVFuncInfo * _g_base_info_find_vfunc (GIRealInfo *rinfo,
gint n_vfuncs,
const gchar *name);
gboolean _g_callable_info_invoke (GICallableInfo *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);
extern ffi_status ffi_prep_closure_loc (ffi_closure *,
ffi_cif *,
void (*fun)(ffi_cif *, void *, void **, void *),

View File

@ -196,3 +196,122 @@ g_vfunc_info_get_invoker (GIVFuncInfo *info)
g_assert_not_reached ();
}
/**
* g_vfunc_info_get_address:
* @info: a #GIVFuncInfo
* @implementor_gtype: #GType implementing this virtual function
* @error: return location for a #GError
*
* This method will look up where inside the type struct of @implementor_gtype
* is the implementation for @info.
*
* Returns: address to a function or %NULL if an error happened
*/
gpointer
g_vfunc_info_get_address (GIVFuncInfo *vfunc_info,
GType implementor_gtype,
GError **error)
{
GIObjectInfo *object_info;
GIStructInfo *struct_info;
GIFieldInfo *field_info = NULL;
int length, i, offset;
gpointer implementor_vtable, func;
object_info = (GIObjectInfo *) g_base_info_get_container (vfunc_info);
struct_info = g_object_info_get_class_struct (object_info);
length = g_struct_info_get_n_fields (struct_info);
for (i = 0; i < length; i++)
{
field_info = g_struct_info_get_field (struct_info, i);
if (strcmp (g_base_info_get_name ( (GIBaseInfo*) field_info),
g_base_info_get_name ( (GIBaseInfo*) vfunc_info)) != 0) {
g_base_info_unref (field_info);
field_info = NULL;
continue;
}
break;
}
if (field_info == NULL)
{
g_set_error (error,
G_INVOKE_ERROR,
G_INVOKE_ERROR_SYMBOL_NOT_FOUND,
"Couldn't find struct field for this vfunc");
return NULL;
}
implementor_vtable = g_type_class_ref (implementor_gtype);
offset = g_field_info_get_offset (field_info);
func = *(gpointer*) G_STRUCT_MEMBER_P (implementor_vtable, offset);
g_type_class_unref (implementor_vtable);
if (func == NULL)
{
g_set_error (error,
G_INVOKE_ERROR,
G_INVOKE_ERROR_SYMBOL_NOT_FOUND,
"Class %s doesn't implement %s",
g_type_name (implementor_gtype),
g_base_info_get_name ( (GIBaseInfo*) vfunc_info));
return NULL;
}
return func;
}
/**
* g_vfunc_info_invoke: (skip)
* @info: a #GIVFuncInfo describing the virtual function to invoke
* @implementor: #GType of the type that implements this virtual function
* @in_args: an array of #GIArgument<!-- -->s, one for each in
* parameter of @info. If there are no in parameter, @in_args
* can be %NULL
* @n_in_args: the length of the @in_args array
* @out_args: an array of #GIArgument<!-- -->s, one for each out
* parameter of @info. If there are no out parameters, @out_args
* may be %NULL
* @n_out_args: the length of the @out_args array
* @return_value: return location for the return value of the
* function. If the function returns void, @return_value may be
* %NULL
* @error: return location for detailed error information, or %NULL
*
* Invokes the function described in @info with the given
* arguments. Note that inout parameters must appear in both
* argument lists.
*
* Returns: %TRUE if the function has been invoked, %FALSE if an
* error occurred.
*/
gboolean
g_vfunc_info_invoke (GIVFuncInfo *info,
GType implementor,
const GIArgument *in_args,
int n_in_args,
const GIArgument *out_args,
int n_out_args,
GIArgument *return_value,
GError **error)
{
gpointer func;
func = g_vfunc_info_get_address (info, implementor, error);
if (*error != NULL)
return FALSE;
return _g_callable_info_invoke ((GICallableInfo*) info,
func,
in_args,
n_in_args,
out_args,
n_out_args,
return_value,
TRUE,
FALSE,
error);
}

View File

@ -37,6 +37,17 @@ GIVFuncInfoFlags g_vfunc_info_get_flags (GIVFuncInfo *info);
gint g_vfunc_info_get_offset (GIVFuncInfo *info);
GISignalInfo * g_vfunc_info_get_signal (GIVFuncInfo *info);
GIFunctionInfo * g_vfunc_info_get_invoker (GIVFuncInfo *info);
gpointer g_vfunc_info_get_address (GIVFuncInfo *info,
GType implementor_gtype,
GError **error);
gboolean g_vfunc_info_invoke (GIVFuncInfo *info,
GType implementor,
const GIArgument *in_args,
int n_in_args,
const GIArgument *out_args,
int n_out_args,
GIArgument *return_value,
GError **error);
G_END_DECLS