+2008-10-21 Johan Bilien <jobi@via.ecp.fr>
+
+ Bug 557241 – "throws" flag for functions
+
+ * tests/scanner/drawable-1.0-expected.gir,
+ tests/scanner/drawable-injected-1.0-expected.gir,
+ tests/scanner/drawable.[ch]: add simple test for throwing
+ function (has GError ** as last argument)
+
+ * giscanner/ast.py: add a 'throws' flag to Function
+ * giscanner/glibtransformer.py: if a function's last paramerter is
+ a GError, set the 'throws' flag and remove that parameter
+ * giscanner/girwriter.py: write out the 'throws' attribute
+ * giscanner/girparser.py: support parsing the 'throws' attribute
+
+ * tests/repository/gitestthrows.c: add a simple test to check the
+ throws flag in a typelib and invoke the function
+
+ * girepository/ginfo.c, girepository/girnode.[ch],
+ girepository/girnode.h, girepository/girparser.c,
+ girepository/girepository.h: Add and parse the GI_FUNCTION_THROWS flag
+
+ * girepository/ginvoke.c: if a function throws, add a GError as last
+ arguments, and propagate the error to the invoker.
+
2008-10-21 Tommi Komulainen <tommi.komulainen@iki.fi>
* giscanner/transformer.py (_create_parameters): Warn if we see
if (blob->wraps_vfunc)
flags = flags | GI_FUNCTION_WRAPS_VFUNC;
+ if (blob->throws)
+ flags = flags | GI_FUNCTION_THROWS;
+
return flags;
}
GITypeInfo *tinfo;
GIArgInfo *ainfo;
gboolean is_method;
+ gboolean throws;
gint n_args, n_invoke_args, in_pos, out_pos, i;
gpointer *args;
gboolean success = FALSE;
+ GError *local_error;
symbol = g_function_info_get_symbol (info);
is_method = (g_function_info_get_flags (info) & GI_FUNCTION_IS_METHOD) != 0
&& (g_function_info_get_flags (info) & GI_FUNCTION_IS_CONSTRUCTOR) == 0;
+ throws = g_function_info_get_flags (info) & GI_FUNCTION_THROWS;
tinfo = g_callable_info_get_return_type ((GICallableInfo *)info);
rtype = get_ffi_type (tinfo);
}
else
n_invoke_args = n_args;
+
+ if (throws)
+ /* Add an argument for the GError */
+ n_invoke_args ++;
+
atypes = g_alloca (sizeof (ffi_type*) * n_invoke_args);
args = g_alloca (sizeof (gpointer) * n_invoke_args);
}
g_base_info_unref ((GIBaseInfo *)ainfo);
}
+
+ local_error = NULL;
+ if (throws)
+ {
+ gpointer address = &local_error;
+ args[n_invoke_args - 1] = &address;
+ atypes[n_invoke_args - 1] = &ffi_type_pointer;
+ }
+
if (in_pos < n_in_args)
{
g_set_error (error,
ffi_call (&cif, func, return_value, args);
- success = TRUE;
+ if (local_error)
+ {
+ g_propagate_error (error, local_error);
+ success = FALSE;
+ }
+ else
+ {
+ success = TRUE;
+ }
out:
return success;
}
GI_FUNCTION_IS_CONSTRUCTOR = 1 << 1,
GI_FUNCTION_IS_GETTER = 1 << 2,
GI_FUNCTION_IS_SETTER = 1 << 3,
- GI_FUNCTION_WRAPS_VFUNC = 1 << 4
+ GI_FUNCTION_WRAPS_VFUNC = 1 << 4,
+ GI_FUNCTION_THROWS = 1 << 5
} GIFunctionInfoFlags;
const gchar * g_function_info_get_symbol (GIFunctionInfo *info);
blob->getter = function->is_getter;
blob->constructor = function->is_constructor;
blob->wraps_vfunc = function->wraps_vfunc;
- blob->reserved = 0;
+ blob->throws = function->throws;
blob->index = 0;
blob->name = write_string (node->name, strings, data, offset2);
blob->symbol = write_string (function->symbol, strings, data, offset2);
gboolean is_getter;
gboolean is_constructor;
gboolean wraps_vfunc;
+ gboolean throws;
gchar *symbol;
const gchar *name;
const gchar *symbol;
const gchar *deprecated;
+ const gchar *throws;
GIrNodeFunction *function;
gboolean found = FALSE;
name = find_attribute ("name", attribute_names, attribute_values);
symbol = find_attribute ("c:identifier", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
+ throws = find_attribute ("throws", attribute_names, attribute_values);
if (name == NULL)
{
if (strcmp (element_name, "callback") == 0)
((GIrNode *)function)->type = G_IR_NODE_CALLBACK;
}
-
+
+ if (throws && strcmp (throws, "1") == 0)
+ function->throws = TRUE;
+ else
+ function->throws = FALSE;
+
if (ctx->current_node == NULL)
{
ctx->current_module->entries =
guint16 blob_type; /* 1 */
guint16 deprecated : 1;
- guint16 setter : 1;
+ guint16 setter : 1;
guint16 getter : 1;
guint16 constructor : 1;
guint16 wraps_vfunc : 1;
- guint16 reserved : 1;
+ guint16 throws : 1;
guint16 index :10;
guint32 name;
class Function(Node):
- def __init__(self, name, retval, parameters, symbol):
+ def __init__(self, name, retval, parameters, symbol, throws=None):
Node.__init__(self, name)
self.retval = retval
self.parameters = parameters
self.symbol = symbol
+ self.throws = not not throws
def __repr__(self):
return '%s(%r, %r, %r)' % (self.__class__.__name__,
node.attrib.get(_cns('type')))
else:
identifier = node.attrib.get(_cns('identifier'))
- return klass(name, retval, parameters, identifier)
+ throws = (node.attrib.get('throws') == '1')
+ return klass(name, retval, parameters, identifier, throws)
def _parse_struct(self, node):
if _glibns('type-name') in node.attrib:
attrs.append(('deprecated-version',
node.deprecated_version))
+ def _append_throws(self, func, attrs):
+ if func.throws:
+ attrs.append(('throws', '1'))
+
def _write_alias(self, alias):
attrs = [('name', alias.name), ('target', alias.target)]
if alias.ctype is not None:
attrs = [('name', func.name),
('c:identifier', func.symbol)]
self._append_deprecated(func, attrs)
+ self._append_throws(func, attrs)
with self.tagcontext(tag_name, attrs):
self._write_return_type(func.retval)
self._write_parameters(func.parameters)
and param.type.name == 'Object'))):
param.transfer = 'full'
+ def _adjust_throws(self, func):
+ if func.parameters == []:
+ return
+
+ last_param = func.parameters.pop()
+
+ if (last_param.type.name == 'GLib.Error' or
+ (self._namespace_name == 'GLib' and
+ last_param.type.name == 'Error')):
+ func.throws = True
+ else:
+ func.parameters.append(last_param)
+
def _resolve_function(self, func):
self._resolve_parameters(func.parameters)
func.retval.type = self._resolve_param_type(func.retval.type)
+ self._adjust_throws(func)
self._adjust_transfer(func.retval)
def _resolve_parameters(self, parameters):
AM_LDFLAGS = -module -avoid-version
LIBS = $(GOBJECT_LIBS)
-noinst_PROGRAMS = gitestrepo
+noinst_PROGRAMS = gitestrepo gitestthrows
gitestrepo_SOURCES = $(srcdir)/gitestrepo.c
gitestrepo_CPPFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
gitestrepo_LDADD = $(GIREPO_LIBS) $(top_builddir)/girepository/libgirepository.la
-TESTS = gitestrepo
-TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG)
\ No newline at end of file
+gitestthrows_SOURCES = $(srcdir)/gitestthrows.c
+gitestthrows_CPPFLAGS = $(GIREPO_CFLAGS) -I$(top_srcdir)/girepository
+gitestthrows_LDADD = $(GIREPO_LIBS) $(top_builddir)/girepository/libgirepository.la
+
+TESTS = gitestrepo gitestthrows
+TESTS_ENVIRONMENT=env top_builddir="$(top_builddir)" $(DEBUG)
--- /dev/null
+
+#include "girepository.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+
+int
+main(int argc, char **argv)
+{
+ GIRepository *repo;
+ gboolean ret;
+ GIBaseInfo *info;
+ char *girdir;
+ GArgument in_arg[1];
+ GArgument ret_arg;
+ GError *error;
+ gboolean invoke_return;
+
+ g_type_init ();
+
+ repo = g_irepository_get_default ();
+
+ girdir = g_build_filename (g_getenv ("top_builddir"), "gir", NULL);
+ g_irepository_prepend_search_path (girdir);
+ g_free (girdir);
+
+ error = NULL;
+ ret = g_irepository_require (repo, "GLib", NULL, 0, &error);
+ g_assert (ret);
+ g_assert (error == NULL);
+
+ info = g_irepository_find_by_name (repo, "GLib", "file_read_link");
+ g_assert (info != NULL);
+ g_assert (g_base_info_get_type (info) == GI_INFO_TYPE_FUNCTION);
+ g_assert (g_function_info_get_flags ((GIFunctionInfo *)info) & GI_FUNCTION_THROWS);
+
+ in_arg[0].v_string = g_strdup ("non-existent-file/hope");
+ error = NULL;
+ invoke_return = g_function_info_invoke ((GIFunctionInfo *)info,
+ in_arg,
+ 1,
+ NULL,
+ 0,
+ &ret_arg,
+ &error);
+ g_free(in_arg[0].v_string);
+
+ g_assert (invoke_return == FALSE);
+ g_assert (error != NULL);
+ g_assert (error->domain == G_FILE_ERROR);
+ g_assert (error->code == G_FILE_ERROR_NOENT);
+
+ exit(0);
+}
</parameter>
</parameters>
</method>
+ <method name="do_foo_maybe_throw"
+ c:identifier="test_drawable_do_foo_maybe_throw"
+ throws="1">
+ <return-value>
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="x">
+ <type name="int" c:type="int"/>
+ </parameter>
+ </parameters>
+ </method>
<field name="parent_instance">
<type name="GObject.Object" c:type="GObject"/>
</field>
</parameter>
</parameters>
</method>
+ <method name="do_foo_maybe_throw"
+ c:identifier="test_drawable_do_foo_maybe_throw"
+ throws="1">
+ <return-value>
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="x">
+ <type name="int" c:type="int"/>
+ </parameter>
+ </parameters>
+ </method>
<method name="get_width" c:identifier="girepo_test_drawable_get_width">
<return-value>
<type name="int" c:type="gint"/>
*width = 42;
*height = 42;
}
+
+void
+test_drawable_do_foo_maybe_throw (TestDrawable *drawable, int x, GError **error)
+{
+ if (x != 42)
+ g_set_error(error, 0, 12, "The answer should be 42!");
+}
void test_drawable_do_foo (TestDrawable *drawable, int x);
void test_drawable_get_origin (TestDrawable *drawable, int *x, int *y);
void test_drawable_get_size (TestDrawable *drawable, guint *width, guint *height);
+void test_drawable_do_foo_maybe_throw (TestDrawable *drawable, int x, GError **error);
struct _TestPixmapObjectClass
{