[perl-Glib-Object-Introspection] Refactor function invocation



commit 3a3eb7052d1661f3f08a0cea3f9497679e816b25
Author: Torsten SchÃnfeld <kaffeetisch gmx de>
Date:   Fri Oct 7 19:31:27 2011 +0200

    Refactor function invocation

 GObjectIntrospection.xs |  484 ++++++++++++++++++++++++-----------------------
 1 files changed, 247 insertions(+), 237 deletions(-)
---
diff --git a/GObjectIntrospection.xs b/GObjectIntrospection.xs
index c66c777..3078ebb 100644
--- a/GObjectIntrospection.xs
+++ b/GObjectIntrospection.xs
@@ -2515,6 +2515,245 @@ generic_interface_finalize (gpointer iface, gpointer data)
 
 /* ------------------------------------------------------------------------- */
 
+void
+invoke_function (GIFunctionInfo *info,
+                 gpointer func_pointer,
+                 SV **sp, I32 ax, SV **mark, I32 items, /* these correspond to dXSARGS */
+                 UV internal_stack_offset)
+{
+	ffi_cif cif;
+	gpointer instance = NULL;
+	guint i;
+	GPerlI11nInvocationInfo iinfo = {0,};
+	guint n_return_values;
+	GIArgument return_value;
+	GError * local_error = NULL;
+	gpointer local_error_address = &local_error;
+
+	prepare_invocation_info (&iinfo, info, items, internal_stack_offset);
+
+	if (iinfo.is_method) {
+		instance = instance_sv_to_pointer (info, ST (0 + iinfo.stack_offset));
+		iinfo.arg_types[0] = &ffi_type_pointer;
+		iinfo.args[0] = &instance;
+	}
+
+	for (i = 0 ; i < iinfo.n_args ; i++) {
+		GIArgInfo * arg_info;
+		GITypeInfo * arg_type;
+		GITransfer transfer;
+		gboolean may_be_null;
+		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);
+		transfer = g_arg_info_get_ownership_transfer (arg_info);
+		may_be_null = g_arg_info_may_be_null (arg_info);
+		perl_stack_pos = i
+                               + iinfo.method_offset
+                               + iinfo.stack_offset
+                               + iinfo.dynamic_stack_offset;
+		ffi_stack_pos = i
+		              + iinfo.method_offset;
+
+		/* 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; */
+
+		dwarn ("  arg %d, tag: %d (%s), is_pointer: %d, is_automatic: %d\n",
+		       i,
+		       g_type_info_get_tag (arg_type),
+		       g_type_tag_to_string (g_type_info_get_tag (arg_type)),
+		       g_type_info_is_pointer (arg_type),
+		       iinfo.is_automatic_arg[i]);
+
+		/* FIXME: Generate a proper usage message if the user did not
+		 * supply enough arguments. */
+		current_sv = perl_stack_pos < items ? ST (perl_stack_pos) : &PL_sv_undef;
+
+		switch (g_arg_info_get_direction (arg_info)) {
+		    case GI_DIRECTION_IN:
+			if (iinfo.is_automatic_arg[i]) {
+				iinfo.dynamic_stack_offset--;
+#if GI_CHECK_VERSION (1, 29, 0)
+			} else if (g_arg_info_is_skip (arg_info)) {
+				iinfo.dynamic_stack_offset--;
+#endif
+			} else {
+				sv_to_arg (current_sv,
+				           &iinfo.in_args[i], arg_info, arg_type,
+				           transfer, may_be_null, &iinfo);
+			}
+			iinfo.arg_types[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 =
+					allocate_out_mem (arg_type);
+				iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
+				iinfo.args[ffi_stack_pos] = &iinfo.aux_args[i];
+			} else {
+				iinfo.out_args[i].v_pointer = &iinfo.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;
+			/* Adjust the dynamic stack offset so that this out
+			 * argument doesn't inadvertedly eat up an in argument. */
+			iinfo.dynamic_stack_offset--;
+			break;
+
+		    case GI_DIRECTION_INOUT:
+			iinfo.in_args[i].v_pointer =
+				iinfo.out_args[i].v_pointer =
+					&iinfo.aux_args[i];
+			if (iinfo.is_automatic_arg[i]) {
+				iinfo.dynamic_stack_offset--;
+#if GI_CHECK_VERSION (1, 29, 0)
+			} else if (g_arg_info_is_skip (arg_info)) {
+				iinfo.dynamic_stack_offset--;
+#endif
+			} else {
+				/* We pass iinfo.in_args[i].v_pointer here,
+				 * not &iinfo.in_args[i], so that the value
+				 * 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);
+			}
+			iinfo.out_arg_infos[i] = arg_type;
+			iinfo.arg_types[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++) {
+		GIArgInfo * arg_info;
+		if (!iinfo.is_automatic_arg[i])
+			continue;
+		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
+		switch (g_arg_info_get_direction (arg_info)) {
+		    case GI_DIRECTION_IN:
+			handle_automatic_arg (i, &iinfo.in_args[i], &iinfo);
+			break;
+		    case GI_DIRECTION_INOUT:
+			handle_automatic_arg (i, &iinfo.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;
+	}
+
+	/* 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))
+	{
+		clear_invocation_info (&iinfo);
+		ccroak ("Could not prepare a call interface");
+	}
+
+	ffi_call (&cif, func_pointer, &return_value, iinfo.args);
+
+	/* free call-scoped callback infos */
+	g_slist_foreach (iinfo.free_after_call,
+	                 (GFunc) release_callback, NULL);
+
+	if (local_error) {
+		gperl_croak_gerror (NULL, local_error);
+	}
+
+	/*
+	 * handle return values
+	 */
+	n_return_values = 0;
+
+	/* place return value and output args on the stack */
+	if (iinfo.has_return_value
+#if GI_CHECK_VERSION (1, 29, 0)
+	    && !g_callable_info_skip_return ((GICallableInfo *) info)
+#endif
+	   )
+	{
+		SV *value = arg_to_sv (&return_value,
+		                       iinfo.return_type_info,
+		                       iinfo.return_type_transfer,
+		                       &iinfo);
+		if (value) {
+			XPUSHs (sv_2mortal (value));
+			n_return_values++;
+		}
+	}
+
+	/* out args */
+	for (i = 0 ; i < iinfo.n_args ; i++) {
+		GIArgInfo * arg_info;
+		if (iinfo.is_automatic_arg[i])
+			continue;
+		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, 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
+		switch (g_arg_info_get_direction (arg_info)) {
+		    case GI_DIRECTION_OUT:
+		    case GI_DIRECTION_INOUT:
+		    {
+			GITransfer transfer;
+			SV *sv;
+			/* If we allocated the memory ourselves, we always own it. */
+			transfer = g_arg_info_is_caller_allocates (arg_info)
+			         ? GI_TRANSFER_CONTAINER
+			         : g_arg_info_get_ownership_transfer (arg_info);
+			sv = arg_to_sv (iinfo.out_args[i].v_pointer,
+			                iinfo.out_arg_infos[i],
+			                transfer,
+			                &iinfo);
+			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_invocation_info (&iinfo);
+
+	dwarn ("  number of return values: %d\n", n_return_values);
+
+	PUTBACK;
+}
+
+/* ------------------------------------------------------------------------- */
+
 MODULE = Glib::Object::Introspection	PACKAGE = Glib::Object::Introspection
 
 void
@@ -2786,255 +3025,26 @@ invoke (class, basename, namespace, method, ...)
 	UV internal_stack_offset = 4;
 	GIRepository *repository;
 	GIFunctionInfo *info;
-	ffi_cif cif;
-	gpointer func_pointer = NULL, instance = NULL;
+	gpointer func_pointer = NULL;
 	const gchar *symbol = NULL;
-	guint i;
-	GPerlI11nInvocationInfo iinfo = {0,};
-	guint n_return_values;
-	GIArgument return_value;
-	GError * local_error = NULL;
-	gpointer local_error_address = &local_error;
     PPCODE:
 	repository = g_irepository_get_default ();
 	info = get_function_info (repository, basename, namespace, method);
 	symbol = g_function_info_get_symbol (info);
-
 	if (!g_typelib_symbol (g_base_info_get_typelib((GIBaseInfo *) info),
 			       symbol, &func_pointer))
 	{
 		ccroak ("Could not locate symbol %s", symbol);
 	}
-
-	prepare_invocation_info (&iinfo, info, items, internal_stack_offset);
-
-	if (iinfo.is_method) {
-		instance = instance_sv_to_pointer (info, ST (0 + iinfo.stack_offset));
-		iinfo.arg_types[0] = &ffi_type_pointer;
-		iinfo.args[0] = &instance;
-	}
-
-	for (i = 0 ; i < iinfo.n_args ; i++) {
-		GIArgInfo * arg_info;
-		GITypeInfo * arg_type;
-		GITransfer transfer;
-		gboolean may_be_null;
-		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);
-		transfer = g_arg_info_get_ownership_transfer (arg_info);
-		may_be_null = g_arg_info_may_be_null (arg_info);
-		perl_stack_pos = i
-                               + iinfo.method_offset
-                               + iinfo.stack_offset
-                               + iinfo.dynamic_stack_offset;
-		ffi_stack_pos = i
-		              + iinfo.method_offset;
-
-		/* 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; */
-
-		dwarn ("  arg %d, tag: %d (%s), is_pointer: %d, is_automatic: %d\n",
-		       i,
-		       g_type_info_get_tag (arg_type),
-		       g_type_tag_to_string (g_type_info_get_tag (arg_type)),
-		       g_type_info_is_pointer (arg_type),
-		       iinfo.is_automatic_arg[i]);
-
-		/* FIXME: Generate a proper usage message if the user did not
-		 * supply enough arguments. */
-		current_sv = perl_stack_pos < items ? ST (perl_stack_pos) : &PL_sv_undef;
-
-		switch (g_arg_info_get_direction (arg_info)) {
-		    case GI_DIRECTION_IN:
-			if (iinfo.is_automatic_arg[i]) {
-				iinfo.dynamic_stack_offset--;
-#if GI_CHECK_VERSION (1, 29, 0)
-			} else if (g_arg_info_is_skip (arg_info)) {
-				iinfo.dynamic_stack_offset--;
-#endif
-			} else {
-				sv_to_arg (current_sv,
-				           &iinfo.in_args[i], arg_info, arg_type,
-				           transfer, may_be_null, &iinfo);
-			}
-			iinfo.arg_types[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 =
-					allocate_out_mem (arg_type);
-				iinfo.out_args[i].v_pointer = &iinfo.aux_args[i];
-				iinfo.args[ffi_stack_pos] = &iinfo.aux_args[i];
-			} else {
-				iinfo.out_args[i].v_pointer = &iinfo.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;
-			/* Adjust the dynamic stack offset so that this out
-			 * argument doesn't inadvertedly eat up an in argument. */
-			iinfo.dynamic_stack_offset--;
-			break;
-
-		    case GI_DIRECTION_INOUT:
-			iinfo.in_args[i].v_pointer =
-				iinfo.out_args[i].v_pointer =
-					&iinfo.aux_args[i];
-			if (iinfo.is_automatic_arg[i]) {
-				iinfo.dynamic_stack_offset--;
-#if GI_CHECK_VERSION (1, 29, 0)
-			} else if (g_arg_info_is_skip (arg_info)) {
-				iinfo.dynamic_stack_offset--;
-#endif
-			} else {
-				/* We pass iinfo.in_args[i].v_pointer here,
-				 * not &iinfo.in_args[i], so that the value
-				 * 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);
-			}
-			iinfo.out_arg_infos[i] = arg_type;
-			iinfo.arg_types[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++) {
-		GIArgInfo * arg_info;
-		if (!iinfo.is_automatic_arg[i])
-			continue;
-		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, i);
-		switch (g_arg_info_get_direction (arg_info)) {
-		    case GI_DIRECTION_IN:
-			handle_automatic_arg (i, &iinfo.in_args[i], &iinfo);
-			break;
-		    case GI_DIRECTION_INOUT:
-			handle_automatic_arg (i, &iinfo.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;
-	}
-
-	/* 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))
-	{
-		clear_invocation_info (&iinfo);
-		ccroak ("Could not prepare a call interface for %s", symbol);
-	}
-
-	ffi_call (&cif, func_pointer, &return_value, iinfo.args);
-
-	/* free call-scoped callback infos */
-	g_slist_foreach (iinfo.free_after_call,
-	                 (GFunc) release_callback, NULL);
-
-	if (local_error) {
-		gperl_croak_gerror (NULL, local_error);
-	}
-
-	/*
-	 * handle return values
-	 */
-	n_return_values = 0;
-
-	/* place return value and output args on the stack */
-	if (iinfo.has_return_value
-#if GI_CHECK_VERSION (1, 29, 0)
-	    && !g_callable_info_skip_return ((GICallableInfo *) info)
-#endif
-	   )
-	{
-		SV *value = arg_to_sv (&return_value,
-		                       iinfo.return_type_info,
-		                       iinfo.return_type_transfer,
-		                       &iinfo);
-		if (value) {
-			XPUSHs (sv_2mortal (value));
-			n_return_values++;
-		}
-	}
-
-	/* out args */
-	for (i = 0 ; i < iinfo.n_args ; i++) {
-		GIArgInfo * arg_info;
-		if (iinfo.is_automatic_arg[i])
-			continue;
-		arg_info = g_callable_info_get_arg ((GICallableInfo *) info, 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
-		switch (g_arg_info_get_direction (arg_info)) {
-		    case GI_DIRECTION_OUT:
-		    case GI_DIRECTION_INOUT:
-		    {
-			GITransfer transfer;
-			SV *sv;
-			/* If we allocated the memory ourselves, we always own it. */
-			transfer = g_arg_info_is_caller_allocates (arg_info)
-			         ? GI_TRANSFER_CONTAINER
-			         : g_arg_info_get_ownership_transfer (arg_info);
-			sv = arg_to_sv (iinfo.out_args[i].v_pointer,
-			                iinfo.out_arg_infos[i],
-			                transfer,
-			                &iinfo);
-			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_invocation_info (&iinfo);
+	invoke_function (info, func_pointer,
+	                 sp, ax, mark, items,
+	                 internal_stack_offset);
+	/* SPAGAIN since invoke_callable probably modified the stack pointer.
+	 * so we need to make sure that our implicit local variable 'sp' is
+	 * correct before the implicit PUTBACK happens. */
+	SPAGAIN;
 	g_base_info_unref ((GIBaseInfo *) info);
 
-	dwarn ("  number of return values: %d\n", n_return_values);
-
-	if (n_return_values == 0) {
-		XSRETURN_EMPTY;
-	} else if (n_return_values == 1) {
-		XSRETURN (1);
-	} else {
-		PUTBACK;
-		return;
-	}
-
 # --------------------------------------------------------------------------- #
 
 MODULE = Glib::Object::Introspection	PACKAGE = Glib::Object::Introspection::GValueWrapper



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]