diff --git a/gicallableinfo.c b/gicallableinfo.c index 28043fa52..fec77b820 100644 --- a/gicallableinfo.c +++ b/gicallableinfo.c @@ -360,6 +360,79 @@ g_callable_info_iterate_return_attributes (GICallableInfo *info, return TRUE; } +/* Extract the correct bits from an ffi_arg return value into + * GIArgument: https://bugzilla.gnome.org/show_bug.cgi?id=665152 + * + * Also see the ffi_call man page - the storage requirements for return + * values are "special". + */ +typedef GIArgument GIFFIReturnValue; + +static void +set_gargument_from_ffi_return_value (GITypeInfo *return_info, + GIArgument *arg, + GIFFIReturnValue *ffi_value) +{ + switch (g_type_info_get_tag (return_info)) { + case GI_TYPE_TAG_INT8: + arg->v_int8 = (gint8) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT8: + arg->v_uint8 = (guint8) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT16: + arg->v_int16 = (gint16) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT16: + arg->v_uint16 = (guint16) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT32: + arg->v_int32 = (gint32) ffi_value->v_long; + break; + case GI_TYPE_TAG_UINT32: + case GI_TYPE_TAG_BOOLEAN: + case GI_TYPE_TAG_UNICHAR: + arg->v_uint32 = (guint32) ffi_value->v_ulong; + break; + case GI_TYPE_TAG_INT64: + arg->v_int64 = (gint64) ffi_value->v_int64; + break; + case GI_TYPE_TAG_UINT64: + arg->v_uint64 = (guint64) ffi_value->v_uint64; + break; + case GI_TYPE_TAG_FLOAT: + arg->v_float = ffi_value->v_float; + break; + case GI_TYPE_TAG_DOUBLE: + arg->v_double = ffi_value->v_double; + break; + case GI_TYPE_TAG_INTERFACE: + { + GIBaseInfo* interface_info; + GIInfoType interface_type; + + interface_info = g_type_info_get_interface(return_info); + interface_type = g_base_info_get_type(interface_info); + + switch(interface_type) { + case GI_INFO_TYPE_ENUM: + case GI_INFO_TYPE_FLAGS: + arg->v_int32 = (gint32) ffi_value->v_long; + break; + default: + arg->v_pointer = (gpointer) ffi_value->v_ulong; + break; + } + + g_base_info_unref(interface_info); + } + break; + default: + arg->v_pointer = (gpointer) ffi_value->v_ulong; + break; + } +} + gboolean _g_callable_info_invoke (GIFunctionInfo *info, gpointer function, @@ -376,16 +449,20 @@ _g_callable_info_invoke (GIFunctionInfo *info, ffi_type *rtype; ffi_type **atypes; GITypeInfo *tinfo; + GITypeInfo *rinfo; + GITypeTag rtag; 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; + GIFFIReturnValue ffi_return_value; + gpointer return_value_p; /* Will point inside the union return_value */ - tinfo = g_callable_info_get_return_type ((GICallableInfo *)info); - rtype = g_type_info_get_ffi_type (tinfo); - g_base_info_unref ((GIBaseInfo *)tinfo); + rinfo = g_callable_info_get_return_type ((GICallableInfo *)info); + rtype = g_type_info_get_ffi_type (rinfo); + rtag = g_type_info_get_tag(rinfo); in_pos = 0; out_pos = 0; @@ -516,7 +593,23 @@ _g_callable_info_invoke (GIFunctionInfo *info, goto out; g_return_val_if_fail (return_value, FALSE); - ffi_call (&cif, function, return_value, args); + /* See comment for GIFFIReturnValue above */ + switch (rtag) + { + case GI_TYPE_TAG_FLOAT: + return_value_p = &ffi_return_value.v_float; + break; + case GI_TYPE_TAG_DOUBLE: + return_value_p = &ffi_return_value.v_double; + break; + case GI_TYPE_TAG_INT64: + case GI_TYPE_TAG_UINT64: + return_value_p = &ffi_return_value.v_uint64; + break; + default: + return_value_p = &ffi_return_value.v_long; + } + ffi_call (&cif, function, return_value_p, args); if (local_error) { @@ -525,8 +618,10 @@ _g_callable_info_invoke (GIFunctionInfo *info, } else { + set_gargument_from_ffi_return_value(rinfo, return_value, &ffi_return_value); success = TRUE; } out: + g_base_info_unref ((GIBaseInfo *)rinfo); return success; } diff --git a/ginvoke.c b/ginvoke.c index 84e1e748f..581956a36 100644 --- a/ginvoke.c +++ b/ginvoke.c @@ -88,57 +88,121 @@ value_to_ffi_type (const GValue *gvalue, gpointer *value) return rettype; } -static void -value_from_ffi_type (GValue *gvalue, gpointer *value) +/* See comment aboe set_gargument_from_ffi_return_value() */ +static ffi_type * +g_value_to_ffi_return_type (const GValue *gvalue, + const GIArgument *ffi_value, + gpointer *value) { - switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) - { - case G_TYPE_INT: - g_value_set_int (gvalue, *(gint*)value); + ffi_type *rettype = NULL; + GType type = g_type_fundamental (G_VALUE_TYPE (gvalue)); + g_assert (type != G_TYPE_INVALID); + + *value = (gpointer)&(ffi_value->v_long); + + switch (type) { + case G_TYPE_CHAR: + rettype = &ffi_type_sint8; + break; + case G_TYPE_UCHAR: + rettype = &ffi_type_uint8; + break; + case G_TYPE_BOOLEAN: + case G_TYPE_INT: + rettype = &ffi_type_sint; + break; + case G_TYPE_UINT: + rettype = &ffi_type_uint; + break; + case G_TYPE_STRING: + case G_TYPE_OBJECT: + case G_TYPE_BOXED: + case G_TYPE_POINTER: + rettype = &ffi_type_pointer; + break; + case G_TYPE_FLOAT: + rettype = &ffi_type_float; + *value = (gpointer)&(ffi_value->v_float); + break; + case G_TYPE_DOUBLE: + rettype = &ffi_type_double; + *value = (gpointer)&(ffi_value->v_double); + break; + case G_TYPE_LONG: + rettype = &ffi_type_slong; + break; + case G_TYPE_ULONG: + rettype = &ffi_type_ulong; + break; + case G_TYPE_INT64: + rettype = &ffi_type_sint64; + *value = (gpointer)&(ffi_value->v_int64); + break; + case G_TYPE_UINT64: + rettype = &ffi_type_uint64; + *value = (gpointer)&(ffi_value->v_uint64); + break; + default: + rettype = &ffi_type_pointer; + *value = NULL; + g_warning ("Unsupported fundamental type: %s", g_type_name (type)); + break; + } + return rettype; +} + +static void +g_value_from_ffi_value (GValue *gvalue, + const GIArgument *value) +{ + switch (g_type_fundamental (G_VALUE_TYPE (gvalue))) { + case G_TYPE_INT: + g_value_set_int (gvalue, (gint)value->v_long); break; - case G_TYPE_FLOAT: - g_value_set_float (gvalue, *(gfloat*)value); + case G_TYPE_FLOAT: + g_value_set_float (gvalue, (gfloat)value->v_float); break; - case G_TYPE_DOUBLE: - g_value_set_double (gvalue, *(gdouble*)value); + case G_TYPE_DOUBLE: + g_value_set_double (gvalue, (gdouble)value->v_double); break; - case G_TYPE_BOOLEAN: - g_value_set_boolean (gvalue, *(gboolean*)value); + case G_TYPE_BOOLEAN: + g_value_set_boolean (gvalue, (gboolean)value->v_long); break; - case G_TYPE_STRING: - g_value_set_string (gvalue, *(gchar**)value); + case G_TYPE_STRING: + g_value_set_string (gvalue, (gchar*)value->v_pointer); break; - case G_TYPE_CHAR: - g_value_set_char (gvalue, *(gchar*)value); + case G_TYPE_CHAR: + g_value_set_char (gvalue, (gchar)value->v_long); break; - case G_TYPE_UCHAR: - g_value_set_uchar (gvalue, *(guchar*)value); + case G_TYPE_UCHAR: + g_value_set_uchar (gvalue, (guchar)value->v_ulong); break; - case G_TYPE_UINT: - g_value_set_uint (gvalue, *(guint*)value); + case G_TYPE_UINT: + g_value_set_uint (gvalue, (guint)value->v_ulong); break; - case G_TYPE_POINTER: - g_value_set_pointer (gvalue, *(gpointer*)value); + case G_TYPE_POINTER: + g_value_set_pointer (gvalue, (gpointer)value->v_pointer); break; - case G_TYPE_LONG: - g_value_set_long (gvalue, *(glong*)value); + case G_TYPE_LONG: + g_value_set_long (gvalue, (glong)value->v_long); break; - case G_TYPE_ULONG: - g_value_set_ulong (gvalue, *(gulong*)value); + case G_TYPE_ULONG: + g_value_set_ulong (gvalue, (gulong)value->v_ulong); break; - case G_TYPE_INT64: - g_value_set_int64 (gvalue, *(gint64*)value); + case G_TYPE_INT64: + g_value_set_int64 (gvalue, (gint64)value->v_int64); break; - case G_TYPE_UINT64: - g_value_set_uint64 (gvalue, *(guint64*)value); + case G_TYPE_UINT64: + g_value_set_uint64 (gvalue, (guint64)value->v_uint64); break; - case G_TYPE_BOXED: - g_value_set_boxed (gvalue, *(gpointer*)value); + case G_TYPE_BOXED: + g_value_set_boxed (gvalue, (gpointer)value->v_pointer); break; - default: - g_warning ("Unsupported fundamental type: %s", - g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue)))); - } + default: + g_warning ("Unsupported fundamental type: %s", + g_type_name (g_type_fundamental (G_VALUE_TYPE (gvalue)))); + } + } void @@ -149,6 +213,7 @@ gi_cclosure_marshal_generic (GClosure *closure, gpointer invocation_hint, gpointer marshal_data) { + GIArgument return_ffi_value; ffi_type *rtype; void *rvalue; int n_args; @@ -160,15 +225,16 @@ gi_cclosure_marshal_generic (GClosure *closure, if (return_gvalue && G_VALUE_TYPE (return_gvalue)) { + rtype = g_value_to_ffi_return_type (return_gvalue, &return_ffi_value, + &rvalue); rtype = value_to_ffi_type (return_gvalue, &rvalue); } else { rtype = &ffi_type_void; + rvalue = &return_ffi_value.v_long; } - rvalue = g_alloca (MAX (rtype->size, sizeof (ffi_arg))); - n_args = n_param_values + 1; atypes = g_alloca (sizeof (ffi_type *) * n_args); args = g_alloca (sizeof (gpointer) * n_args); @@ -204,5 +270,5 @@ gi_cclosure_marshal_generic (GClosure *closure, ffi_call (&cif, marshal_data ? marshal_data : cc->callback, rvalue, args); if (return_gvalue && G_VALUE_TYPE (return_gvalue)) - value_from_ffi_type (return_gvalue, rvalue); + g_value_from_ffi_value (return_gvalue, &return_ffi_value); }