diff options
author | Colin Walters <walters@verbum.org> | 2012-01-04 14:05:04 -0500 |
---|---|---|
committer | Colin Walters <walters@verbum.org> | 2012-01-04 16:33:34 -0500 |
commit | dc69b12d5e44f9d3b209759082f721237a8c9a06 (patch) | |
tree | bd86c278cc45ebcd15dfb77def589940676c2c65 | |
parent | 4cecec9c4f8365eef0e5d9271b9ba0ce3e3ba012 (diff) | |
download | gjs-dc69b12d5e44f9d3b209759082f721237a8c9a06.tar.gz |
function: Fix ffi return value handling on 32 bit
We had two major problems in marshaling. One is that our conversion
routine took an unsigned ffi_arg value, which we put signed values
into. Also, our fallback case was copying from ffi_arg (which is
always 64 bit on architectures we support) into the pointer member of
the GIArgument union, but on 32 bit architecture pointers are just 32
bit.
Fix both of these by creating a new union, and then passing in a
pointer to the correct member of it for the given return value type
we're supplying to ffi. We then need to again convert from this union
type into a GIArgument.
Debugged with lots of help from Ray Strode <rstrode@redhat.com>
https://bugzilla.gnome.org/show_bug.cgi?id=665152
-rw-r--r-- | gi/function.c | 68 |
1 files changed, 52 insertions, 16 deletions
diff --git a/gi/function.c b/gi/function.c index aedc0fd5..9fecf860 100644 --- a/gi/function.c +++ b/gi/function.c @@ -155,6 +155,7 @@ set_return_ffi_arg_from_giargument (GITypeInfo *ret_type, switch (g_type_info_get_tag(ret_type)) { case GI_TYPE_TAG_INT8: *(ffi_sarg *) result = return_value->v_int8; + break; case GI_TYPE_TAG_UINT8: *(ffi_arg *) result = return_value->v_uint8; break; @@ -198,7 +199,7 @@ set_return_ffi_arg_from_giargument (GITypeInfo *ret_type, } } default: - *(ffi_arg *) result = (ffi_arg) return_value->v_pointer; + *(ffi_arg *) result = (ffi_arg) return_value->v_uint64; break; } } @@ -367,33 +368,57 @@ get_length_from_arg (GArgument *arg, GITypeTag tag) /* 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". */ +union GjsFFIReturnValue { + ffi_arg rv_ffi_arg; + ffi_sarg rv_ffi_sarg; + guint64 rv_u64; + gint64 rv_s64; + float rv_float; + double rv_double; +}; static void -set_gargument_from_ffi_return_value (GITypeInfo *return_info, - GIArgument *return_value, - ffi_arg value) +set_gargument_from_ffi_return_value (GITypeInfo *return_info, + GIArgument *return_value, + union GjsFFIReturnValue *value) { switch (g_type_info_get_tag (return_info)) { case GI_TYPE_TAG_INT8: - return_value->v_int8 = (gint8) value; + return_value->v_int8 = (gint8) value->rv_ffi_sarg; + break; case GI_TYPE_TAG_UINT8: - return_value->v_uint8 = (guint8) value; + return_value->v_uint8 = (guint8) value->rv_ffi_arg; break; case GI_TYPE_TAG_INT16: - return_value->v_int16 = (gint16) value; + return_value->v_int16 = (gint16) value->rv_ffi_sarg; break; case GI_TYPE_TAG_UINT16: - return_value->v_uint16 = (guint16) value; + return_value->v_uint16 = (guint16) value->rv_ffi_arg; break; case GI_TYPE_TAG_INT32: - return_value->v_int32 = (gint32) value; + return_value->v_int32 = (gint32) value->rv_ffi_sarg; break; case GI_TYPE_TAG_UINT32: case GI_TYPE_TAG_BOOLEAN: case GI_TYPE_TAG_UNICHAR: - return_value->v_uint32 = (guint32) value; + return_value->v_uint32 = (guint32) value->rv_ffi_arg; break; + case GI_TYPE_TAG_INT64: + return_value->v_int64 = (gint64) value->rv_s64; + break; + case GI_TYPE_TAG_UINT64: + return_value->v_uint64 = (guint64) value->rv_u64; + break; + case GI_TYPE_TAG_FLOAT: + return_value->v_float = value->rv_float; + break; + case GI_TYPE_TAG_DOUBLE: + return_value->v_double = value->rv_double; + break; case GI_TYPE_TAG_INTERFACE: { GIBaseInfo* interface_info; @@ -405,16 +430,16 @@ set_gargument_from_ffi_return_value (GITypeInfo *return_info, switch(interface_type) { case GI_INFO_TYPE_ENUM: case GI_INFO_TYPE_FLAGS: - return_value->v_int32 = (gint32) value; + return_value->v_int32 = (gint32) value->rv_ffi_sarg; break; default: - return_value->v_pointer = (gpointer) value; + return_value->v_pointer = (gpointer) value->rv_ffi_arg; break; } } break; default: - return_value->v_pointer = (gpointer) value; + return_value->v_pointer = (gpointer) value->rv_ffi_arg; break; } } @@ -443,7 +468,8 @@ gjs_invoke_c_function(JSContext *context, GArgument *out_arg_cvalues; GArgument *inout_original_arg_cvalues; gpointer *ffi_arg_pointers; - ffi_arg return_value; + union GjsFFIReturnValue return_value; + gpointer return_value_p; /* Will point inside the union return_value */ GArgument return_gargument; guint8 processed_c_args = 0; @@ -760,7 +786,17 @@ gjs_invoke_c_function(JSContext *context, g_assert_cmpuint(c_arg_pos, ==, c_argc); g_assert_cmpuint(gi_arg_pos, ==, gi_argc); - ffi_call(&(function->invoker.cif), function->invoker.native_address, &return_value, ffi_arg_pointers); + + /* See comment for GjsFFIReturnValue above */ + if (return_tag == GI_TYPE_TAG_FLOAT) + return_value_p = &return_value.rv_float; + else if (return_tag == GI_TYPE_TAG_DOUBLE) + return_value_p = &return_value.rv_double; + else if (return_tag == GI_TYPE_TAG_INT64 || return_tag == GI_TYPE_TAG_UINT64) + return_value_p = &return_value.rv_u64; + else + return_value_p = &return_value.rv_ffi_arg; + ffi_call(&(function->invoker.cif), function->invoker.native_address, return_value_p, ffi_arg_pointers); gjs_runtime_pop_context(JS_GetRuntime(context)); @@ -788,7 +824,7 @@ gjs_invoke_c_function(JSContext *context, g_assert_cmpuint(next_rval, <, function->js_out_argc); - set_gargument_from_ffi_return_value(&return_info, &return_gargument, return_value); + set_gargument_from_ffi_return_value(&return_info, &return_gargument, &return_value); array_length_pos = g_type_info_get_array_length(&return_info); if (array_length_pos >= 0) { |