The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
GObjectIntrospection.xs 2661
MANIFEST 01
META.json 23
META.yml 1112
Makefile.PL 01
NEWS 06
gperl-i11n-enums.c 44
gperl-i11n-field.c 44
gperl-i11n-invoke-c.c 16398
gperl-i11n-invoke-perl.c 68109
gperl-i11n-invoke.c 0112
gperl-i11n-marshal-arg.c 12
gperl-i11n-marshal-array.c 611
gperl-i11n-marshal-callback.c 11
gperl-i11n-marshal-interface.c 8103
lib/Glib/Object/Introspection.pm 11
t/arg-checks.t 15
17 files changed (This is a version diff) 296534
@@ -82,52 +82,80 @@ typedef struct {
 	gint length_pos;
 } GPerlI11nArrayInfo;
 
-/* This stores information that the different marshallers might need to
- * communicate to each other.  This struct is used for invoking C and Perl
- * code. */
+/* The next three structs store information that the different marshallers
+ * might need to communicate to each other.  This struct is the basis used for
+ * invoking C and Perl code. */
 typedef struct {
 	GICallableInfo *interface;
-	const gchar *target_package;
-	const gchar *target_namespace;
-	const gchar *target_function;
 
 	gboolean is_function;
 	gboolean is_vfunc;
 	gboolean is_callback;
 	gboolean is_signal;
 
+	/* The number of args described by the typelib. */
 	guint n_args;
-	guint n_invoke_args;
-	guint n_expected_args;
-	guint n_nullable_args;
-	guint n_given_args;
+
+	/* The current position under investigation in the list of typelib
+	 * args. */
+	guint current_pos;
+
+	/* Information about the args from the typelib. */
+	GIArgInfo ** arg_infos;
+	GITypeInfo ** arg_types;
+
+	/* An array of places for storing out out/in-out or automatic args. */
+	GIArgument * aux_args;
+
+	gboolean has_return_value;
+	ffi_type * return_type_ffi;
+	GITypeInfo * return_type_info;
+	GITransfer return_type_transfer;
+
+	GSList * callback_infos;
+	GSList * array_infos;
+
+	GSList * free_after_call;
+} GPerlI11nInvocationInfo;
+
+/* This struct is used when invoking C code. */
+typedef struct {
+	GPerlI11nInvocationInfo base;
+
+	const gchar *target_package;
+	const gchar *target_namespace;
+	const gchar *target_function;
+
 	gboolean is_constructor;
 	gboolean is_method;
 	gboolean throws;
 
+	/* The number of args that need to be given to the C function. */
+	guint n_invoke_args;
+	/* The number of args for which no value is required. */
+	guint n_nullable_args;
+	/* The number of necessary args, i.e. those that are not automatic or
+	 * nullable. */
+	guint n_expected_args;
+	/* The number of args given by the caller. */
+	guint n_given_args;
+
 	gpointer * args;
-	ffi_type ** arg_types;
+	ffi_type ** arg_types_ffi;
 	GIArgument * in_args;
 	GIArgument * out_args;
-	GITypeInfo ** out_arg_infos;
-	GIArgument * aux_args;
 	gboolean * is_automatic_arg;
 
-	gboolean has_return_value;
-	ffi_type * return_type_ffi;
-	GITypeInfo * return_type_info;
-	GITransfer return_type_transfer;
-
-	guint current_pos;
+	guint constructor_offset;
 	guint method_offset;
 	guint stack_offset;
 	gint dynamic_stack_offset;
+} GPerlI11nCInvocationInfo;
 
-	GSList * callback_infos;
-	GSList * free_after_call;
-
-	GSList * array_infos;
-} GPerlI11nInvocationInfo;
+/* This struct is used when invoking Perl code. */
+typedef struct {
+	GPerlI11nInvocationInfo base;
+} GPerlI11nPerlInvocationInfo;
 
 /* callbacks */
 static GPerlI11nPerlCallbackInfo * create_perl_callback_closure_for_named_sub (GIBaseInfo *cb_info, gchar *sub_name);
@@ -140,6 +168,14 @@ static void attach_c_callback_data (GPerlI11nCCallbackInfo *info, gpointer data)
 static void release_c_callback (gpointer data);
 
 /* invocation */
+static void prepare_invocation_info (GPerlI11nInvocationInfo *iinfo,
+                                     GICallableInfo *info);
+static void clear_invocation_info (GPerlI11nInvocationInfo *iinfo);
+
+static void free_after_call (GPerlI11nInvocationInfo *iinfo,
+                             GFunc func, gpointer data);
+static void invoke_free_after_call_handlers (GPerlI11nInvocationInfo *iinfo);
+
 #if GI_CHECK_VERSION (1, 33, 10)
 static void invoke_perl_signal_handler (ffi_cif* cif,
                                         gpointer resp,
@@ -159,8 +195,6 @@ static void invoke_c_code (GICallableInfo *info,
                            const gchar *package,
                            const gchar *namespace,
                            const gchar *function);
-static void free_after_call (GPerlI11nInvocationInfo *iinfo,
-                             GFunc func, gpointer data);
 
 /* info finders */
 static GIFunctionInfo * get_function_info (GIRepository *repository,
@@ -283,6 +317,7 @@ static void call_carp_carp (const char *msg);
 #include "gperl-i11n-field.c"
 #include "gperl-i11n-gvalue.c"
 #include "gperl-i11n-info.c"
+#include "gperl-i11n-invoke.c"
 #include "gperl-i11n-invoke-c.c"
 #include "gperl-i11n-invoke-perl.c"
 #include "gperl-i11n-marshal-arg.c"
@@ -5,6 +5,7 @@ gperl-i11n-enums.c
 gperl-i11n-field.c
 gperl-i11n-gvalue.c
 gperl-i11n-info.c
+gperl-i11n-invoke.c
 gperl-i11n-invoke-c.c
 gperl-i11n-invoke-perl.c
 gperl-i11n-marshal-arg.c
@@ -4,7 +4,7 @@
       "Glib::Object::Introspection Team <gtk-perl-list at gnome dot org>"
    ],
    "dynamic_config" : 1,
-   "generated_by" : "ExtUtils::MakeMaker version 6.86, CPAN::Meta::Converter version 2.133380",
+   "generated_by" : "ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520",
    "license" : [
       "lgpl_2_1"
    ],
@@ -63,7 +63,8 @@
          "url" : "git://git.gnome.org/perl-Glib-Object-Introspection",
          "web" : "http://git.gnome.org/browse/perl-Glib-Object-Introspection"
       },
+      "x_IRC" : "irc://irc.gimp.org/#gtk-perl",
       "x_MailingList" : "https://mail.gnome.org/mailman/listinfo/gtk-perl-list"
    },
-   "version" : "0.024"
+   "version" : "0.025"
 }
@@ -3,18 +3,18 @@ abstract: 'Dynamically create Perl language bindings'
 author:
   - 'Glib::Object::Introspection Team <gtk-perl-list at gnome dot org>'
 build_requires:
-  ExtUtils::MakeMaker: 0
+  ExtUtils::MakeMaker: '0'
 configure_requires:
-  ExtUtils::Depends: 0.3
-  ExtUtils::MakeMaker: 0
-  ExtUtils::PkgConfig: 1
-  Glib: 1.28
+  ExtUtils::Depends: '0.3'
+  ExtUtils::MakeMaker: '0'
+  ExtUtils::PkgConfig: '1'
+  Glib: '1.28'
 dynamic_config: 1
-generated_by: 'ExtUtils::MakeMaker version 6.86, CPAN::Meta::Converter version 2.133380'
+generated_by: 'ExtUtils::MakeMaker version 6.98, CPAN::Meta::Converter version 2.141520'
 license: lgpl
 meta-spec:
   url: http://module-build.sourceforge.net/META-spec-v1.4.html
-  version: 1.4
+  version: '1.4'
 name: Glib-Object-Introspection
 no_index:
   directory:
@@ -26,13 +26,14 @@ no_index:
     - MY
     - Glib::Object::Introspection::_FuncWrapper
 requires:
-  ExtUtils::Depends: 0.3
-  ExtUtils::PkgConfig: 1
-  Glib: 1.28
+  ExtUtils::Depends: '0.3'
+  ExtUtils::PkgConfig: '1'
+  Glib: '1.28'
 resources:
+  IRC: irc://irc.gimp.org/#gtk-perl
   MailingList: https://mail.gnome.org/mailman/listinfo/gtk-perl-list
   bugtracker: http://rt.cpan.org/Public/Dist/Display.html?Name=Glib-Object-Introspection
   homepage: http://gtk2-perl.sourceforge.net
   license: http://www.gnu.org/licenses/lgpl-2.1.html
   repository: git://git.gnome.org/perl-Glib-Object-Introspection
-version: 0.024
+version: '0.025'
@@ -62,6 +62,7 @@ my %meta_merge = (
             homepage    => 'http://gtk2-perl.sourceforge.net',
             x_MailingList =>
                 'https://mail.gnome.org/mailman/listinfo/gtk-perl-list',
+            x_IRC => "irc://irc.gimp.org/#gtk-perl",
             bugtracker  => {
                     web     =>
                     'http://rt.cpan.org/Public/Dist/Display.html?Name=Glib-Object-Introspection',
@@ -1,3 +1,9 @@
+Overview of changes in Glib::Object::Introspection 0.025
+========================================================
+
+* Fix many argument conversion bugs on 64bit big-endian architectures.
+* Added 'x_IRC' metadata tag so MetaCPAN displays a link to the IRC channel
+
 Overview of changes in Glib::Object::Introspection 0.024
 ========================================================
 
@@ -1,10 +1,10 @@
 /* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
 
-#define FILL_VALUES(values) \
+#define FILL_VALUES(values, value_type)                                 \
 	{ gint i; \
 	for (i = 0; i < n_values; i++) { \
 		GIValueInfo *value_info = g_enum_info_get_value (info, i); \
-		(values)[i].value = g_value_info_get_value (value_info); \
+		(values)[i].value = (value_type) g_value_info_get_value (value_info); \
 		/* FIXME: Can we assume that the strings will stick around long enough? */ \
 		(values)[i].value_nick = g_base_info_get_name (value_info); \
 		(values)[i].value_name = g_base_info_get_attribute (value_info, "c:identifier"); \
@@ -37,10 +37,10 @@ register_unregistered_enum (GIEnumInfo *info)
 	n_values = g_enum_info_get_n_values (info);
 	if (info_type == GI_INFO_TYPE_ENUM) {
 		values = g_new0 (GEnumValue, n_values+1); /* zero-terminated */
-		FILL_VALUES ((GEnumValue *) values);
+		FILL_VALUES ((GEnumValue *) values, gint);
 	} else {
 		values = g_new0 (GFlagsValue, n_values+1); /* zero-terminated */
-		FILL_VALUES ((GFlagsValue *) values);
+		FILL_VALUES ((GFlagsValue *) values, guint);
 	}
 
 	if (info_type == GI_INFO_TYPE_ENUM) {
@@ -67,7 +67,7 @@ get_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer)
 	    g_type_info_get_tag (field_type) == GI_TYPE_TAG_INTERFACE &&
 	    g_base_info_get_type (interface_info) == GI_INFO_TYPE_STRUCT)
 	{
-		gsize offset;
+		gint offset;
 		offset = g_field_info_get_offset (field_info);
 		value.v_pointer = mem + offset;
 		sv = arg_to_sv (&value,
@@ -114,9 +114,9 @@ set_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer, SV *sv)
 		/* FIXME: No GIArgInfo and no GPerlI11nInvocationInfo here.
 		 * What if the struct contains an object pointer, or a callback
 		 * field? */
-		gsize offset = g_field_info_get_offset (field_info);
+		gint offset = g_field_info_get_offset (field_info);
 		if (!g_type_info_is_pointer (field_type)) {	/* By value */
-			gssize size;
+			gsize size;
 			/* Enforce GI_TRANSFER_NOTHING since we will copy into
 			 * the memory that has already been allocated inside
 			 * 'mem' */
@@ -162,7 +162,7 @@ set_field (GIFieldInfo *field_info, gpointer mem, GITransfer transfer, SV *sv)
 	else if (tag == GI_TYPE_TAG_VOID &&
 	         g_type_info_is_pointer (field_type))
 	{
-		gsize offset = g_field_info_get_offset (field_info);
+		gint offset = g_field_info_get_offset (field_info);
 		sv_to_arg (sv, &arg, NULL, field_type,
 		           transfer, TRUE, NULL);
 		G_STRUCT_MEMBER (gpointer, mem, offset) = arg.v_pointer;
@@ -1,19 +1,20 @@
 /* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
 
-static void _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
+static void _prepare_c_invocation_info (GPerlI11nCInvocationInfo *iinfo,
                                         GICallableInfo *info,
                                         IV items,
                                         UV internal_stack_offset,
                                         const gchar *package,
                                         const gchar *namespace,
                                         const gchar *function);
-static void _clear_c_invocation_info (GPerlI11nInvocationInfo *iinfo);
-static void _check_n_args (GPerlI11nInvocationInfo *iinfo);
+static void _clear_c_invocation_info (GPerlI11nCInvocationInfo *iinfo);
+static void _check_n_args (GPerlI11nCInvocationInfo *iinfo);
 static void _handle_automatic_arg (guint pos,
+                                   GIArgInfo * arg_info,
+                                   GITypeInfo * arg_type,
                                    GIArgument * arg,
-                                   GPerlI11nInvocationInfo * invocation_info);
+                                   GPerlI11nCInvocationInfo * invocation_info);
 static gpointer _allocate_out_mem (GITypeInfo *arg_type);
-static void _invoke_free_after_call_handlers (GPerlI11nInvocationInfo *iinfo);
 
 static void
 invoke_c_code (GICallableInfo *info,
@@ -27,7 +28,7 @@ invoke_c_code (GICallableInfo *info,
 	ffi_cif cif;
 	gpointer instance = NULL;
 	guint i;
-	GPerlI11nInvocationInfo iinfo = {0,};
+	GPerlI11nCInvocationInfo iinfo;
 	guint n_return_values;
 #if GI_CHECK_VERSION (1, 32, 0)
 	GIFFIReturnValue ffi_return_value;
@@ -46,7 +47,7 @@ invoke_c_code (GICallableInfo *info,
 
 	if (iinfo.is_method) {
 		instance = instance_sv_to_pointer (info, ST (0 + iinfo.stack_offset));
-		iinfo.arg_types[0] = &ffi_type_pointer;
+		iinfo.arg_types_ffi[0] = &ffi_type_pointer;
 		iinfo.args[0] = &instance;
 	}
 
@@ -54,7 +55,7 @@ invoke_c_code (GICallableInfo *info,
 	 * --- handle arguments -----------------------------------------------
 	 */
 
-	for (i = 0 ; i < iinfo.n_args ; i++) {
+	for (i = 0 ; i < iinfo.base.n_args ; i++) {
 		GIArgInfo * arg_info;
 		GITypeInfo * arg_type;
 		GITransfer transfer;
@@ -62,28 +63,28 @@ invoke_c_code (GICallableInfo *info,
 		gint perl_stack_pos, ffi_stack_pos;
 		SV *current_sv;
 
-		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
-		/* In case of out and in-out args, arg_type is unref'ed after
-		 * the function has been invoked */
-		arg_type = g_arg_info_get_type (arg_info);
+		arg_info = iinfo.base.arg_infos[i];
+		arg_type = iinfo.base.arg_types[i];
 		transfer = g_arg_info_get_ownership_transfer (arg_info);
 		may_be_null = g_arg_info_may_be_null (arg_info);
 #if GI_CHECK_VERSION (1, 29, 0)
 		is_skipped = g_arg_info_is_skip (arg_info);
 #endif
-		perl_stack_pos = i
-                               + iinfo.method_offset
-                               + iinfo.stack_offset
-                               + iinfo.dynamic_stack_offset;
-		ffi_stack_pos = i
-		              + iinfo.method_offset;
+		perl_stack_pos = (gint) i
+		               + (gint) iinfo.constructor_offset
+		               + (gint) iinfo.method_offset
+		               + (gint) iinfo.stack_offset
+		               + iinfo.dynamic_stack_offset;
+		ffi_stack_pos = (gint) i
+		              + (gint) iinfo.method_offset;
+		g_assert (perl_stack_pos >= 0 && ffi_stack_pos >= 0);
 
 		/* FIXME: Is this right?  I'm confused about the relation of
 		 * the numbers in g_callable_info_get_arg and
 		 * g_arg_info_get_closure and g_arg_info_get_destroy.  We used
 		 * to add method_offset, but that stopped being correct at some
 		 * point. */
-		iinfo.current_pos = i; /* + method_offset; */
+		iinfo.base.current_pos = i; /* + method_offset; */
 
 		dwarn ("  arg %d, tag: %d (%s), is_pointer: %d, is_automatic: %d\n",
 		       i,
@@ -105,26 +106,24 @@ invoke_c_code (GICallableInfo *info,
 			} else {
 				sv_to_arg (current_sv,
 				           &iinfo.in_args[i], arg_info, arg_type,
-				           transfer, may_be_null, &iinfo);
+				           transfer, may_be_null, &iinfo.base);
 			}
-			iinfo.arg_types[ffi_stack_pos] =
+			iinfo.arg_types_ffi[ffi_stack_pos] =
 				g_type_info_get_ffi_type (arg_type);
 			iinfo.args[ffi_stack_pos] = &iinfo.in_args[i];
-			g_base_info_unref ((GIBaseInfo *) arg_type);
 			break;
 
 		    case GI_DIRECTION_OUT:
 			if (g_arg_info_is_caller_allocates (arg_info)) {
-				iinfo.aux_args[i].v_pointer =
+				iinfo.base.aux_args[i].v_pointer =
 					_allocate_out_mem (arg_type);
-				iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
-				iinfo.args[ffi_stack_pos] = &iinfo.aux_args[i];
+				iinfo.out_args[i].v_pointer = &iinfo.base.aux_args[i];
+				iinfo.args[ffi_stack_pos] = &iinfo.base.aux_args[i];
 			} else {
-				iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
+				iinfo.out_args[i].v_pointer = &iinfo.base.aux_args[i];
 				iinfo.args[ffi_stack_pos] = &iinfo.out_args[i];
 			}
-			iinfo.out_arg_infos[i] = arg_type;
-			iinfo.arg_types[ffi_stack_pos] = &ffi_type_pointer;
+			iinfo.arg_types_ffi[ffi_stack_pos] = &ffi_type_pointer;
 			/* Adjust the dynamic stack offset so that this out
 			 * argument doesn't inadvertedly eat up an in argument. */
 			iinfo.dynamic_stack_offset--;
@@ -133,7 +132,7 @@ invoke_c_code (GICallableInfo *info,
 		    case GI_DIRECTION_INOUT:
 			iinfo.in_args[i].v_pointer =
 				iinfo.out_args[i].v_pointer =
-					&iinfo.aux_args[i];
+					&iinfo.base.aux_args[i];
 			if (iinfo.is_automatic_arg[i]) {
 				iinfo.dynamic_stack_offset--;
 			} else if (is_skipped) {
@@ -144,40 +143,38 @@ invoke_c_code (GICallableInfo *info,
 				 * pointed to is filled from the SV. */
 				sv_to_arg (current_sv,
 				           iinfo.in_args[i].v_pointer, arg_info, arg_type,
-				           transfer, may_be_null, &iinfo);
+				           transfer, may_be_null, &iinfo.base);
 			}
-			iinfo.out_arg_infos[i] = arg_type;
-			iinfo.arg_types[ffi_stack_pos] = &ffi_type_pointer;
+			iinfo.arg_types_ffi[ffi_stack_pos] = &ffi_type_pointer;
 			iinfo.args[ffi_stack_pos] = &iinfo.in_args[i];
 			break;
 		}
-
-		g_base_info_unref ((GIBaseInfo *) arg_info);
 	}
 
 	/* do another pass to handle automatic args */
-	for (i = 0 ; i < iinfo.n_args ; i++) {
+	for (i = 0 ; i < iinfo.base.n_args ; i++) {
 		GIArgInfo * arg_info;
+		GITypeInfo * arg_type;
 		if (!iinfo.is_automatic_arg[i])
 			continue;
-		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
+		arg_info = iinfo.base.arg_infos[i];
+		arg_type = iinfo.base.arg_types[i];
 		switch (g_arg_info_get_direction (arg_info)) {
 		    case GI_DIRECTION_IN:
-			_handle_automatic_arg (i, &iinfo.in_args[i], &iinfo);
+			_handle_automatic_arg (i, arg_info, arg_type, &iinfo.in_args[i], &iinfo);
 			break;
 		    case GI_DIRECTION_INOUT:
-			_handle_automatic_arg (i, &iinfo.aux_args[i], &iinfo);
+			_handle_automatic_arg (i, arg_info, arg_type, &iinfo.base.aux_args[i], &iinfo);
 			break;
 		    case GI_DIRECTION_OUT:
 			/* handled later */
 			break;
 		}
-		g_base_info_unref ((GIBaseInfo *) arg_info);
 	}
 
 	if (iinfo.throws) {
 		iinfo.args[iinfo.n_invoke_args - 1] = &local_error_address;
-		iinfo.arg_types[iinfo.n_invoke_args - 1] = &ffi_type_pointer;
+		iinfo.arg_types_ffi[iinfo.n_invoke_args - 1] = &ffi_type_pointer;
 	}
 
 	/*
@@ -186,7 +183,7 @@ invoke_c_code (GICallableInfo *info,
 
 	/* prepare and call the function */
 	if (FFI_OK != ffi_prep_cif (&cif, FFI_DEFAULT_ABI, iinfo.n_invoke_args,
-	                            iinfo.return_type_ffi, iinfo.arg_types))
+	                            iinfo.base.return_type_ffi, iinfo.arg_types_ffi))
 	{
 		_clear_c_invocation_info (&iinfo);
 		ccroak ("Could not prepare a call interface");
@@ -206,9 +203,10 @@ invoke_c_code (GICallableInfo *info,
 	SPAGAIN;
 
 	/* free call-scoped data */
-	_invoke_free_after_call_handlers (&iinfo);
+	invoke_free_after_call_handlers (&iinfo.base);
 
 	if (local_error) {
+		_clear_c_invocation_info (&iinfo);
 		gperl_croak_gerror (NULL, local_error);
 	}
 
@@ -219,7 +217,7 @@ invoke_c_code (GICallableInfo *info,
 #if GI_CHECK_VERSION (1, 32, 0)
 	/* libffi has special semantics for return value storage; see `man
 	 * ffi_call`.  We use gobject-introspection's extraction helper. */
-	gi_type_info_extract_ffi_return_value (iinfo.return_type_info,
+	gi_type_info_extract_ffi_return_value (iinfo.base.return_type_info,
 	                                       &ffi_return_value,
 	                                       &return_value);
 #endif
@@ -227,7 +225,7 @@ invoke_c_code (GICallableInfo *info,
 	n_return_values = 0;
 
 	/* place return value and output args on the stack */
-	if (iinfo.has_return_value
+	if (iinfo.base.has_return_value
 #if GI_CHECK_VERSION (1, 29, 0)
 	    && !g_callable_info_skip_return ((GICallableInfo *) info)
 #endif
@@ -235,9 +233,9 @@ invoke_c_code (GICallableInfo *info,
 	{
 		SV *value;
 		value = SAVED_STACK_SV (arg_to_sv (&return_value,
-		                                   iinfo.return_type_info,
-		                                   iinfo.return_type_transfer,
-		                                   &iinfo));
+		                                   iinfo.base.return_type_info,
+		                                   iinfo.base.return_type_transfer,
+		                                   &iinfo.base));
 		if (value) {
 			XPUSHs (sv_2mortal (value));
 			n_return_values++;
@@ -245,14 +243,13 @@ invoke_c_code (GICallableInfo *info,
 	}
 
 	/* out args */
-	for (i = 0 ; i < iinfo.n_args ; i++) {
+	for (i = 0 ; i < iinfo.base.n_args ; i++) {
 		GIArgInfo * arg_info;
 		if (iinfo.is_automatic_arg[i])
 			continue;
-		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
+		arg_info = iinfo.base.arg_infos[i];
 #if GI_CHECK_VERSION (1, 29, 0)
 		if (g_arg_info_is_skip (arg_info)) {
-			g_base_info_unref ((GIBaseInfo *) arg_info);
 			continue;
 		}
 #endif
@@ -267,21 +264,19 @@ invoke_c_code (GICallableInfo *info,
 			         ? GI_TRANSFER_CONTAINER
 			         : g_arg_info_get_ownership_transfer (arg_info);
 			sv = SAVED_STACK_SV (arg_to_sv (iinfo.out_args[i].v_pointer,
-			                                iinfo.out_arg_infos[i],
+			                                iinfo.base.arg_types[i],
 			                                transfer,
-			                                &iinfo));
+			                                &iinfo.base));
 			if (sv) {
 				XPUSHs (sv_2mortal (sv));
 				n_return_values++;
 			}
-			g_base_info_unref ((GIBaseInfo*) iinfo.out_arg_infos[i]);
 			break;
 		    }
 
 		    default:
 			break;
 		}
-		g_base_info_unref ((GIBaseInfo *) arg_info);
 	}
 
 	_clear_c_invocation_info (&iinfo);
@@ -294,7 +289,7 @@ invoke_c_code (GICallableInfo *info,
 /* ------------------------------------------------------------------------- */
 
 static void
-_prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
+_prepare_c_invocation_info (GPerlI11nCInvocationInfo *iinfo,
                             GICallableInfo *info,
                             IV items,
                             UV internal_stack_offset,
@@ -304,52 +299,43 @@ _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
 {
 	guint i;
 
+	prepare_invocation_info ((GPerlI11nInvocationInfo *) iinfo, info);
+
 	dwarn ("C invoke: %s::%s::%s => %s\n"
 	       "  n_args: %d\n",
 	       package, namespace, function,
 	       g_base_info_get_name (info),
 	       g_callable_info_get_n_args (info));
 
-	iinfo->interface = info;
 	iinfo->target_package = package;
 	iinfo->target_namespace = namespace;
 	iinfo->target_function = function;
 
-	iinfo->is_function = GI_IS_FUNCTION_INFO (info);
-	iinfo->is_vfunc = GI_IS_VFUNC_INFO (info);
-	iinfo->is_callback = (g_base_info_get_type (info) == GI_INFO_TYPE_CALLBACK);
-	dwarn ("  is_function = %d, is_vfunc = %d, is_callback = %d\n",
-	       iinfo->is_function, iinfo->is_vfunc, iinfo->is_callback);
-
-	iinfo->stack_offset = internal_stack_offset;
+	iinfo->stack_offset = (guint) internal_stack_offset;
+	g_assert (items >= iinfo->stack_offset);
+	iinfo->n_given_args = ((guint) items) - iinfo->stack_offset;
+	iinfo->n_invoke_args = iinfo->base.n_args;
 
 	iinfo->is_constructor = FALSE;
-	if (iinfo->is_function) {
+	if (iinfo->base.is_function) {
 		iinfo->is_constructor =
 			g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR;
 	}
-	if (iinfo->is_constructor) {
-		iinfo->stack_offset++;
-	}
-
-	iinfo->n_given_args = items - iinfo->stack_offset;
-
-	iinfo->n_invoke_args = iinfo->n_args =
-		g_callable_info_get_n_args ((GICallableInfo *) info);
 
 	/* FIXME: can a vfunc not throw? */
 	iinfo->throws = FALSE;
-	if (iinfo->is_function) {
+	if (iinfo->base.is_function) {
 		iinfo->throws =
 			g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
 	}
 	if (iinfo->throws) {
+		/* Add one for the implicit GError arg. */
 		iinfo->n_invoke_args++;
 	}
 
-	if (iinfo->is_vfunc) {
+	if (iinfo->base.is_vfunc) {
 		iinfo->is_method = TRUE;
-	} else if (iinfo->is_callback) {
+	} else if (iinfo->base.is_callback) {
 		iinfo->is_method = FALSE;
 	} else {
 		iinfo->is_method =
@@ -357,6 +343,7 @@ _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
 			&& !iinfo->is_constructor;
 	}
 	if (iinfo->is_method) {
+		/* Add one for the implicit invocant arg. */
 		iinfo->n_invoke_args++;
 	}
 
@@ -367,36 +354,30 @@ _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
 	       iinfo->n_args, iinfo->n_invoke_args, iinfo->n_given_args,
 	       iinfo->is_constructor, iinfo->is_method);
 
-	iinfo->return_type_info =
-		g_callable_info_get_return_type ((GICallableInfo *) info);
-	iinfo->has_return_value =
-		GI_TYPE_TAG_VOID != g_type_info_get_tag (iinfo->return_type_info);
-	iinfo->return_type_ffi = g_type_info_get_ffi_type (iinfo->return_type_info);
-	iinfo->return_type_transfer = g_callable_info_get_caller_owns ((GICallableInfo *) info);
-
 	/* allocate enough space for all args in both the out and in lists.
 	 * we'll only use as much as we need.  since function argument lists
 	 * are typically small, this shouldn't be a big problem. */
 	if (iinfo->n_invoke_args) {
-		gint n = iinfo->n_invoke_args;
+		guint n = iinfo->n_invoke_args;
 		iinfo->in_args = gperl_alloc_temp (sizeof (GIArgument) * n);
 		iinfo->out_args = gperl_alloc_temp (sizeof (GIArgument) * n);
-		iinfo->out_arg_infos = gperl_alloc_temp (sizeof (GITypeInfo*) * n);
-		iinfo->arg_types = gperl_alloc_temp (sizeof (ffi_type *) * n);
+		iinfo->arg_types_ffi = gperl_alloc_temp (sizeof (ffi_type *) * n);
 		iinfo->args = gperl_alloc_temp (sizeof (gpointer) * n);
-		iinfo->aux_args = gperl_alloc_temp (sizeof (GIArgument) * n);
 		iinfo->is_automatic_arg = gperl_alloc_temp (sizeof (gboolean) * n);
 	}
 
+	/* If we call a constructor, we skip the initial package name resulting
+	 * from the "Package->new" syntax.  If we call a method, we handle the
+	 * invocant separately. */
+	iinfo->constructor_offset = iinfo->is_constructor ? 1 : 0;
 	iinfo->method_offset = iinfo->is_method ? 1 : 0;
 	iinfo->dynamic_stack_offset = 0;
 
 	/* Make a first pass to mark args that are filled in automatically, and
 	 * thus have no counterpart on the Perl side. */
-	for (i = 0 ; i < iinfo->n_args ; i++) {
-		GIArgInfo * arg_info =
-			g_callable_info_get_arg ((GICallableInfo *) info, i);
-		GITypeInfo * arg_type = g_arg_info_get_type (arg_info);
+	for (i = 0 ; i < iinfo->base.n_args ; i++) {
+		GIArgInfo * arg_info = iinfo->base.arg_infos[i];
+		GITypeInfo * arg_type = iinfo->base.arg_types[i];
 		GITypeTag arg_tag = g_type_info_get_tag (arg_type);
 
 		if (arg_tag == GI_TYPE_TAG_ARRAY) {
@@ -419,18 +400,14 @@ _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
 			}
 			g_base_info_unref ((GIBaseInfo *) interface);
 		}
-
-		g_base_info_unref ((GIBaseInfo *) arg_type);
-		g_base_info_unref ((GIBaseInfo *) arg_info);
 	}
 
 	/* Make another pass to count the expected args. */
-	iinfo->n_expected_args = iinfo->method_offset;
+	iinfo->n_expected_args = iinfo->constructor_offset + iinfo->method_offset;
 	iinfo->n_nullable_args = 0;
-	for (i = 0 ; i < iinfo->n_args ; i++) {
-		GIArgInfo * arg_info =
-			g_callable_info_get_arg ((GICallableInfo *) info, i);
-		GITypeInfo * arg_type = g_arg_info_get_type (arg_info);
+	for (i = 0 ; i < iinfo->base.n_args ; i++) {
+		GIArgInfo * arg_info = iinfo->base.arg_infos[i];
+		GITypeInfo * arg_type = iinfo->base.arg_types[i];
 		GITypeTag arg_tag = g_type_info_get_tag (arg_type);
 		gboolean is_out = GI_DIRECTION_OUT == g_arg_info_get_direction (arg_info);
 		gboolean is_automatic = iinfo->is_automatic_arg[i];
@@ -444,23 +421,18 @@ _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
 		/* Callback user data may always be NULL. */
 		if (g_arg_info_may_be_null (arg_info) || arg_tag == GI_TYPE_TAG_VOID)
 			iinfo->n_nullable_args++;
-
-		g_base_info_unref ((GIBaseInfo *) arg_type);
-		g_base_info_unref ((GIBaseInfo *) arg_info);
 	}
 
 	/* If the return value is an array which comes with an outbound length
 	 * arg, then mark that length arg as automatic, too. */
-	if (g_type_info_get_tag (iinfo->return_type_info) == GI_TYPE_TAG_ARRAY) {
-		gint pos = g_type_info_get_array_length (iinfo->return_type_info);
+	if (g_type_info_get_tag (iinfo->base.return_type_info) == GI_TYPE_TAG_ARRAY) {
+		gint pos = g_type_info_get_array_length (iinfo->base.return_type_info);
 		if (pos >= 0) {
-			GIArgInfo * arg_info =
-				g_callable_info_get_arg ((GICallableInfo *) info, pos);
+			GIArgInfo * arg_info = iinfo->base.arg_infos[pos];
 			if (GI_DIRECTION_OUT == g_arg_info_get_direction (arg_info)) {
 				dwarn ("  pos %d is automatic (array length)\n", pos);
 				iinfo->is_automatic_arg[pos] = TRUE;
 			}
-			g_base_info_unref (arg_info);
 		}
 	}
 
@@ -468,38 +440,29 @@ _prepare_c_invocation_info (GPerlI11nInvocationInfo *iinfo,
 	 * descendants receive from gobject-introspection: values of this type
 	 * are always marked transfer=none, even for constructors. */
 	if (iinfo->is_constructor &&
-	    g_type_info_get_tag (iinfo->return_type_info) == GI_TYPE_TAG_INTERFACE)
+	    g_type_info_get_tag (iinfo->base.return_type_info) == GI_TYPE_TAG_INTERFACE)
 	{
-		GIBaseInfo * interface = g_type_info_get_interface (iinfo->return_type_info);
+		GIBaseInfo * interface = g_type_info_get_interface (iinfo->base.return_type_info);
 		if (GI_IS_REGISTERED_TYPE_INFO (interface) &&
 		    g_type_is_a (get_gtype (interface),
 		                 G_TYPE_INITIALLY_UNOWNED))
 		{
-			iinfo->return_type_transfer = GI_TRANSFER_EVERYTHING;
+			iinfo->base.return_type_transfer = GI_TRANSFER_EVERYTHING;
 		}
 		g_base_info_unref ((GIBaseInfo *) interface);
 	}
 }
 
 static void
-_clear_c_invocation_info (GPerlI11nInvocationInfo *iinfo)
+_clear_c_invocation_info (GPerlI11nCInvocationInfo *iinfo)
 {
-	g_slist_free (iinfo->free_after_call);
-
-	/* The actual callback infos might be needed later, so we cannot free
-	 * them here. */
-	g_slist_free (iinfo->callback_infos);
-
-	g_slist_foreach (iinfo->array_infos, (GFunc) g_free, NULL);
-	g_slist_free (iinfo->array_infos);
-
-	g_base_info_unref ((GIBaseInfo *) iinfo->return_type_info);
+	clear_invocation_info ((GPerlI11nInvocationInfo *) iinfo);
 }
 
 /* ------------------------------------------------------------------------- */
 
 static gchar *
-_format_target (GPerlI11nInvocationInfo *iinfo)
+_format_target (GPerlI11nCInvocationInfo *iinfo)
 {
 	gchar *caller = NULL;
 	if (iinfo->target_package && iinfo->target_namespace && iinfo->target_function) {
@@ -513,14 +476,14 @@ _format_target (GPerlI11nInvocationInfo *iinfo)
 		                      NULL);
 	} else {
 		caller = g_strconcat ("Callable ",
-		                      g_base_info_get_name (iinfo->interface),
+		                      g_base_info_get_name (iinfo->base.interface),
 		                      NULL);
 	}
 	return caller;
 }
 
 static void
-_check_n_args (GPerlI11nInvocationInfo *iinfo)
+_check_n_args (GPerlI11nCInvocationInfo *iinfo)
 {
 	if (iinfo->n_expected_args != iinfo->n_given_args) {
 		/* Avoid the cost of formatting the target until we know we
@@ -529,12 +492,12 @@ _check_n_args (GPerlI11nInvocationInfo *iinfo)
 		if (iinfo->n_given_args < (iinfo->n_expected_args - iinfo->n_nullable_args)) {
 			caller = _format_target (iinfo);
 			ccroak ("%s: passed too few parameters "
-			        "(expected %d, got %d)",
+			        "(expected %u, got %u)",
 			        caller, iinfo->n_expected_args, iinfo->n_given_args);
 		} else if (iinfo->n_given_args > iinfo->n_expected_args) {
 			caller = _format_target (iinfo);
 			cwarn ("*** %s: passed too many parameters "
-			       "(expected %d, got %d); ignoring excess",
+			       "(expected %u, got %u); ignoring excess",
 			       caller, iinfo->n_expected_args, iinfo->n_given_args);
 		}
 		if (caller)
@@ -546,25 +509,30 @@ _check_n_args (GPerlI11nInvocationInfo *iinfo)
 
 static void
 _handle_automatic_arg (guint pos,
+                       GIArgInfo * arg_info,
+                       GITypeInfo * arg_type,
                        GIArgument * arg,
-                       GPerlI11nInvocationInfo * invocation_info)
+                       GPerlI11nCInvocationInfo * invocation_info)
 {
 	GSList *l;
 
 	/* array length */
-	for (l = invocation_info->array_infos; l != NULL; l = l->next) {
+	for (l = invocation_info->base.array_infos; l != NULL; l = l->next) {
 		GPerlI11nArrayInfo *ainfo = l->data;
 		if (((gint) pos) == ainfo->length_pos) {
+			SV *conversion_sv;
 			dwarn ("  setting automatic arg %d (array length) to %"G_GSIZE_FORMAT"\n",
 			       pos, ainfo->length);
-			/* FIXME: Is it OK to always use v_size here? */
-			arg->v_size = ainfo->length;
+			conversion_sv = newSVuv (ainfo->length);
+			sv_to_arg (conversion_sv, arg, arg_info, arg_type,
+			           GI_TRANSFER_NOTHING, FALSE, NULL);
+			SvREFCNT_dec (conversion_sv);
 			return;
 		}
 	}
 
 	/* callback destroy notify */
-	for (l = invocation_info->callback_infos; l != NULL; l = l->next) {
+	for (l = invocation_info->base.callback_infos; l != NULL; l = l->next) {
 		GPerlI11nPerlCallbackInfo *cinfo = l->data;
 		if (((gint) pos) == cinfo->destroy_pos) {
 			dwarn ("  setting automatic arg %d (destroy notify for calllback %p)\n",
@@ -623,36 +591,3 @@ _allocate_out_mem (GITypeInfo *arg_type)
 		return NULL;
 	}
 }
-
-/* ------------------------------------------------------------------------- */
-
-typedef struct {
-	GFunc func;
-	gpointer data;
-} FreeClosure;
-
-static void
-free_after_call (GPerlI11nInvocationInfo *iinfo, GFunc func, gpointer data)
-{
-	FreeClosure *closure = g_new (FreeClosure, 1);
-	closure->func = func;
-	closure->data = data;
-	iinfo->free_after_call
-		= g_slist_prepend (iinfo->free_after_call, closure);
-}
-
-static void
-_invoke_free_closure (FreeClosure *closure)
-{
-	closure->func (closure->data, NULL);
-	g_free (closure);
-}
-
-static void
-_invoke_free_after_call_handlers (GPerlI11nInvocationInfo *iinfo)
-{
-	g_slist_foreach (iinfo->free_after_call,
-	                 (GFunc) _invoke_free_closure, NULL);
-	g_slist_free (iinfo->free_after_call);
-	iinfo->free_after_call = NULL;
-}
@@ -1,19 +1,24 @@
 /* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
 
-static void _prepare_perl_invocation_info (GPerlI11nInvocationInfo *iinfo,
+static void _prepare_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo,
                                            GICallableInfo *info,
                                            gpointer *args);
-static void _clear_perl_invocation_info (GPerlI11nInvocationInfo *iinfo);
+static void _clear_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo);
+static void _fill_ffi_return_value (GITypeInfo *return_info,
+                                    gpointer resp,
+                                    GIArgument *arg);
+
 
 static void
 invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata)
 {
 	GPerlI11nPerlCallbackInfo *info;
 	GICallableInfo *cb_interface;
-	GPerlI11nInvocationInfo iinfo = {0,};
+	GPerlI11nPerlInvocationInfo iinfo;
 	guint args_offset = 0, i;
 	guint in_inout;
-	guint n_return_values, n_returned;
+	guint n_return_values;
+	I32 n_returned;
 	I32 context;
 	SV *first_sv = NULL, *last_sv = NULL;
 	dGPERL_CALLBACK_MARSHAL_SP;
@@ -50,7 +55,7 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 	/* convert the implicit instance argument and push the first SV onto
 	 * the stack; depending on the "swap" setting, this might be the
 	 * instance or the user data.  this is only relevant for signals. */
-	if (iinfo.is_signal) {
+	if (iinfo.base.is_signal) {
 		SV *instance_sv, *data_sv;
 		args_offset = 1;
 		instance_sv = SAVED_STACK_SV (instance_pointer_to_sv (
@@ -72,13 +77,13 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 	 * suitable converters; push in and in-out arguments onto the perl
 	 * stack */
 	in_inout = 0;
-	for (i = 0; i < iinfo.n_args; i++) {
-		GIArgInfo *arg_info = g_callable_info_get_arg (cb_interface, i);
-		GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
+	for (i = 0; i < iinfo.base.n_args; i++) {
+		GIArgInfo *arg_info = iinfo.base.arg_infos[i];
+		GITypeInfo *arg_type = iinfo.base.arg_types[i];
 		GITransfer transfer = g_arg_info_get_ownership_transfer (arg_info);
 		GIDirection direction = g_arg_info_get_direction (arg_info);
 
-		iinfo.current_pos = i;
+		iinfo.base.current_pos = i;
 
 		dwarn ("arg info: %s (%p)\n"
 		       "  direction: %d\n"
@@ -113,7 +118,7 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 				? *((gpointer *) args[i+args_offset])
 				: args[i+args_offset];
 			raw_to_arg (raw, &arg, arg_type);
-			sv = SAVED_STACK_SV (arg_to_sv (&arg, arg_type, transfer, &iinfo));
+			sv = SAVED_STACK_SV (arg_to_sv (&arg, arg_type, transfer, &iinfo.base));
 			/* If arg_to_sv returns NULL, we take that as 'skip
 			 * this argument'; happens for GDestroyNotify, for
 			 * example. */
@@ -126,9 +131,6 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 		{
 			in_inout++;
 		}
-
-		g_base_info_unref ((GIBaseInfo *) arg_info);
-		g_base_info_unref ((GIBaseInfo *) arg_type);
 	}
 
 	/* push the last SV onto the stack; this might be the user data or the
@@ -149,7 +151,7 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 
 	/* determine suitable Perl call context */
 	context = G_VOID | G_DISCARD;
-	if (iinfo.has_return_value) {
+	if (iinfo.base.has_return_value) {
 		context = in_inout > 0
 		  ? G_ARRAY
 		  : G_SCALAR;
@@ -162,15 +164,15 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 	}
 
 	/* do the call, demand #in-out+#out+#return-value return values */
-	n_return_values = iinfo.has_return_value
+	n_return_values = iinfo.base.has_return_value
 	  ? in_inout + 1
 	  : in_inout;
 	n_returned = info->sub_name
 		? call_method (info->sub_name, context)
 		: call_sv (info->code, context);
-	if (n_return_values != 0 && n_returned != n_return_values) {
+	if (n_return_values != 0 && (n_returned < 0 || ((guint) n_returned) != n_return_values)) {
 		ccroak ("callback returned %d values "
-		        "but is supposed to return %d values",
+		        "but is supposed to return %u values",
 		        n_returned, n_return_values);
 	}
 
@@ -194,16 +196,14 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 		}
 
 		out_index = 0;
-		for (i = 0; i < iinfo.n_args; i++) {
-			GIArgInfo *arg_info = g_callable_info_get_arg (cb_interface, i);
-			GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
+		for (i = 0; i < iinfo.base.n_args; i++) {
+			GIArgInfo *arg_info = iinfo.base.arg_infos[i];
+			GITypeInfo *arg_type = iinfo.base.arg_types[i];
 			GIDirection direction = g_arg_info_get_direction (arg_info);
 			gpointer out_pointer = * (gpointer *) args[i+args_offset];
 
 			if (!out_pointer) {
 				dwarn ("skipping out arg %d\n", i);
-				g_base_info_unref (arg_info);
-				g_base_info_unref (arg_type);
 				continue;
 			}
 
@@ -226,29 +226,26 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 				}
 				sv_to_arg (returned_values[out_index], &tmp_arg,
 				           arg_info, arg_type,
-				           transfer, may_be_null, &iinfo);
+				           transfer, may_be_null, &iinfo.base);
 				if (!is_caller_allocated) {
 					arg_to_raw (&tmp_arg, out_pointer, arg_type);
 				}
 				out_index++;
 			}
-
-			g_base_info_unref (arg_info);
-			g_base_info_unref (arg_type);
 		}
 
 		g_free (returned_values);
 	}
 
 	/* store return value in resp, if any */
-	if (iinfo.has_return_value) {
+	if (iinfo.base.has_return_value) {
 		GIArgument arg;
 		GITypeInfo *type_info;
 		GITransfer transfer;
 		gboolean may_be_null;
 
-		type_info = iinfo.return_type_info;
-		transfer = iinfo.return_type_transfer;
+		type_info = iinfo.base.return_type_info;
+		transfer = iinfo.base.return_type_transfer;
 		may_be_null = g_callable_info_may_return_null (cb_interface); /* FIXME */
 
 		dwarn ("ret type: %p\n"
@@ -261,8 +258,8 @@ invoke_perl_code (ffi_cif* cif, gpointer resp, gpointer* args, gpointer userdata
 		       transfer);
 
 		sv_to_arg (POPs, &arg, NULL, type_info,
-		           transfer, may_be_null, &iinfo);
-		arg_to_raw (&arg, resp, type_info);
+		           transfer, may_be_null, &iinfo.base);
+		_fill_ffi_return_value (type_info, resp, &arg);
 	}
 
 	PUTBACK;
@@ -337,76 +334,120 @@ invoke_perl_signal_handler (ffi_cif* cif, gpointer resp, gpointer* args, gpointe
 /* -------------------------------------------------------------------------- */
 
 static void
-_prepare_perl_invocation_info (GPerlI11nInvocationInfo *iinfo,
+_prepare_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo,
                                GICallableInfo *info,
                                gpointer *args)
 {
 	guint i;
 
+	prepare_invocation_info ((GPerlI11nInvocationInfo *) iinfo, info);
+
 	dwarn ("Perl invoke: %s\n"
 	       "  n_args: %d\n",
 	       g_base_info_get_name (info),
 	       g_callable_info_get_n_args (info));
 
-	iinfo->interface = info;
-
 	/* When invoking Perl code, we currently always use a complete
 	 * description of the callable (from a record field or some callback
 	 * typedef) for functions, vfuncs and calllbacks.  This implies that
 	 * there is no implicit invocant; it always appears explicitly in the
 	 * arg list.  For signals, however, the invocant is implicit. */
-	iinfo->is_function = GI_IS_FUNCTION_INFO (info);
-	iinfo->is_vfunc = GI_IS_VFUNC_INFO (info);
-	iinfo->is_signal = GI_IS_SIGNAL_INFO (info);
-	iinfo->is_callback = (g_base_info_get_type (info) == GI_INFO_TYPE_CALLBACK);
-	dwarn ("  is_function = %d, is_vfunc = %d, is_callback = %d, is_signal = %d\n",
-	       iinfo->is_function, iinfo->is_vfunc, iinfo->is_callback, iinfo->is_signal);
-
-	iinfo->n_args = g_callable_info_get_n_args (info);
 
 	/* FIXME: 'throws'? */
 
-	iinfo->return_type_info = g_callable_info_get_return_type (info);
-	iinfo->has_return_value =
-		GI_TYPE_TAG_VOID != g_type_info_get_tag (iinfo->return_type_info);
-	iinfo->return_type_transfer = g_callable_info_get_caller_owns (info);
-
-	iinfo->dynamic_stack_offset = 0;
-
 	/* Find array length arguments and store their value in aux_args so
 	 * that array_to_sv can later fetch them. */
-	if (iinfo->n_args) {
-		iinfo->aux_args = gperl_alloc_temp (sizeof (GIArgument) * iinfo->n_args);
-	}
-	for (i = 0 ; i < iinfo->n_args ; i++) {
-		GIArgInfo *arg_info = g_callable_info_get_arg (info, i);
-		GITypeInfo *arg_type = g_arg_info_get_type (arg_info);
+	for (i = 0 ; i < iinfo->base.n_args ; i++) {
+		GITypeInfo *arg_type = iinfo->base.arg_types[i];
 		GITypeTag arg_tag = g_type_info_get_tag (arg_type);
 
 		if (arg_tag == GI_TYPE_TAG_ARRAY) {
 			gint pos = g_type_info_get_array_length (arg_type);
 			if (pos >= 0) {
-				GIArgInfo *length_arg_info = g_callable_info_get_arg (info, i);
-				GITypeInfo *length_arg_type = g_arg_info_get_type (arg_info);
-				raw_to_arg (args[pos], &iinfo->aux_args[pos], length_arg_type);
+				GITypeInfo *length_arg_type = iinfo->base.arg_types[pos];
+				raw_to_arg (args[pos], &iinfo->base.aux_args[pos], length_arg_type);
 				dwarn ("  pos %d is array length => %"G_GSIZE_FORMAT"\n",
 				       pos, iinfo->aux_args[pos].v_size);
-				g_base_info_unref (length_arg_type);
-				g_base_info_unref (length_arg_info);
 			}
 		}
-
-		g_base_info_unref (arg_type);
-		g_base_info_unref (arg_info);
 	}
 }
 
 static void
-_clear_perl_invocation_info (GPerlI11nInvocationInfo *iinfo)
+_clear_perl_invocation_info (GPerlI11nPerlInvocationInfo *iinfo)
 {
-	/* The actual callback infos might be needed later, so we cannot free
-	 * them here. */
-	g_slist_free (iinfo->callback_infos);
+	clear_invocation_info ((GPerlI11nInvocationInfo *) iinfo);
+}
+
+/* ------------------------------------------------------------------------- */
 
-	g_base_info_unref ((GIBaseInfo *) iinfo->return_type_info);
+/* Copied from pygobject's pygi-closure.c. */
+static void
+_fill_ffi_return_value (GITypeInfo *return_info,
+                        gpointer resp,
+                        GIArgument *arg)
+{
+	if (!resp)
+		return;
+	switch (g_type_info_get_tag (return_info)) {
+	    case GI_TYPE_TAG_BOOLEAN:
+		*((ffi_sarg *) resp) = arg->v_boolean;
+		break;
+	    case GI_TYPE_TAG_INT8:
+		*((ffi_sarg *) resp) = arg->v_int8;
+		break;
+	    case GI_TYPE_TAG_UINT8:
+		*((ffi_arg *) resp) = arg->v_uint8;
+		break;
+	    case GI_TYPE_TAG_INT16:
+		*((ffi_sarg *) resp) = arg->v_int16;
+		break;
+	    case GI_TYPE_TAG_UINT16:
+		*((ffi_arg *) resp) = arg->v_uint16;
+		break;
+	    case GI_TYPE_TAG_INT32:
+		*((ffi_sarg *) resp) = arg->v_int32;
+		break;
+	    case GI_TYPE_TAG_UINT32:
+		*((ffi_arg *) resp) = arg->v_uint32;
+		break;
+	    case GI_TYPE_TAG_INT64:
+		*((ffi_sarg *) resp) = arg->v_int64;
+		break;
+	    case GI_TYPE_TAG_UINT64:
+		*((ffi_arg *) resp) = arg->v_uint64;
+		break;
+	    case GI_TYPE_TAG_FLOAT:
+		*((gfloat *) resp) = arg->v_float;
+		break;
+	    case GI_TYPE_TAG_DOUBLE:
+		*((gdouble *) resp) = arg->v_double;
+		break;
+	    case GI_TYPE_TAG_GTYPE:
+		*((ffi_arg *) resp) = arg->v_size;
+		break;
+	    case GI_TYPE_TAG_UNICHAR:
+		*((ffi_arg *) resp) = arg->v_uint32;
+		break;
+	    case GI_TYPE_TAG_INTERFACE:
+		{
+			GIBaseInfo *interface_info;
+			interface_info = g_type_info_get_interface (return_info);
+			switch (g_base_info_get_type (interface_info)) {
+			    case GI_INFO_TYPE_ENUM:
+				*(ffi_sarg *) resp = arg->v_int;
+				break;
+			    case GI_INFO_TYPE_FLAGS:
+				*(ffi_arg *) resp = arg->v_uint;
+				break;
+			    default:
+				*(ffi_arg *) resp = (ffi_arg) arg->v_pointer;
+				break;
+			}
+			break;
+		}
+	    default:
+		*(ffi_arg *) resp = (ffi_arg) arg->v_pointer;
+		break;
+	}
 }
@@ -0,0 +1,112 @@
+/* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
+
+static void
+prepare_invocation_info (GPerlI11nInvocationInfo *iinfo,
+                         GICallableInfo *info)
+{
+	gint orig_n_args;
+	guint i;
+
+	dwarn ("invoke: %s\n"
+	       "  n_args: %d\n",
+	       g_base_info_get_name (info),
+	       g_callable_info_get_n_args (info));
+
+	iinfo->interface = info;
+
+	iinfo->is_function = GI_IS_FUNCTION_INFO (info);
+	iinfo->is_vfunc = GI_IS_VFUNC_INFO (info);
+	iinfo->is_callback = (g_base_info_get_type (info) == GI_INFO_TYPE_CALLBACK);
+	iinfo->is_signal = GI_IS_SIGNAL_INFO (info);
+	dwarn ("  is_function = %d, is_vfunc = %d, is_callback = %d\n",
+	       iinfo->is_function, iinfo->is_vfunc, iinfo->is_callback);
+
+	orig_n_args = g_callable_info_get_n_args (info);
+	g_assert (orig_n_args >= 0);
+	iinfo->n_args = (guint) orig_n_args;
+
+	if (iinfo->n_args) {
+		iinfo->arg_infos = gperl_alloc_temp (sizeof (GITypeInfo*) * iinfo->n_args);
+		iinfo->arg_types = gperl_alloc_temp (sizeof (GITypeInfo*) * iinfo->n_args);
+		iinfo->aux_args = gperl_alloc_temp (sizeof (GIArgument) * iinfo->n_args);
+	} else {
+		iinfo->arg_infos = NULL;
+		iinfo->arg_types = NULL;
+		iinfo->aux_args = NULL;
+	}
+
+	for (i = 0 ; i < iinfo->n_args ; i++) {
+		iinfo->arg_infos[i] = g_callable_info_get_arg (info, (gint) i);
+		iinfo->arg_types[i] = g_arg_info_get_type (iinfo->arg_infos[i]);
+	}
+
+	iinfo->return_type_info = g_callable_info_get_return_type (info);
+	iinfo->has_return_value =
+		GI_TYPE_TAG_VOID != g_type_info_get_tag (iinfo->return_type_info);
+	iinfo->return_type_ffi = g_type_info_get_ffi_type (iinfo->return_type_info);
+	iinfo->return_type_transfer = g_callable_info_get_caller_owns (info);
+
+	iinfo->callback_infos = NULL;
+	iinfo->array_infos = NULL;
+
+	iinfo->free_after_call = NULL;
+}
+
+static void
+clear_invocation_info (GPerlI11nInvocationInfo *iinfo)
+{
+	guint i;
+
+	for (i = 0 ; i < iinfo->n_args ; i++) {
+		g_base_info_unref ((GIBaseInfo *) iinfo->arg_types[i]);
+		g_base_info_unref ((GIBaseInfo *) iinfo->arg_infos[i]);
+	}
+
+	g_slist_free (iinfo->free_after_call);
+	iinfo->free_after_call = NULL;
+
+	/* The actual callback infos might be needed later, so we cannot free
+	 * them here. */
+	g_slist_free (iinfo->callback_infos);
+	iinfo->callback_infos = NULL;
+
+	g_slist_foreach (iinfo->array_infos, (GFunc) g_free, NULL);
+	g_slist_free (iinfo->array_infos);
+	iinfo->array_infos = NULL;
+
+	g_base_info_unref ((GIBaseInfo *) iinfo->return_type_info);
+	iinfo->return_type_info = NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct {
+	GFunc func;
+	gpointer data;
+} FreeClosure;
+
+static void
+free_after_call (GPerlI11nInvocationInfo *iinfo, GFunc func, gpointer data)
+{
+	FreeClosure *closure = g_new (FreeClosure, 1);
+	closure->func = func;
+	closure->data = data;
+	iinfo->free_after_call
+		= g_slist_prepend (iinfo->free_after_call, closure);
+}
+
+static void
+_invoke_free_closure (FreeClosure *closure)
+{
+	closure->func (closure->data, NULL);
+	g_free (closure);
+}
+
+static void
+invoke_free_after_call_handlers (GPerlI11nInvocationInfo *iinfo)
+{
+	/* We free the FreeClosures themselves directly after invoking them.  The list
+	   is freed in clear_invocation_info. */
+	g_slist_foreach (iinfo->free_after_call,
+	                 (GFunc) _invoke_free_closure, NULL);
+}
@@ -215,7 +215,8 @@ arg_to_sv (GIArgument * arg,
 		SV *sv;
 		gchar buffer[6];
 		gint length = g_unichar_to_utf8 (arg->v_uint32, buffer);
-		sv = newSVpv (buffer, length);
+		g_assert (length >= 0);
+		sv = newSVpv (buffer, (STRLEN) length);
 		SvUTF8_on (sv);
 		return sv;
 	    }
@@ -42,10 +42,14 @@ array_to_sv (GITypeInfo *info,
 	} else {
 		length = g_type_info_get_array_fixed_size (info);
 		if (length < 0) {
-			guint length_pos = g_type_info_get_array_length (info);
+			SV *conversion_sv;
+			gint length_pos = g_type_info_get_array_length (info);
 			g_assert (iinfo && iinfo->aux_args);
-			/* FIXME: Is it OK to always use v_size here? */
-			length = iinfo->aux_args[length_pos].v_size;
+			conversion_sv = arg_to_sv (&(iinfo->aux_args[length_pos]),
+			                           iinfo->arg_types[length_pos],
+			                           GI_TRANSFER_NOTHING, NULL);
+			length = SvIV (conversion_sv);
+			SvREFCNT_dec (conversion_sv);
 		}
 	}
 
@@ -67,7 +71,7 @@ array_to_sv (GITypeInfo *info,
 	for (i = 0; i < length; i++) {
 		GIArgument *arg;
 		SV *value;
-		arg = pointer + i * item_size;
+		arg = pointer + ((gsize) i) * item_size;
 		value = arg_to_sv (arg, param_info, item_transfer, iinfo);
 		if (value)
 			av_push (av, value);
@@ -91,7 +95,8 @@ sv_to_array (GITransfer transfer,
 	GITransfer item_transfer;
 	GITypeInfo *param_info;
 	GITypeTag param_tag;
-	gint i, length, length_pos;
+	gint length_pos;
+	gsize i, length;
 	GPerlI11nArrayInfo *array_info = NULL;
         GArray *array;
         gpointer raw_array;
@@ -133,7 +138,7 @@ sv_to_array (GITransfer transfer,
 
 	is_zero_terminated = g_type_info_is_zero_terminated (type_info);
 	item_size = size_of_type_info (param_info);
-	length = av_len (av) + 1;
+	length = (gsize) (av_len (av) + 1); /* av_len always returns at least -1 */
 	array = g_array_sized_new (is_zero_terminated, FALSE, item_size, length);
 
 	/* Arrays containing non-basic types as non-pointers need to be treated
@@ -117,7 +117,7 @@ callback_to_sv (GICallableInfo *interface, gpointer func, GPerlI11nInvocationInf
 	}
 
 	arg_info = g_callable_info_get_arg (invocation_info->interface,
-	                                    invocation_info->current_pos);
+	                                    (gint) invocation_info->current_pos);
 
 	dwarn ("      C callback at %d (%s)\n",
 	       invocation_info->current_pos,
@@ -1,5 +1,8 @@
 /* -*- mode: c; indent-tabs-mode: t; c-basic-offset: 8; -*- */
 
+void _store_enum (GIEnumInfo * info, gint value, GIArgument * arg);
+gint _retrieve_enum (GIEnumInfo * info, GIArgument * arg);
+
 static gpointer
 instance_sv_to_pointer (GICallableInfo *info, SV *sv)
 {
@@ -226,25 +229,27 @@ sv_to_interface (GIArgInfo * arg_info,
 
 	    case GI_INFO_TYPE_ENUM:
 	    {
+		gint value;
 		GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
 		if (G_TYPE_NONE == type) {
 			ccroak ("Could not handle unknown enum type %s",
 			        g_base_info_get_name (interface));
 		}
-		/* FIXME: Check storage type? */
-		arg->v_long = gperl_convert_enum (type, sv);
+		value = gperl_convert_enum (type, sv);
+		_store_enum (interface, value, arg);
 		break;
 	    }
 
 	    case GI_INFO_TYPE_FLAGS:
 	    {
+		gint value;
 		GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
 		if (G_TYPE_NONE == type) {
 			ccroak ("Could not handle unknown flags type %s",
 			        g_base_info_get_name (interface));
 		}
-		/* FIXME: Check storage type? */
-		arg->v_long = gperl_convert_flags (type, sv);
+		value = gperl_convert_flags (type, sv);
+		_store_enum (interface, value, arg);
 		break;
 	    }
 
@@ -312,25 +317,27 @@ interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own, GPerlI11nInvoc
 
 	    case GI_INFO_TYPE_ENUM:
 	    {
+		gint value;
 		GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
 		if (G_TYPE_NONE == type) {
 			ccroak ("Could not handle unknown enum type %s",
 			        g_base_info_get_name (interface));
 		}
-		/* FIXME: Is it right to just use v_long here? */
-		sv = gperl_convert_back_enum (type, arg->v_long);
+		value = _retrieve_enum (interface, arg);
+		sv = gperl_convert_back_enum (type, value);
 		break;
 	    }
 
 	    case GI_INFO_TYPE_FLAGS:
 	    {
+		gint value;
 		GType type = get_gtype ((GIRegisteredTypeInfo *) interface);
 		if (G_TYPE_NONE == type) {
 			ccroak ("Could not handle unknown flags type %s",
 			        g_base_info_get_name (interface));
 		}
-		/* FIXME: Is it right to just use v_long here? */
-		sv = gperl_convert_back_flags (type, arg->v_long);
+		value = _retrieve_enum (interface, arg);
+		sv = gperl_convert_back_flags (type, value);
 		break;
 	    }
 
@@ -348,3 +355,91 @@ interface_to_sv (GITypeInfo* info, GIArgument *arg, gboolean own, GPerlI11nInvoc
 
 	return sv;
 }
+
+/* ------------------------------------------------------------------------- */
+
+void
+_store_enum (GIEnumInfo * info, gint value, GIArgument * arg)
+{
+	GITypeTag tag = g_enum_info_get_storage_type (info);
+	switch (tag) {
+	    case GI_TYPE_TAG_BOOLEAN:
+		arg->v_boolean = (gboolean) value;
+		break;
+
+	    case GI_TYPE_TAG_INT8:
+		arg->v_int8 = (gint8) value;
+		break;
+
+	    case GI_TYPE_TAG_UINT8:
+		arg->v_uint8 = (guint8) value;
+		break;
+
+	    case GI_TYPE_TAG_INT16:
+		arg->v_int16 = (gint16) value;
+		break;
+
+	    case GI_TYPE_TAG_UINT16:
+		arg->v_uint16 = (guint16) value;
+		break;
+
+	    case GI_TYPE_TAG_INT32:
+		arg->v_int32 = (gint32) value;
+		break;
+
+	    case GI_TYPE_TAG_UINT32:
+		arg->v_uint32 = (guint32) value;
+		break;
+
+	    case GI_TYPE_TAG_INT64:
+		arg->v_int64 = (gint64) value;
+		break;
+
+	    case GI_TYPE_TAG_UINT64:
+		arg->v_uint64 = (guint64) value;
+		break;
+
+	    default:
+		ccroak ("Unhandled enumeration type %s (%d) encountered",
+		        g_type_tag_to_string (tag), tag);
+	}
+}
+
+gint
+_retrieve_enum (GIEnumInfo * info, GIArgument * arg)
+{
+	GITypeTag tag = g_enum_info_get_storage_type (info);
+	switch (tag) {
+	    case GI_TYPE_TAG_BOOLEAN:
+		return (gint) arg->v_boolean;
+
+	    case GI_TYPE_TAG_INT8:
+		return (gint) arg->v_int8;
+
+	    case GI_TYPE_TAG_UINT8:
+		return (gint) arg->v_uint8;
+
+	    case GI_TYPE_TAG_INT16:
+		return (gint) arg->v_int16;
+
+	    case GI_TYPE_TAG_UINT16:
+		return (gint) arg->v_uint16;
+
+	    case GI_TYPE_TAG_INT32:
+		return (gint) arg->v_int32;
+
+	    case GI_TYPE_TAG_UINT32:
+		return (gint) arg->v_uint32;
+
+	    case GI_TYPE_TAG_INT64:
+		return (gint) arg->v_int64;
+
+	    case GI_TYPE_TAG_UINT64:
+		return (gint) arg->v_uint64;
+
+	    default:
+		ccroak ("Unhandled enumeration type %s (%d) encountered",
+		        g_type_tag_to_string (tag), tag);
+		return 0;
+	}
+}
@@ -19,7 +19,7 @@ use strict;
 use warnings;
 use Glib;
 
-our $VERSION = '0.024';
+our $VERSION = '0.025';
 
 use Carp;
 $Carp::Internal{(__PACKAGE__)}++;
@@ -5,15 +5,19 @@ BEGIN { require './t/inc/setup.pl' };
 use strict;
 use warnings;
 
-plan tests => 5;
+plan tests => 8;
 
 {
   is (Regress::test_int8 (-127), -127);
+  isa_ok (Regress::TestObj->constructor, 'Regress::TestObj');
 }
 
 {
   is (eval { Regress::test_int8 () }, undef);
   like ($@, qr/too few/);
+
+  is (eval { Regress::TestObj::constructor }, undef);
+  like ($@, qr/too few/);
 }
 
 {