From 22580d644fece9a3c6cfa661650bdf0bcc31b8ea Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 13 May 2005 15:58:43 +0000 Subject: [PATCH] Make the generated C code compile. 2005-05-13 Matthias Clasen * src/compiler.c (format_output): Make the generated C code compile. * README: Point to a recent libffi snapshot. * tests/invoke/*: Some invoke tests. * src/Makefile: Add ginvoke.c and the necessary libffi information. * src/girepository.h (g_function_info_invoke): Add a GError argument. * src/ginvoke.c (g_function_info_invoke): Initial implementation of invoke functionality based on libffi. --- ChangeLog | 16 +++ README | 5 +- src/Makefile | 16 ++- src/compiler.c | 2 + src/ginvoke.c | 314 +++++++++++++++++++++++++++++++++++++++++++++++ src/girepository.h | 12 +- tests/invoke/Makefile | 28 +++++ tests/invoke/invoke.c | 158 ++++++++++++++++++++++++ tests/invoke/testfns.c | 27 ++++ tests/invoke/testfns.xml | 47 +++++++ 10 files changed, 619 insertions(+), 6 deletions(-) create mode 100644 src/ginvoke.c create mode 100644 tests/invoke/Makefile create mode 100644 tests/invoke/invoke.c create mode 100644 tests/invoke/testfns.c create mode 100644 tests/invoke/testfns.xml diff --git a/ChangeLog b/ChangeLog index 3a52729..dbc06c9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,21 @@ 2005-05-13 Matthias Clasen + * src/compiler.c (format_output): Make the generated + C code compile. + + * README: Point to a recent libffi snapshot. + + * tests/invoke/*: Some invoke tests. + + * src/Makefile: Add ginvoke.c and the necessary + libffi information. + + * src/girepository.h (g_function_info_invoke): Add + a GError argument. + + * src/ginvoke.c (g_function_info_invoke): Initial + implementation of invoke functionality based on libffi. + * src/gidlnode.c (g_idl_node_build_metadata): Pass the strings and types hash tables in the right order when recursing. diff --git a/README b/README index 1f07049..e17c5ad 100644 --- a/README +++ b/README @@ -3,7 +3,7 @@ This is a very first prototype of an introspection framework for GObject. The metadata format is described in metadata-format.txt, the XML IDL format follows the DTD in gidl.dtd. Look at the files in tests/ for IDL examples. -The code in src/ currently produces three things: +The code in src/ currently produces four things: - g-idl-compile, a metadata compiler. It converts one or more IDL files into one or more metadata blobs. It can either emit the raw metadata blob (--raw) or C code (--code). @@ -11,6 +11,9 @@ The code in src/ currently produces three things: - g-idl-generate, an IDL generator, using the repository API. It generates IDL files from binary metadata which can be in a shared object, or a raw metadata blob (--raw). +- a function to invoke functions, given the function info object. The + implementation is based on libffi (a recent snapshot of libffi can + be found at http://spindazzle.org/libffi-green.tar.gz). There are a number of IDL test files in test/, and a script to do roundtrip tests (IDL -> binary -> IDL). diff --git a/src/Makefile b/src/Makefile index fe96a1f..2a11a2e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,12 +1,20 @@ AR=ar CC=gcc -g -CFLAGS=`pkg-config --cflags glib-2.0 gobject-2.0` -LIBS=`pkg-config --libs glib-2.0 gobject-2.0` + +GLIB_CFLAGS=`pkg-config --cflags glib-2.0 gobject-2.0` +GLIB_LIBS=`pkg-config --libs glib-2.0 gobject-2.0` + +LIBFFI_CFLAGS=-I../libffi/include -DHAVE_LIBFFI +LIBFFI_LIBS=../libffi/.libs/libffi.a + +CFLAGS= $(GLIB_CFLAGS) $(LIBFFI_CFLAGS) +LIBS= $(GLIB_LIBS) $(LIBFFI_LIBS) LIBIREPOSITORY_OBJS = \ girepository.o \ gmetadata.o \ - ginfo.o + ginfo.o \ + ginvoke.o COMPILER_OBJS = \ gidlparser.o \ @@ -17,7 +25,7 @@ COMPILER_OBJS = \ GENERATE_OBJS = generate.o -all: libirepository.so g-idl-generate g-idl-compiler +all: libirepository.a libirepository.so g-idl-generate g-idl-compiler libirepository.so: $(LIBIREPOSITORY_OBJS) $(CC) -shared -o $@ $(LIBIREPOSITORY_OBJS) $(LIBS) diff --git a/src/compiler.c b/src/compiler.c index 3fc3424..ab74089 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -46,6 +46,8 @@ format_output (guchar *metadata, result = g_string_sized_new (6 * len); + g_string_append_printf (result, "#include \n\n"); + g_string_append_printf (result, "const unsigned char _G_METADATA[] = \n{"); for (i = 0; i < len; i++) diff --git a/src/ginvoke.c b/src/ginvoke.c new file mode 100644 index 0000000..f1161bf --- /dev/null +++ b/src/ginvoke.c @@ -0,0 +1,314 @@ +/* GObject introspection: Invoke functionality + * + * Copyright (C) 2005 Matthias Clasen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include + +#include +#include + +#include "girepository.h" + +GQuark +g_invoke_error_quark (void) +{ + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string ("g-invoke-error-quark"); + return quark; +} + + +#ifdef HAVE_LIBFFI + +#include "ffi.h" + +static ffi_type * +get_ffi_type (GITypeInfo *info) +{ + ffi_type *rettype; + + if (g_type_info_is_pointer (info)) + rettype = &ffi_type_pointer; + else + switch (g_type_info_get_tag (info)) + { + case GI_TYPE_TAG_VOID: + rettype = &ffi_type_void; + break; + case GI_TYPE_TAG_BOOLEAN: + rettype = &ffi_type_uint; + break; + case GI_TYPE_TAG_INT8: + rettype = &ffi_type_sint8; + break; + case GI_TYPE_TAG_UINT8: + rettype = &ffi_type_uint8; + break; + case GI_TYPE_TAG_INT16: + rettype = &ffi_type_sint16; + break; + case GI_TYPE_TAG_UINT16: + rettype = &ffi_type_uint16; + break; + case GI_TYPE_TAG_INT32: + rettype = &ffi_type_sint32; + break; + case GI_TYPE_TAG_UINT32: + rettype = &ffi_type_uint32; + break; + case GI_TYPE_TAG_INT64: + rettype = &ffi_type_sint64; + break; + case GI_TYPE_TAG_UINT64: + rettype = &ffi_type_uint64; + break; + case GI_TYPE_TAG_FLOAT: + rettype = &ffi_type_float; + break; + case GI_TYPE_TAG_DOUBLE: + rettype = &ffi_type_double; + break; + case GI_TYPE_TAG_STRING: + rettype = &ffi_type_pointer; + break; + case GI_TYPE_TAG_GSTRING: + rettype = &ffi_type_pointer; + break; + case GI_TYPE_TAG_INT: + rettype = &ffi_type_sint; + break; + case GI_TYPE_TAG_UINT: + rettype = &ffi_type_uint; + break; + case GI_TYPE_TAG_LONG: + rettype = &ffi_type_slong; + break; + case GI_TYPE_TAG_ULONG: + rettype = &ffi_type_ulong; + break; + case GI_TYPE_TAG_ARRAY: + case GI_TYPE_TAG_INTERFACE: + case GI_TYPE_TAG_GLIST: + case GI_TYPE_TAG_GSLIST: + case GI_TYPE_TAG_GHASH: + case GI_TYPE_TAG_ERROR: + rettype = &ffi_type_pointer; + break; + default: + g_assert_not_reached (); + } + + return rettype; +} + +/** + * g_function_info_invoke: + * @info: a #GIFunctionInfo describing the function to invoke + * @in_args: an array of #GArguments, 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 #GArguments, 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. This function uses dlsym() to obtain a pointer + * to the function, so the library or shared object containing the + * described function must either be linked to the caller, or must + * have been dlopen()ed before calling this function. + * + * Returns: %TRUE if the function has been invoked, %FALSE if an + * error occurred. + */ +gboolean +g_function_info_invoke (GIFunctionInfo *info, + const GArgument *in_args, + int n_in_args, + const GArgument *out_args, + int n_out_args, + GArgument *return_value, + GError **error) +{ + ffi_cif cif; + ffi_type *rtype; + ffi_type **atypes; + const gchar *symbol; + gpointer func; + GITypeInfo *tinfo; + GIArgInfo *ainfo; + gint n_args, in_pos, out_pos, i; + gpointer *args; + gboolean success = FALSE; + + symbol = g_function_info_get_symbol (info); + + func = dlsym (NULL, symbol); + + if (func == NULL) + { + gchar *msg; + + msg = dlerror (); + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + "Could not locate %s: %s", symbol, msg ? msg : "dlsym() failed"); + + return FALSE; + } + + tinfo = g_callable_info_get_return_type ((GICallableInfo *)info); + rtype = get_ffi_type (tinfo); + g_base_info_unref ((GIBaseInfo *)tinfo); + + n_args = g_callable_info_get_n_args ((GICallableInfo *)info); + atypes = g_new (ffi_type*, n_args); + args = g_new (gpointer, n_args); + + in_pos = 0; + out_pos = 0; + for (i = 0; i < n_args; i++) + { + 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] = 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"); + goto out; + } + + args[i] = (gpointer)&in_args[in_pos]; + in_pos++; + + break; + case GI_DIRECTION_OUT: + atypes[i] = &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"); + goto out; + } + + args[i] = (gpointer)&out_args[out_pos]; + out_pos++; + break; + case GI_DIRECTION_INOUT: + atypes[i] = &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"); + goto out; + } + + if (out_pos >= n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too few \"in\" arguments"); + goto out; + } + + args[i] = (gpointer)&in_args[in_pos]; + in_pos++; + out_pos++; + break; + default: + g_assert_not_reached (); + } + g_base_info_unref ((GIBaseInfo *)ainfo); + } + if (in_pos < n_in_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"in\" arguments"); + goto out; + } + if (out_pos < n_out_args) + { + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_ARGUMENT_MISMATCH, + "Too many \"out\" arguments"); + goto out; + } + + if (ffi_prep_cif (&cif, FFI_DEFAULT_ABI, n_args, rtype, atypes) != FFI_OK) + goto out; + + ffi_call (&cif, func, return_value, args); + + success = TRUE; + + out: + g_free (atypes); + g_free (args); + + return success; +} + +#else /* !HAVE_LIBFFI */ + +gboolean +g_function_info_invoke (GIFunctionInfo *info, + const GArgument *in_args, + int n_in_args, + const GArgument *out_args, + int n_out_args, + GArgument *return_value, + GError **error) +{ + g_set_error (error, + G_INVOKE_ERROR, + G_INVOKE_ERROR_FAILED, + "g_function_info_invoke() is not available"); + + return FALSE; +} + +#endif + diff --git a/src/girepository.h b/src/girepository.h index 7893250..56b883b 100644 --- a/src/girepository.h +++ b/src/girepository.h @@ -168,12 +168,22 @@ typedef union gpointer v_pointer; } GArgument; +#define G_INVOKE_ERROR (g_invoke_error_quark ()) + +typedef enum +{ + G_INVOKE_ERROR_FAILED, + G_INVOKE_ERROR_SYMBOL_NOT_FOUND, + G_INVOKE_ERROR_ARGUMENT_MISMATCH +} GInvokeError; + gboolean g_function_info_invoke (GIFunctionInfo *info, const GArgument *in_args, int n_in_args, const GArgument *out_args, int n_out_args, - GArgument *return_value); + GArgument *return_value, + GError **error); /* GICallableInfo */ diff --git a/tests/invoke/Makefile b/tests/invoke/Makefile new file mode 100644 index 0000000..57cea96 --- /dev/null +++ b/tests/invoke/Makefile @@ -0,0 +1,28 @@ +AR=ar +CC=gcc -g +CFLAGS=`pkg-config --cflags glib-2.0` -I../../libffi/include -I../../src +LIBS=`pkg-config --libs glib-2.0 gobject-2.0` + +TESTFNS_OBJS = \ + testfns.o \ + testfns-metadata.o + +INVOKE_OBJS = \ + invoke.o + +all: testfns.so invoke + +testfns.so: $(TESTFNS_OBJS) + $(CC) -shared -o $@ $(TESTFNS_OBJS) $(LIBS) ../../src/libirepository.so + +testfns-metadata.c: testfns.xml + ../../src/g-idl-compiler testfns.xml -o testfns-metadata.c + +invoke: $(INVOKE_OBJS) + $(CC) -o $@ $(INVOKE_OBJS) $(LIBS) -ldl ../../src/libirepository.so + +.c.o: + $(CC) -c $< $(CFLAGS) + +clean: + rm -rf *.o *~ *.a *.so diff --git a/tests/invoke/invoke.c b/tests/invoke/invoke.c new file mode 100644 index 0000000..5e093ff --- /dev/null +++ b/tests/invoke/invoke.c @@ -0,0 +1,158 @@ +#include +#include + +#include +#include + +int +main (int argc, char *argv[]) +{ + const gchar *testfns = "./testfns.so"; + void *handle; + GIRepository *rep; + GIBaseInfo *info; + GIFunctionInfo *function; + GArgument in_args[3]; + GArgument out_args[3]; + GArgument retval; + gint res; + gchar *blurb; + gint len; + GError *error = NULL; + + g_type_init (); + + rep = g_irepository_get_default (); + + g_print ("before dlopening %s: %d infos in the repository\n", + testfns, + g_irepository_get_n_infos (rep, "test")); + + handle = dlopen (testfns, RTLD_NOW|RTLD_GLOBAL); + if (!handle) + g_print ("dlopen failed: %s\n", dlerror ()); + + g_print ("after dlopening %s: %d infos in the repository\n", + testfns, + g_irepository_get_n_infos (rep, "test")); + + /* test1 calculates x + 4, + * taking x as an in parameter + * and returning the result + */ + info = g_irepository_find_by_name (rep, "test", "test1"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + retval.v_int = 0; + in_args[0].v_int = 4; + if (!g_function_info_invoke (function, in_args, 1, NULL, 0, &retval, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + g_assert (retval.v_int == 8); + g_base_info_unref (info); + + /* test2 calculates x + 4, + * taking x as an in parameter + * and storing the result in an out parameter + */ + info = g_irepository_find_by_name (rep, "test", "test2"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + in_args[0].v_int = 5; + res = 0; + out_args[0].v_pointer = &res; + if (!g_function_info_invoke (function, in_args, 1, out_args, 1, &retval, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_assert (res == 9); + g_base_info_unref (info); + + /* test3 calculates x + 4, + * taking x as an inout parameter + * and storing the result in the same parameter + */ + info = g_irepository_find_by_name (rep, "test", "test3"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + res = 6; + in_args[0].v_pointer = out_args[0].v_pointer = &res; + if (!g_function_info_invoke (function, in_args, 1, out_args, 1, &retval, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_assert (res == 10); + g_base_info_unref (info); + + /* test4 prints out a string + */ + info = g_irepository_find_by_name (rep, "test", "test4"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + in_args[0].v_pointer = "hello world\n"; + if (!g_function_info_invoke (function, in_args, 1, NULL, 0, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_base_info_unref (info); + + /* test5 returns a string and a length + */ + info = g_irepository_find_by_name (rep, "test", "test5"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + blurb = NULL; + len = 0; + out_args[0].v_pointer = &blurb; + out_args[1].v_pointer = &len; + if (!g_function_info_invoke (function, NULL, 0, out_args, 2, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_assert (strcmp (blurb, "hey there") == 0); + g_assert (len == strlen (blurb)); + g_base_info_unref (info); + + /* test error handling */ + + /* test6 is not implemented */ + info = g_irepository_find_by_name (rep, "test", "test6"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + if (!g_function_info_invoke (function, NULL, 0, NULL, 0, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_base_info_unref (info); + g_clear_error (&error); + + /* too few in arguments */ + info = g_irepository_find_by_name (rep, "test", "test2"); + g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION); + function = (GIFunctionInfo *)info; + + if (!g_function_info_invoke (function, NULL, 0, NULL, 0, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_clear_error (&error); + + /* too few out arguments */ + if (!g_function_info_invoke (function, in_args, 1, NULL, 0, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_clear_error (&error); + + /* too many in arguments */ + if (!g_function_info_invoke (function, in_args, 2, out_args, 1, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_clear_error (&error); + + /* too many out arguments */ + if (!g_function_info_invoke (function, in_args, 1, out_args, 2, NULL, &error)) + g_print ("Invokation of %s failed: %s\n", g_base_info_get_name (info), error->message); + + g_clear_error (&error); + + return 0; +} diff --git a/tests/invoke/testfns.c b/tests/invoke/testfns.c new file mode 100644 index 0000000..2a61c6b --- /dev/null +++ b/tests/invoke/testfns.c @@ -0,0 +1,27 @@ +#include + +gint test1 (gint in) +{ + return in + 4; +} + +void test2 (gint in, gint *out) +{ + *out = in + 4; +} + +void test3 (gint *inout) +{ + *inout = *inout + 4; +} + +void test4 (const gchar *blurb) +{ + g_printf (blurb); +} + +void test5 (gchar **blurb, gint *len) +{ + *blurb = g_strdup ("hey there"); + *len = strlen (*blurb); +} diff --git a/tests/invoke/testfns.xml b/tests/invoke/testfns.xml new file mode 100644 index 0000000..db1b44a --- /dev/null +++ b/tests/invoke/testfns.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- 2.7.4