summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/g-ir-scanner.160
-rw-r--r--gir/DBus-1.0.gir2
-rw-r--r--gir/DBusGLib-1.0.gir2
-rw-r--r--gir/GL-1.0.gir2
-rw-r--r--gir/Makefile.am12
-rw-r--r--gir/cairo-1.0.gir5
-rw-r--r--gir/fontconfig-2.0.gir2
-rw-r--r--gir/freetype2-2.0.gir2
-rw-r--r--gir/libxml2-2.0.gir2
-rw-r--r--gir/xfixes-4.0.gir2
-rw-r--r--gir/xft-2.0.gir2
-rw-r--r--gir/xlib-2.0.gir2
-rw-r--r--gir/xrandr-1.3.gir2
-rw-r--r--girepository/girmodule.c2
-rw-r--r--girepository/girparser.c109
-rw-r--r--girepository/gitypelib.c4
-rw-r--r--giscanner/Makefile.am6
-rw-r--r--giscanner/annotationparser.py734
-rw-r--r--giscanner/ast.py752
-rw-r--r--giscanner/codegen.py137
-rw-r--r--giscanner/dumper.py2
-rw-r--r--giscanner/gdumpparser.py455
-rw-r--r--giscanner/girparser.py487
-rw-r--r--giscanner/girwriter.py235
-rw-r--r--giscanner/glibast.py91
-rw-r--r--giscanner/glibtransformer.py1205
-rw-r--r--giscanner/introspectablepass.py159
-rw-r--r--giscanner/maintransformer.py933
-rw-r--r--giscanner/scannermain.py169
-rw-r--r--giscanner/sourcescanner.py10
-rw-r--r--giscanner/testcodegen.py119
-rw-r--r--giscanner/transformer.py749
-rw-r--r--giscanner/utils.py13
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/scanner/Annotation-1.0-expected.gir109
-rw-r--r--tests/scanner/Bar-1.0-expected.gir9
-rw-r--r--tests/scanner/Foo-1.0-expected.gir125
-rw-r--r--tests/scanner/GtkFrob-1.0-expected.gir5
-rw-r--r--tests/scanner/Makefile.am2
-rw-r--r--tests/scanner/Regress-1.0-expected.gir152
-rw-r--r--tests/scanner/TestInherit-1.0-expected.gir6
-rw-r--r--tests/scanner/Utility-1.0-expected.gir18
42 files changed, 3696 insertions, 3200 deletions
diff --git a/docs/g-ir-scanner.1 b/docs/g-ir-scanner.1
index 3b071e87..7e08d1d8 100644
--- a/docs/g-ir-scanner.1
+++ b/docs/g-ir-scanner.1
@@ -82,21 +82,30 @@ to pass the argument to this function to g_irepository_dump.
.B \--program-arg=ARG
Additional argument to pass to program for introspection.
.TP
-.B \--strip-prefix=PREFIX
-If this option is specified a prefix will be stripped from all functions.
-If not specified, the lower case version of the namespace will be used.
-Eg, a strip prefix of
-.B g_
-and a namespace set to
-.B GLib
-will export the function
-.B g_type_name
-as
-.B GLib.type_name.
+.B \--identifier-prefix=PREFIX
+This option may be specified multiple times. Each one
+gives a prefix that will be stripped from all C identifiers.
+If none specified, the namespace will be used.
+Eg, an identifier prefix of
+.B Foo
+will export the identifier
+.B typdef struct _FooBar FooBar;
+as
+.B Foo.Bar.
+.TP
+.B \--symbol-prefix=PREFIX
+This option may be specified multiple times. Each one
+gives a prefix that will be stripped from all C symbols.
+Eg, an symbol prefix of
+.B foo
+will export the symbol
+.B foo_bar_do_something
+as
+.B Foo.Bar.do_something.
.TP
.B \--output=FILENAME
Name of the file to output. Normally namespace + format extension.
-Eg, GLib.gir.
+Eg, GLib-2.0.gir.
.TP
.B \--pkg=PACKAGE
List of pkg-config packages to get compiler and linker flags from.
@@ -112,33 +121,6 @@ If not specified, the packages specified with --pkg= will be used.
.B \--verbose
Be verbose, include some debugging information.
.TP
-.B \--typelib-xml
-Convert the resulting xml to only output the types relevant
-to the typelib compiler. This is mainly useful for verifying the
-correctness of the typelib itself.
-.TP
-.B \--inject=FILENAME
-Injects a variant of a GIR file into the scanner. This is used to add
-custom functions to a GIR wrapping a library without modifying the upstream
-library itself. The Format of the inject file is similar to a GIR,
-but the root node is <injections> rather than <repository> and
-<inject path="..."> where ... is an xpath expression.
-
-Example:
-
- <inject path="namespace/class[@name='TestDrawable']">
- <method name="get_width" c:identifier="girepo_test_drawable_get_width">
- <return-value transfer-ownership="none">
- <type name="int" c:type="gint"/>
- </return-value>
- </method>
- </inject>
-
-The example above will add a new method called get_with to the TestDrawable class.
-.TP
-.B \--xpath-assertions=FILENAME
-Loads a list xpath assertions from FILENAME, this is useful for verifying
-that the GIR itself is properly generated.
.SH ENVIRONMENT VARIABLES
The g-ir-scanner uses the XDG_DATA_DIRS variable to check for dirs,
the gir's are located in XDG_DATA_DIRS/share/gir-1.0. It is normally
diff --git a/gir/DBus-1.0.gir b/gir/DBus-1.0.gir
index 331c2287..5eab4ce1 100644
--- a/gir/DBus-1.0.gir
+++ b/gir/DBus-1.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
diff --git a/gir/DBusGLib-1.0.gir b/gir/DBusGLib-1.0.gir
index c71d77b5..b9ba1327 100644
--- a/gir/DBusGLib-1.0.gir
+++ b/gir/DBusGLib-1.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
diff --git a/gir/GL-1.0.gir b/gir/GL-1.0.gir
index 0defbbf1..cf07454b 100644
--- a/gir/GL-1.0.gir
+++ b/gir/GL-1.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="GL" version="1.0">
diff --git a/gir/Makefile.am b/gir/Makefile.am
index 2a99986c..f08af00d 100644
--- a/gir/Makefile.am
+++ b/gir/Makefile.am
@@ -33,7 +33,7 @@ GLIB_LIBRARY=glib-2.0
endif
GLib_2_0_gir_LIBS = $(GLIB_LIBRARY)
-GLib_2_0_gir_SCANNERFLAGS = --strip-prefix=G --c-include="glib.h"
+GLib_2_0_gir_SCANNERFLAGS = --reparse-validate --identifier-prefix=G --symbol-prefix=g --symbol-prefix=glib --c-include="glib.h"
GLib_2_0_gir_PACKAGES = glib-2.0
GLib_2_0_gir_CFLAGS = \
-I$(GLIB_INCLUDEDIR) \
@@ -65,7 +65,7 @@ endif
GObject-2.0.gir: GLib-2.0.gir
GObject_2_0_gir_LIBS = $(GOBJECT_LIBRARY)
-GObject_2_0_gir_SCANNERFLAGS = --strip-prefix=G --c-include="glib-object.h" --add-include-path=.
+GObject_2_0_gir_SCANNERFLAGS = --reparse-validate --identifier-prefix=G --c-include="glib-object.h" --add-include-path=.
GObject_2_0_gir_PACKAGES = gobject-2.0
GObject_2_0_gir_INCLUDES = GLib-2.0
GObject_2_0_gir_CFLAGS = \
@@ -91,7 +91,7 @@ endif
GModule-2.0.gir: GLib-2.0.gir
GModule_2_0_gir_LIBS = $(GMODULE_LIBRARY)
-GModule_2_0_gir_SCANNERFLAGS = --strip-prefix=G --c-include="gmodule.h" --add-include-path=.
+GModule_2_0_gir_SCANNERFLAGS = --identifier-prefix=G --c-include="gmodule.h" --add-include-path=.
GModule_2_0_gir_PACKAGES = gmodule-2.0
GModule_2_0_gir_INCLUDES = GLib-2.0
GModule_2_0_gir_CFLAGS = \
@@ -122,7 +122,7 @@ endif
Gio-2.0.gir: GObject-2.0.gir
Gio_2_0_gir_LIBS = $(GIO_LIBRARY)
-Gio_2_0_gir_SCANNERFLAGS = --warn-all --strip-prefix=G --c-include="gio/gio.h" --add-include-path=.
+Gio_2_0_gir_SCANNERFLAGS = --reparse-validate --warn-all --identifier-prefix=G --c-include="gio/gio.h" --add-include-path=.
Gio_2_0_gir_PACKAGES = gio-2.0 $(GIO_UNIX_PACKAGES)
Gio_2_0_gir_INCLUDES = GObject-2.0
Gio_2_0_gir_CFLAGS = \
@@ -141,7 +141,9 @@ GIRepository-2.0.gir: GObject-2.0.gir $(top_builddir)/girepository/libgireposito
GIRepository_2_0_gir_LIBS = girepository-1.0
GIRepository_2_0_gir_SCANNERFLAGS = \
- --strip-prefix=g \
+ --warn-all \
+ --identifier-prefix=GI \
+ --symbol-prefix=g \
--c-include="girepository.h" \
--add-include-path=.
GIRepository_2_0_gir_PACKAGES = gobject-2.0
diff --git a/gir/cairo-1.0.gir b/gir/cairo-1.0.gir
index 52e4e769..05f1a783 100644
--- a/gir/cairo-1.0.gir
+++ b/gir/cairo-1.0.gir
@@ -1,9 +1,10 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
- <namespace name="cairo" version="1.0">
+ <namespace name="cairo" version="1.0"
+ c:prefix="cairo">
<record name="Context" c:type="cairo_t" foreign="1"/>
<record name="Surface" c:type="cairo_surface_t" foreign="1"/>
<record name="Matrix" c:type="cairo_matrix_t" foreign="1"/>
diff --git a/gir/fontconfig-2.0.gir b/gir/fontconfig-2.0.gir
index 024c57d0..192d7205 100644
--- a/gir/fontconfig-2.0.gir
+++ b/gir/fontconfig-2.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="fontconfig" version="2.0">
diff --git a/gir/freetype2-2.0.gir b/gir/freetype2-2.0.gir
index 222d3389..495726a2 100644
--- a/gir/freetype2-2.0.gir
+++ b/gir/freetype2-2.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="freetype2" version="2.0">
diff --git a/gir/libxml2-2.0.gir b/gir/libxml2-2.0.gir
index 3305d689..cd962d0e 100644
--- a/gir/libxml2-2.0.gir
+++ b/gir/libxml2-2.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="libxml2" version="2.0">
diff --git a/gir/xfixes-4.0.gir b/gir/xfixes-4.0.gir
index 4441e702..7a0fa18e 100644
--- a/gir/xfixes-4.0.gir
+++ b/gir/xfixes-4.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="xfixes" version="4.0">
diff --git a/gir/xft-2.0.gir b/gir/xft-2.0.gir
index db7f77c2..1117847d 100644
--- a/gir/xft-2.0.gir
+++ b/gir/xft-2.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="xft" version="2.0">
diff --git a/gir/xlib-2.0.gir b/gir/xlib-2.0.gir
index 3349fb07..10816521 100644
--- a/gir/xlib-2.0.gir
+++ b/gir/xlib-2.0.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="xlib" version="2.0">
diff --git a/gir/xrandr-1.3.gir b/gir/xrandr-1.3.gir
index 30e5c5fd..d8844da1 100644
--- a/gir/xrandr-1.3.gir
+++ b/gir/xrandr-1.3.gir
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0">
<namespace name="xrandr" version="1.3">
diff --git a/girepository/girmodule.c b/girepository/girmodule.c
index 70b1d2a8..ebee26c5 100644
--- a/girepository/girmodule.c
+++ b/girepository/girmodule.c
@@ -309,7 +309,7 @@ g_ir_module_build_typelib (GIrModule *module)
/* fill in header */
header = (Header *)data;
memcpy (header, G_IR_MAGIC, 16);
- header->major_version = 3;
+ header->major_version = 4;
header->minor_version = 0;
header->reserved = 0;
header->n_entries = n_entries;
diff --git a/girepository/girparser.c b/girepository/girparser.c
index 570e6488..bdb87811 100644
--- a/girepository/girparser.c
+++ b/girepository/girparser.c
@@ -33,7 +33,7 @@
/* This is a "major" version in the sense that it's only bumped
* for incompatible changes.
*/
-#define SUPPORTED_GIR_VERSION "1.1"
+#define SUPPORTED_GIR_VERSION "1.2"
struct _GIrParser
{
@@ -48,33 +48,33 @@ typedef enum
STATE_REPOSITORY,
STATE_INCLUDE,
STATE_C_INCLUDE,
- STATE_PACKAGE,
- STATE_NAMESPACE, /* 5 */
+ STATE_PACKAGE, /* 5 */
+ STATE_NAMESPACE,
STATE_ENUM,
STATE_BITFIELD,
STATE_FUNCTION,
- STATE_FUNCTION_RETURN,
- STATE_FUNCTION_PARAMETERS, /* 10 */
+ STATE_FUNCTION_RETURN, /* 10 */
+ STATE_FUNCTION_PARAMETERS,
STATE_FUNCTION_PARAMETER,
STATE_CLASS,
STATE_CLASS_FIELD,
- STATE_CLASS_PROPERTY,
- STATE_INTERFACE, /* 15 */
+ STATE_CLASS_PROPERTY, /* 15 */
+ STATE_INTERFACE,
STATE_INTERFACE_PROPERTY,
STATE_INTERFACE_FIELD,
STATE_IMPLEMENTS,
- STATE_PREREQUISITE,
- STATE_BOXED, /* 20 */
+ STATE_PREREQUISITE, /* 20 */
+ STATE_BOXED,
STATE_BOXED_FIELD,
STATE_STRUCT,
STATE_STRUCT_FIELD,
- STATE_ERRORDOMAIN,
- STATE_UNION, /* 25 */
+ STATE_ERRORDOMAIN, /* 25 */
+ STATE_UNION,
STATE_UNION_FIELD,
STATE_NAMESPACE_CONSTANT,
STATE_CLASS_CONSTANT,
- STATE_INTERFACE_CONSTANT,
- STATE_ALIAS, /* 30 */
+ STATE_INTERFACE_CONSTANT, /* 30 */
+ STATE_ALIAS,
STATE_TYPE,
STATE_ATTRIBUTE,
STATE_DOC,
@@ -779,6 +779,7 @@ start_function (GMarkupParseContext *context,
const gchar *throws;
GIrNodeFunction *function;
gboolean found = FALSE;
+ gboolean in_embedded_type;
switch (ctx->state)
{
@@ -787,15 +788,14 @@ start_function (GMarkupParseContext *context,
strcmp (element_name, "callback") == 0);
break;
case STATE_CLASS:
- found = strcmp (element_name, "function") == 0;
- /* fallthrough */
case STATE_BOXED:
case STATE_STRUCT:
case STATE_UNION:
- found = (found || strcmp (element_name, "constructor") == 0);
+ found = strcmp (element_name, "constructor") == 0;
/* fallthrough */
case STATE_INTERFACE:
found = (found ||
+ strcmp (element_name, "function") == 0 ||
strcmp (element_name, "method") == 0 ||
strcmp (element_name, "callback") == 0);
break;
@@ -809,12 +809,13 @@ start_function (GMarkupParseContext *context,
if (!found)
return FALSE;
- if (ctx->state == STATE_STRUCT_FIELD)
- ctx->in_embedded_type = TRUE;
+ in_embedded_type = ctx->state == STATE_STRUCT_FIELD;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION))
return TRUE;
+ ctx->in_embedded_type = in_embedded_type;
+
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);
@@ -964,15 +965,16 @@ parse_property_transfer (GIrNodeProperty *property,
}
}
-static void
-parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name)
+static gboolean
+parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *name,
+ GError **error)
{
if (transfer == NULL)
{
- if (!name)
- g_warning ("required attribute 'transfer-ownership' missing");
- else
- g_warning ("required attribute 'transfer-ownership' for function '%s'", name);
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "required attribute 'transfer-ownership' missing");
+ return FALSE;
}
else if (strcmp (transfer, "none") == 0)
{
@@ -991,8 +993,12 @@ parse_param_transfer (GIrNodeParam *param, const gchar *transfer, const gchar *n
}
else
{
- g_warning ("Unknown transfer-ownership value: %s", transfer);
+ g_set_error (error, G_MARKUP_ERROR,
+ G_MARKUP_ERROR_INVALID_CONTENT,
+ "invalid value for 'transfer-ownership': %s", transfer);
+ return FALSE;
}
+ return TRUE;
}
static gboolean
@@ -1078,7 +1084,8 @@ start_parameter (GMarkupParseContext *context,
else
param->allow_none = FALSE;
- parse_param_transfer (param, transfer, name);
+ if (!parse_param_transfer (param, transfer, name, error))
+ return FALSE;
if (scope && strcmp (scope, "call") == 0)
param->scope = GI_SCOPE_TYPE_CALL;
@@ -1142,14 +1149,25 @@ start_field (GMarkupParseContext *context,
const gchar *bits;
const gchar *branch;
GIrNodeField *field;
+ ParseState target_state;
+ gboolean introspectable;
switch (ctx->state)
{
case STATE_CLASS:
+ target_state = STATE_CLASS_FIELD;
+ break;
case STATE_BOXED:
+ target_state = STATE_BOXED_FIELD;
+ break;
case STATE_STRUCT:
+ target_state = STATE_STRUCT_FIELD;
+ break;
case STATE_UNION:
+ target_state = STATE_UNION_FIELD;
+ break;
case STATE_INTERFACE:
+ target_state = STATE_INTERFACE_FIELD;
break;
default:
return FALSE;
@@ -1158,6 +1176,13 @@ start_field (GMarkupParseContext *context,
if (strcmp (element_name, "field") != 0)
return FALSE;
+ g_assert (ctx->state != STATE_PASSTHROUGH);
+
+ /* We handle introspectability specially here; we replace with just gpointer
+ * for the type.
+ */
+ introspectable = introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state);
+
name = find_attribute ("name", attribute_names, attribute_values);
readable = find_attribute ("readable", attribute_names, attribute_values);
writable = find_attribute ("writable", attribute_names, attribute_values);
@@ -1172,7 +1197,15 @@ start_field (GMarkupParseContext *context,
field = (GIrNodeField *)g_ir_node_new (G_IR_NODE_FIELD,
ctx->current_module);
- ctx->current_typed = (GIrNode*) field;
+ if (introspectable)
+ {
+ ctx->current_typed = (GIrNode*) field;
+ }
+ else
+ {
+ field->type = parse_type (ctx, "gpointer");
+ }
+
((GIrNode *)field)->name = g_strdup (name);
/* Fields are assumed to be read-only.
* (see also girwriter.py and generate.c)
@@ -1193,7 +1226,6 @@ start_field (GMarkupParseContext *context,
iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, field);
- state_switch (ctx, STATE_CLASS_FIELD);
}
break;
case G_IR_NODE_INTERFACE:
@@ -1202,7 +1234,6 @@ start_field (GMarkupParseContext *context,
iface = (GIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, field);
- state_switch (ctx, STATE_INTERFACE_FIELD);
}
break;
case G_IR_NODE_BOXED:
@@ -1211,7 +1242,6 @@ start_field (GMarkupParseContext *context,
boxed = (GIrNodeBoxed *)CURRENT_NODE (ctx);
boxed->members = g_list_append (boxed->members, field);
- state_switch (ctx, STATE_BOXED_FIELD);
}
break;
case G_IR_NODE_STRUCT:
@@ -1220,7 +1250,6 @@ start_field (GMarkupParseContext *context,
struct_ = (GIrNodeStruct *)CURRENT_NODE (ctx);
struct_->members = g_list_append (struct_->members, field);
- state_switch (ctx, STATE_STRUCT_FIELD);
}
break;
case G_IR_NODE_UNION:
@@ -1242,7 +1271,6 @@ start_field (GMarkupParseContext *context,
union_->discriminators = g_list_append (union_->discriminators, constant);
}
- state_switch (ctx, STATE_UNION_FIELD);
}
break;
default:
@@ -2169,7 +2197,8 @@ start_return_value (GMarkupParseContext *context,
state_switch (ctx, STATE_FUNCTION_RETURN);
transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
- parse_param_transfer (param, transfer, NULL);
+ if (!parse_param_transfer (param, transfer, NULL, error))
+ return FALSE;
switch (CURRENT_NODE (ctx)->type)
{
@@ -2979,8 +3008,9 @@ start_element_handler (GMarkupParseContext *context,
{
g_markup_parse_context_get_position (context, &line_number, &char_number);
if (!g_str_has_prefix (element_name, "c:"))
- g_printerr ("%s:%d:%d: warning: dropping to PASSTHROUGH\n",
- ctx->file_path, line_number, char_number);
+ g_printerr ("%s:%d:%d: warning: element %s from state %d is unknown, ignoring\n",
+ ctx->file_path, line_number, char_number, element_name,
+ ctx->state);
state_switch (ctx, STATE_PASSTHROUGH);
ctx->unknown_depth = 1;
}
@@ -3026,9 +3056,9 @@ require_one_of_end_elements (GMarkupParseContext *context,
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
- "Unexpected end tag '%s' on line %d char %d; current state=%d",
+ "Unexpected end tag '%s' on line %d char %d; current state=%d (prev=%d)",
actual_name,
- line_number, char_number, ctx->state);
+ line_number, char_number, ctx->state, ctx->prev_state);
return FALSE;
}
@@ -3379,6 +3409,7 @@ end_element_handler (GMarkupParseContext *context,
case STATE_PASSTHROUGH:
ctx->unknown_depth -= 1;
+ g_assert (ctx->unknown_depth >= 0);
if (ctx->unknown_depth == 0)
state_switch (ctx, ctx->prev_state);
break;
@@ -3486,7 +3517,9 @@ g_ir_parser_parse_string (GIrParser *parser,
g_markup_parse_context_free (context);
- return ctx.modules->data;
+ if (ctx.modules)
+ return ctx.modules->data;
+ return NULL;
}
/**
diff --git a/girepository/gitypelib.c b/girepository/gitypelib.c
index ef87c0e6..f47a743d 100644
--- a/girepository/gitypelib.c
+++ b/girepository/gitypelib.c
@@ -288,12 +288,12 @@ validate_header_basic (const guint8 *memory,
}
- if (header->major_version != 3 || header->minor_version != 0)
+ if (header->major_version != 4 || header->minor_version != 0)
{
g_set_error (error,
G_TYPELIB_ERROR,
G_TYPELIB_ERROR_INVALID_HEADER,
- "Typelib version mismatch; expected 3, found %d",
+ "Typelib version mismatch; expected 4, found %d",
header->major_version);
return FALSE;
diff --git a/giscanner/Makefile.am b/giscanner/Makefile.am
index 642bcbc5..708fdf8a 100644
--- a/giscanner/Makefile.am
+++ b/giscanner/Makefile.am
@@ -36,18 +36,22 @@ pkgpyexec_PYTHON = \
annotationparser.py \
ast.py \
cachestore.py \
+ codegen.py \
config.py \
dumper.py \
+ introspectablepass.py \
girparser.py \
girwriter.py \
glibast.py \
- glibtransformer.py \
+ gdumpparser.py \
libtoolimporter.py \
minixpath.py \
odict.py \
+ primarytransformer.py \
shlibs.py \
scannermain.py \
sourcescanner.py \
+ testcodegen.py \
transformer.py \
utils.py \
xmlwriter.py
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 9e4340fa..fc5f77d6 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -22,23 +22,7 @@
import re
-from .ast import (Array, Bitfield, Callback, Class, Enum, Field, Function,
- Interface, List, Map, Parameter, Property, Record, Return,
- Type, Union, Varargs,
- default_array_types,
- BASIC_GIR_TYPES,
- PARAM_DIRECTION_INOUT,
- PARAM_DIRECTION_IN,
- PARAM_DIRECTION_OUT,
- PARAM_SCOPE_CALL,
- PARAM_SCOPE_ASYNC,
- PARAM_SCOPE_NOTIFIED,
- PARAM_TRANSFER_NONE,
- PARAM_TRANSFER_CONTAINER,
- PARAM_TRANSFER_FULL,
- TYPE_ANY, TYPE_NONE)
from .odict import odict
-from .glibast import GLibBoxed
# All gtk-doc comments needs to start with this:
_COMMENT_HEADER = '*\n '
@@ -73,9 +57,6 @@ OPT_DESTROY = 'destroy'
OPT_SKIP = 'skip'
OPT_FOREIGN = 'foreign'
-# Specific option values
-OPT_VAL_BITFIELD = 'bitfield'
-
# Array options - array specific annotations
OPT_ARRAY_FIXED_SIZE = 'fixed-size'
OPT_ARRAY_LENGTH = 'length'
@@ -85,10 +66,6 @@ OPT_SCOPE_ASYNC = 'async'
OPT_SCOPE_CALL = 'call'
OPT_SCOPE_NOTIFIED = 'notified'
-class InvalidAnnotationError(Exception):
- pass
-
-
class DocBlock(object):
def __init__(self, name, options):
@@ -153,16 +130,14 @@ class AnnotationParser(object):
OPTION_RE = re.compile(r'\([A-Za-z]+[^(]*\)')
RETURNS_RE = re.compile(r'^return(s?)( value)?:', re.IGNORECASE)
- def __init__(self, namespace, source_scanner, transformer):
+ def __init__(self, source_scanner):
self._blocks = {}
- self._namespace = namespace
- self._transformer = transformer
- for comment in source_scanner.get_comments():
- self._parse_comment(comment)
+ self._source_scanner = source_scanner
def parse(self):
- aa = AnnotationApplier(self._blocks, self._transformer)
- aa.parse(self._namespace)
+ for comment in self._source_scanner.get_comments():
+ self._parse_comment(comment)
+ return self._blocks
def _parse_comment(self, comment):
# We're looking for gtk-doc comments here, they look like this:
@@ -199,7 +174,6 @@ class AnnotationParser(object):
else:
block_name, block_options = block_header, {}
block = DocBlock(block_name, block_options)
- debug = block_name == 'annotation_object_compute_sum_n'
comment_lines = []
parsing_parameters = True
last_param_tag = None
@@ -311,701 +285,3 @@ class AnnotationParser(object):
opened = -1
return options
-
-
-class AnnotationApplier(object):
-
- def __init__(self, blocks, transformer):
- self._blocks = blocks
- self._transformer = transformer
-
- def _get_tag(self, block, tag_name):
- if block is None:
- return None
-
- return block.get(tag_name)
-
- def parse(self, namespace):
- self._namespace = namespace
- for node in namespace.nodes[:]:
- self._parse_node(node)
- del self._namespace
-
- # Boring parsing boilerplate.
-
- def _parse_node(self, node):
- if isinstance(node, Function):
- self._parse_function(node)
- elif isinstance(node, Enum):
- self._parse_enum(node)
- elif isinstance(node, Bitfield):
- self._parse_bitfield(node)
- elif isinstance(node, Class):
- self._parse_class(node)
- elif isinstance(node, Interface):
- self._parse_interface(node)
- elif isinstance(node, Callback):
- self._parse_callback(node)
- elif isinstance(node, Record):
- self._parse_record(node)
- elif isinstance(node, Union):
- self._parse_union(node)
- elif isinstance(node, GLibBoxed):
- self._parse_boxed(node)
-
- def _parse_class(self, class_):
- block = self._blocks.get(class_.type_name)
- self._parse_node_common(class_, block)
- self._parse_constructors(class_.constructors)
- self._parse_methods(class_, class_.methods)
- self._parse_vfuncs(class_, class_.virtual_methods)
- self._parse_methods(class_, class_.static_methods)
- self._parse_properties(class_, class_.properties)
- self._parse_signals(class_, class_.signals)
- self._parse_fields(class_, class_.fields)
- if block:
- class_.doc = block.comment
- self._parse_type_instance_tags(class_, block)
-
- def _parse_interface(self, interface):
- block = self._blocks.get(interface.type_name)
- self._parse_node_common(interface, block)
- self._parse_methods(interface, interface.methods)
- self._parse_vfuncs(interface, interface.virtual_methods)
- self._parse_properties(interface, interface.properties)
- self._parse_signals(interface, interface.signals)
- self._parse_fields(interface, interface.fields)
- if block:
- interface.doc = block.comment
-
- def _parse_record(self, record):
- block = self._blocks.get(record.symbol)
- self._parse_node_common(record, block)
- self._parse_constructors(record.constructors)
- self._parse_methods(record, record.methods)
- self._parse_fields(record, record.fields, block)
- if block:
- record.doc = block.comment
-
- def _parse_boxed(self, boxed):
- block = self._blocks.get(boxed.name)
- self._parse_node_common(boxed, block)
- self._parse_constructors(boxed.constructors)
- self._parse_methods(boxed, boxed.methods)
- if block:
- boxed.doc = block.comment
-
- def _parse_union(self, union):
- block = self._blocks.get(union.name)
- self._parse_node_common(union, block)
- self._parse_fields(union, union.fields, block)
- self._parse_constructors(union.constructors)
- self._parse_methods(union, union.methods)
- if block:
- union.doc = block.comment
-
- def _parse_enum(self, enum):
- block = self._blocks.get(enum.symbol)
- self._parse_node_common(enum, block)
- if block:
- enum.doc = block.comment
- type_opt = block.options.get(OPT_TYPE)
- if type_opt and type_opt.one() == OPT_VAL_BITFIELD:
- # This is hack, but hey, it works :-)
- enum.__class__ = Bitfield
-
- def _parse_bitfield(self, bitfield):
- block = self._blocks.get(bitfield.symbol)
- self._parse_node_common(bitfield, block)
- if block:
- bitfield.doc = block.comment
-
- def _parse_constructors(self, constructors):
- for ctor in constructors:
- self._parse_function(ctor)
-
- def _parse_fields(self, parent, fields, block=None):
- for field in fields:
- self._parse_field(parent, field, block)
-
- def _parse_properties(self, parent, properties):
- for prop in properties:
- self._parse_property(parent, prop)
-
- def _parse_methods(self, parent, methods):
- for method in methods:
- self._parse_method(parent, method)
-
- def _parse_vfuncs(self, parent, vfuncs):
- for vfunc in vfuncs:
- self._parse_vfunc(parent, vfunc)
-
- def _parse_signals(self, parent, signals):
- for signal in signals:
- self._parse_signal(parent, signal)
-
- def _parse_property(self, parent, prop):
- block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
- self._parse_node_common(prop, block)
- if block:
- prop.doc = block.comment
-
- transfer_tag = self._get_tag(block, TAG_TRANSFER)
- if transfer_tag is not None:
- options = {OPT_TRANSFER: Option(transfer_tag.value)}
- else:
- options = {}
- prop.transfer = self._extract_transfer(parent, prop, options)
-
- type_tag = self._get_tag(block, TAG_TYPE)
- if type_tag:
- prop.type = self._resolve(type_tag.value, prop.type)
-
- def _parse_callback(self, callback):
- block = self._blocks.get(callback.ctype)
- self._parse_node_common(callback, block)
- self._parse_params(callback, callback.parameters, block)
- self._parse_return(callback, callback.retval, block)
- if block:
- callback.doc = block.comment
-
- def _parse_callable(self, callable, block):
- self._parse_node_common(callable, block)
- for i, param in enumerate(callable.parameters):
- if (param.type.ctype != 'GDestroyNotify' and
- param.type.name != 'GLib.DestroyNotify'):
- continue
- if i < 2:
- break
- callback_param = callable.parameters[i-2]
- if callback_param.closure_index != -1:
- callback_param.scope = OPT_SCOPE_NOTIFIED
- callback_param.transfer = PARAM_TRANSFER_NONE
-
- self._parse_params(callable, callable.parameters, block)
- self._parse_return(callable, callable.retval, block)
- if block:
- callable.doc = block.comment
-
- def _parse_function(self, func):
- block = self._blocks.get(func.symbol)
- self._parse_callable(func, block)
- self._parse_rename_to_func(func, block)
-
- def _parse_signal(self, parent, signal):
- block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
- self._parse_node_common(signal, block)
- # We're only attempting to name the signal parameters if
- # the number of parameter tags (@foo) is the same or greater
- # than the number of signal parameters
- if block and len(block.tags) > len(signal.parameters):
- names = block.tags.items()
- else:
- names = []
- for i, param in enumerate(signal.parameters):
- if names:
- name, tag = names[i+1]
- param.name = name
- options = getattr(tag, 'options', {})
- param_type = options.get(OPT_TYPE)
- if param_type:
- param.type = self._resolve(param_type.one(), param.type)
- else:
- tag = None
- self._parse_param(signal, param, tag)
- self._parse_return(signal, signal.retval, block)
- if block:
- signal.doc = block.comment
-
- def _parse_method(self, parent, meth):
- block = self._blocks.get(meth.symbol)
- self._parse_function(meth)
- virtual = self._get_tag(block, TAG_VFUNC)
- if virtual:
- invoker_name = virtual.value
- matched = False
- for vfunc in parent.virtual_methods:
- if vfunc.name == invoker_name:
- matched = True
- vfunc.invoker = meth
- break
- if not matched:
- print "warning: unmatched virtual invoker %r for method %r" % \
- (invoker_name, meth.symbol)
-
- def _parse_vfunc(self, parent, vfunc):
- key = '%s::%s' % (parent.type_name, vfunc.name)
- self._parse_callable(vfunc, self._blocks.get(key))
- if vfunc.invoker:
- # We normally expect annotations like (element-type) to be
- # applied to the invoker.
- block = self._blocks.get(vfunc.invoker.symbol)
- self._parse_callable(vfunc, block)
-
- def _parse_field(self, parent, field, block=None):
- if isinstance(field, Callback):
- self._parse_callback(field)
- else:
- if not block:
- return
- tag = block.get(field.name)
- if not tag:
- return
- t = tag.options.get('type')
- if not t:
- return
- field.type.name = self._transformer.resolve_type_name(t.one())
-
- def _check_arg_annotations(self, parent, params, block):
- if block is None:
- return
- for tag in block.tags.keys():
- if tag == TAG_RETURNS:
- continue
- for param in params:
- if param.name == tag:
- break
- else:
- return
- #print 'WARNING: annotation for "%s" refers to unknown ' \
- # 'argument "%s"' % (parent.name, tag)
-
- def _parse_params(self, parent, params, block):
- self._check_arg_annotations(parent, params, block)
- for param in params:
- tag = self._get_tag(block, param.name)
- self._parse_param(parent, param, tag)
-
- def _parse_return(self, parent, return_, block):
- tag = self._get_tag(block, TAG_RETURNS)
- self._parse_param_ret_common(parent, return_, tag)
-
- def _get_parameter_index(self, parent, param_name, location_name):
- index = parent.get_parameter_index(param_name)
- if index is None:
- raise InvalidAnnotationError(
- "can't find parameter %s referenced by parameter %s of %r"
- % (param_name, location_name, parent.name))
-
- return index
-
- def _parse_param(self, parent, param, tag):
- options = getattr(tag, 'options', {})
- if isinstance(parent, Function):
- scope = options.get(OPT_SCOPE)
- if scope:
- param.scope = scope.one()
- if param.scope not in [PARAM_SCOPE_CALL,
- PARAM_SCOPE_ASYNC,
- PARAM_SCOPE_NOTIFIED]:
- raise InvalidAnnotationError(
- "scope for %s of %r is invalid (%r), must be one of "
- "call, async, notified."
- % (param.name, parent.name, param.scope))
- param.transfer = PARAM_TRANSFER_NONE
- elif (param.type.ctype == 'GAsyncReadyCallback' or
- param.type.name == 'Gio.AsyncReadyCallback'):
- param.scope = OPT_SCOPE_ASYNC
- param.transfer = PARAM_TRANSFER_NONE
-
- destroy = options.get(OPT_DESTROY)
- if destroy:
- param.destroy_index = self._get_parameter_index(parent,
- destroy.one(),
- param.name)
- self._fixup_param_destroy(parent, param)
- closure = options.get(OPT_CLOSURE)
- if closure:
- param.closure_index = self._get_parameter_index(parent,
- closure.one(),
- param.name)
- self._fixup_param_closure(parent, param)
- if isinstance(parent, Callback):
- if OPT_CLOSURE in options:
- # For callbacks, (closure) appears without an
- # argument, and tags a parameter that is a closure. We
- # represent it (weirdly) in the gir and typelib by
- # setting param.closure_index to its own index.
- param.closure_index = parent.get_parameter_index(param.name)
- self._fixup_param_closure(parent, param)
-
- self._parse_param_ret_common(parent, param, tag)
-
- def _fixup_param_destroy(self, parent, param):
- for p in parent.parameters:
- if p is not param and p.destroy_index == param.destroy_index:
- p.destroy_index = -1
-
- def _fixup_param_closure(self, parent, param):
- for p in parent.parameters:
- if p is not param and p.closure_index == param.closure_index:
- p.closure_index = -1
-
- def _parse_param_ret_common(self, parent, node, tag):
- options = getattr(tag, 'options', {})
- (node.direction, node.caller_allocates) = \
- self._extract_direction(node, options)
- container_type = self._extract_container_type(
- parent, node, options)
- if container_type is not None:
- node.type = container_type
- if node.direction is None:
- node.direction = self._guess_direction(node)
- node.caller_allocates = False
- node.transfer = self._extract_transfer(parent, node, options)
- param_type = options.get(OPT_TYPE)
- if param_type:
- node.type = self._resolve(param_type.one(), node.type)
-
- if (OPT_ALLOW_NONE in options or
- node.type.ctype == 'GCancellable*'):
- node.allow_none = True
-
- assert node.transfer is not None
- if tag is not None and tag.comment is not None:
- node.doc = tag.comment
-
- for key in options:
- if '.' in key:
- value = options.get(key)
- if value:
- node.attributes.append((key, value.one()))
-
- def _extract_direction(self, node, options):
- caller_allocates = False
- if (OPT_INOUT in options or
- OPT_INOUT_ALT in options):
- direction = PARAM_DIRECTION_INOUT
- elif OPT_OUT in options:
- subtype = options[OPT_OUT]
- if subtype is not None:
- subtype = subtype.one()
- direction = PARAM_DIRECTION_OUT
- if subtype in (None, ''):
- if (node.type.name not in BASIC_GIR_TYPES) and node.type.ctype:
- caller_allocates = '**' not in node.type.ctype
- else:
- caller_allocates = False
- elif subtype == 'caller-allocates':
- caller_allocates = True
- elif subtype == 'callee-allocates':
- caller_allocates = False
- else:
- raise InvalidAnnotationError(
- "out direction for %s is invalid (%r)" % (node, subtype))
- elif OPT_IN in options:
- direction = PARAM_DIRECTION_IN
- else:
- direction = node.direction
- return (direction, caller_allocates)
-
- def _guess_array(self, node):
- ctype = node.type.ctype
- if ctype is None:
- return False
- if not ctype.endswith('*'):
- return False
- if node.type.canonical in default_array_types:
- return True
- return False
-
- def _is_array_type(self, node):
- if node.type.name in ['GLib.Array', 'GLib.PtrArray',
- 'GLib.ByteArray']:
- return True
- if node.type.ctype in ['GArray*', 'GPtrArray*', 'GByteArray*']:
- return True
- return False
-
- def _extract_container_type(self, parent, node, options):
- has_element_type = OPT_ELEMENT_TYPE in options
- has_array = OPT_ARRAY in options
-
- if not has_array:
- has_array = self._is_array_type(node)
-
- # FIXME: This is a hack :-(
- if (not isinstance(node, Field) and
- (not has_element_type and
- (node.direction is None
- or isinstance(node, Return)
- or node.direction == PARAM_DIRECTION_IN))):
- if self._guess_array(node):
- has_array = True
-
- if has_array:
- container_type = self._parse_array(parent, node, options)
- elif has_element_type:
- container_type = self._parse_element_type(parent, node, options)
- else:
- container_type = None
-
- return container_type
-
- def _parse_array(self, parent, node, options):
- array_opt = options.get(OPT_ARRAY)
- if array_opt:
- array_values = array_opt.all()
- else:
- array_values = {}
-
- is_g_array = self._is_array_type(node)
-
- element_type = options.get(OPT_ELEMENT_TYPE)
- if element_type is not None:
- element_type_node = self._resolve(element_type.one())
- else:
- if is_g_array:
- element_type_node = None
- else:
- element_type_node = Type(node.type.name) # erase ctype
-
- if is_g_array:
- type_name = node.type.name
- else:
- type_name = None
-
- container_type = Array(type_name, node.type.ctype,
- element_type_node)
- container_type.is_const = node.type.is_const
- if OPT_ARRAY_ZERO_TERMINATED in array_values:
- container_type.zeroterminated = array_values.get(
- OPT_ARRAY_ZERO_TERMINATED) == '1'
- length = array_values.get(OPT_ARRAY_LENGTH)
- if length is not None:
- param_index = self._get_parameter_index(parent, length, node.name)
- container_type.length_param_index = param_index
- # For in parameters we're incorrectly deferring
- # char/unsigned char to utf8 when a length annotation
- # is specified.
- if (isinstance(node, Parameter) and
- node.type.name == 'utf8' and
- self._guess_direction(node) == PARAM_DIRECTION_IN and
- element_type is None):
- # FIXME: unsigned char/guchar should be uint8
- container_type.element_type = Type('gint8')
- container_type.size = array_values.get(OPT_ARRAY_FIXED_SIZE)
- return container_type
-
- def _resolve(self, type_str, orig_node=None):
- def grab_one(type_str, resolver, top_combiner, combiner):
- """Return a complete type, and the trailing string part after it.
- Use resolver() on each identifier, and combiner() on the parts of
- each complete type. (top_combiner is used on the top-most type.)"""
- bits = re.split(r'([,<>])', type_str, 1)
- first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
- args = [resolver(first)]
- if sep == '<':
- while sep != '>':
- next, rest = grab_one(rest, resolver, combiner, combiner)
- args.append(next)
- sep, rest = rest[0], rest[1:]
- else:
- rest = sep + rest
- return top_combiner(*args), rest
- def resolver(ident):
- return self._transformer.resolve_param_type(Type(ident))
- def combiner(base, *rest):
- if not rest:
- return base
- if (base.name in ['GLib.List', 'GLib.SList'] or
- base.ctype in ['GList*', 'GSList*']) and len(rest)==1:
- return List(base.name, base.ctype, *rest)
- if (base.name in ['GLib.HashTable'] or
- base.ctype in ['GHashTable*']) and len(rest)==2:
- return Map(base.name, base.ctype, *rest)
- print "WARNING: throwing away type parameters:", type_str
- return base
- def top_combiner(base, *rest):
- """For the top most type, recycle orig_node if possible."""
- if orig_node is not None:
- orig_node.name = base.name
- base = orig_node # preserve other properties of orig_node
- return combiner(base, *rest)
-
- result, rest = grab_one(type_str, resolver, top_combiner, combiner)
- if rest:
- print "WARNING: throwing away trailing part of type:", type_str
- return result
-
- def _parse_element_type(self, parent, node, options):
- element_type_opt = options.get(OPT_ELEMENT_TYPE)
- element_type = element_type_opt.flat()
- if (node.type.name in ['GLib.List', 'GLib.SList'] or
- node.type.ctype in ['GList*', 'GSList*']):
- assert len(element_type) == 1
- container_type = List(
- node.type.name,
- node.type.ctype,
- self._resolve(element_type[0]))
- elif (node.type.name in ['GLib.HashTable'] or
- node.type.ctype in ['GHashTable*']):
- assert len(element_type) == 2
- container_type = Map(
- node.type.name,
- node.type.ctype,
- self._resolve(element_type[0]),
- self._resolve(element_type[1]))
- elif self._is_array_type(node):
- container_type = Array(node.type.name,
- node.type.ctype,
- self._resolve(element_type[0]))
- else:
- print 'FIXME: unhandled element-type container:', node
- return container_type
-
- def _extract_transfer(self, parent, node, options):
- transfer_opt = options.get(OPT_TRANSFER)
- if transfer_opt is None:
- transfer = self._guess_transfer(node, options)
- else:
- transfer = transfer_opt.one()
- if transfer is None:
- transfer = PARAM_TRANSFER_FULL
- if transfer not in [PARAM_TRANSFER_NONE,
- PARAM_TRANSFER_CONTAINER,
- PARAM_TRANSFER_FULL]:
- raise InvalidAnnotationError(
- "transfer for %s of %r is invalid (%r), must be one of "
- "none, container, full." % (node, parent.name, transfer))
- return transfer
-
- def _parse_node_common(self, node, block):
- self._parse_version(node, block)
- self._parse_deprecated(node, block)
- self._parse_attributes(node, block)
- self._parse_skip(node, block)
- self._parse_foreign(node, block)
-
- def _parse_version(self, node, block):
- since_tag = self._get_tag(block, TAG_SINCE)
- if since_tag is None:
- return
- node.version = since_tag.value
-
- def _parse_deprecated(self, node, block):
- deprecated_tag = self._get_tag(block, TAG_DEPRECATED)
- if deprecated_tag is None:
- return
- value = deprecated_tag.value
- if ': ' in value:
- version, desc = value.split(': ')
- else:
- desc = value
- version = None
- node.deprecated = desc
- if version is not None:
- node.deprecated_version = version
-
- def _parse_attributes(self, node, block):
- annos_tag = self._get_tag(block, TAG_ATTRIBUTES)
- if annos_tag is None:
- return
- options = AnnotationParser.parse_options(annos_tag.value)
- for key, value in options.iteritems():
- if value:
- node.attributes.append((key, value.one()))
-
- def _parse_skip(self, node, block):
- if block is not None:
- if OPT_SKIP in block.options:
- node.skip = True
-
- def _parse_foreign(self, node, block):
- if block is not None:
- if OPT_FOREIGN in block.options:
- node.foreign = True
-
- def _parse_type_instance_tags(self, node, block):
- tag = self._get_tag(block, TAG_UNREF_FUNC)
- node.unref_func = tag.value if tag else None
- tag = self._get_tag(block, TAG_REF_FUNC)
- node.ref_func = tag.value if tag else None
- tag = self._get_tag(block, TAG_SET_VALUE_FUNC)
- node.set_value_func = tag.value if tag else None
- tag = self._get_tag(block, TAG_GET_VALUE_FUNC)
- node.get_value_func = tag.value if tag else None
-
- def _parse_rename_to_func(self, node, block):
- rename_to_tag = self._get_tag(block, TAG_RENAME_TO)
- if rename_to_tag is None:
- return
- new_name = rename_to_tag.value
-
- shadowed = []
-
- def shadowed_filter(n):
- if isinstance(n, Function) and n.symbol == new_name:
- shadowed.append(n)
- return False
- return True
-
- self._namespace.remove_matching(shadowed_filter)
- if len(shadowed) == 1:
- # method override; use the same (stripped) name as the overloaded
- # method referenced.
- # Note that 'g_timeout_add_full' may specify a new_name of
- # 'g_timeout_add' but the *real* name desired is the stripped name
- # of 'g_timeout_add' which is 'timeout_add' (for example).
- node.name = shadowed[0].name
- elif len(shadowed) == 0:
- # literal rename, to force a particular prefix strip or whatever
- # Example: the "nm-utils" package uses a "NM" prefix in most places
- # but some functions have an "nm_utils_" prefix; the 'Rename To:'
- # annotation in this case is used to strip the 'utils_' part off.
- node.name = new_name
- else:
- assert False # more than two shadowed methods? Shouldn't happen.
-
- def _guess_direction(self, node):
- if node.direction:
- return node.direction
- is_pointer = False
- if node.type.ctype:
- is_pointer = '*' in node.type.ctype
-
- if is_pointer and node.type.name in BASIC_GIR_TYPES:
- return PARAM_DIRECTION_OUT
-
- return PARAM_DIRECTION_IN
-
- def _guess_transfer(self, node, options):
- if node.transfer is not None:
- return node.transfer
-
- # Anything with 'const' gets none
- if node.type.is_const:
- return PARAM_TRANSFER_NONE
- elif node.type.name in [TYPE_NONE, TYPE_ANY]:
- return PARAM_TRANSFER_NONE
- elif isinstance(node.type, Varargs):
- return PARAM_TRANSFER_NONE
- elif isinstance(node, Parameter):
- if node.direction in [PARAM_DIRECTION_INOUT,
- PARAM_DIRECTION_OUT]:
- if node.caller_allocates:
- return PARAM_TRANSFER_NONE
- return PARAM_TRANSFER_FULL
- # This one is a hack for compatibility; the transfer
- # for string parameters really has no defined meaning.
- elif node.type.canonical == 'utf8':
- return PARAM_TRANSFER_FULL
- else:
- return PARAM_TRANSFER_NONE
- elif isinstance(node, Return):
- if (isinstance(node.type, Array) and
- node.type.element_type is not None and
- node.type.element_type.name == 'utf8'):
- return PARAM_TRANSFER_FULL
- elif (node.type.canonical in BASIC_GIR_TYPES or
- (node.type.canonical in [TYPE_NONE, TYPE_ANY] and
- node.type.is_const)):
- return PARAM_TRANSFER_NONE
- else:
- return PARAM_TRANSFER_FULL
- elif isinstance(node, Field):
- return PARAM_TRANSFER_NONE
- elif isinstance(node, Property):
- return PARAM_TRANSFER_NONE
- else:
- raise AssertionError(node)
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 8c7ea093..3bb6cf82 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -19,63 +19,173 @@
# Boston, MA 02111-1307, USA.
#
-"""AST nodes
-This file descbribes abstract data type nodes independent on the
-implementation language.
-
-These can later on be extended (eg subclassed) with additional information
-which is language/library/domain specific.
+from .odict import odict
+from .utils import to_underscores
+
+class Type(object):
+ """A Type can be either:
+* A reference to a node (target_giname)
+* A reference to a "fundamental" type like 'utf8'
+* A "foreign" type - this can be any string."
+If none are specified, then it's in an "unresolved" state.
+In this case, the ctype must be specified.
"""
+ def __init__(self,
+ ctype=None,
+ target_fundamental=None,
+ target_giname=None,
+ target_foreign=None,
+ _target_unknown=False,
+ is_const=False,
+ origin_symbol=None):
+ self.ctype = ctype
+ self.origin_symbol = origin_symbol
+ if _target_unknown:
+ assert isinstance(self, TypeUnknown)
+ elif target_fundamental:
+ assert target_giname is None
+ assert target_foreign is None
+ elif target_giname:
+ assert '.' in target_giname
+ assert target_fundamental is None
+ assert target_foreign is None
+ elif target_foreign:
+ assert ctype is not None
+ assert target_giname is None
+ assert target_fundamental is None
+ else:
+ assert ctype is not None
+ self.target_fundamental = target_fundamental
+ self.target_giname = target_giname
+ self.target_foreign = target_foreign
+ self.is_const = is_const
+
+ @property
+ def resolved(self):
+ return (self.target_fundamental or
+ self.target_giname or
+ self.target_foreign)
+
+ def get_giname(self):
+ assert self.target_giname is not None
+ return self.target_giname.split('.')[1]
+
+ def __cmp__(self, other):
+ if self.target_fundamental:
+ return cmp(self.target_fundamental, other.target_fundamental)
+ if self.target_giname:
+ return cmp(self.target_giname, other.target_giname)
+ if self.target_foreign:
+ return cmp(self.target_foreign, other.target_foreign)
+ return cmp(self.ctype, other.ctype)
+
+ def is_equiv(self, typeval):
+ """Return True if the specified types are compatible at
+ an introspection level, disregarding their C types.
+ A sequence may be given for typeval, in which case
+ this function returns True if the type is compatible with
+ any."""
+ if isinstance(typeval, (list, tuple)):
+ for val in typeval:
+ if self.is_equiv(val):
+ return True
+ return False
+ return self == typeval
+
+ def clone(self):
+ return Type(target_fundamental=self.target_fundamental,
+ target_giname=self.target_giname,
+ target_foreign=self.target_foreign,
+ ctype=self.ctype,
+ is_const=self.is_const)
+
+ def __str__(self):
+ if self.target_fundamental:
+ return self.target_fundamental
+ elif self.target_giname:
+ return self.target_giname
+ elif self.target_foreign:
+ return self.target_foreign
+
+ def __repr__(self):
+ if self.target_fundamental:
+ data = 'target_fundamental=%s, ' % (self.target_fundamental, )
+ elif self.target_giname:
+ data = 'target_giname=%s, ' % (self.target_giname, )
+ elif self.target_foreign:
+ data = 'target_foreign=%s, ' % (self.target_foreign, )
+ else:
+ data = ''
+ return '%s(%sctype=%s)' % (self.__class__.__name__, data, self.ctype)
+
+class TypeUnknown(Type):
+ def __init__(self):
+ Type.__init__(self, _target_unknown=True)
+
######
## Fundamental types
######
# Two special ones
-TYPE_NONE = 'none'
-TYPE_ANY = 'gpointer'
+TYPE_NONE = Type(target_fundamental='none', ctype='void')
+TYPE_ANY = Type(target_fundamental='gpointer', ctype='gpointer')
# "Basic" types
-TYPE_BOOLEAN = 'gboolean'
-TYPE_INT8 = 'gint8'
-TYPE_UINT8 = 'guint8'
-TYPE_INT16 = 'gint16'
-TYPE_UINT16 = 'guint16'
-TYPE_INT32 = 'gint32'
-TYPE_UINT32 = 'guint32'
-TYPE_INT64 = 'gint64'
-TYPE_UINT64 = 'guint64'
-TYPE_CHAR = 'gchar'
-TYPE_SHORT = 'gshort'
-TYPE_USHORT = 'gushort'
-TYPE_INT = 'gint'
-TYPE_UINT = 'guint'
-TYPE_LONG = 'glong'
-TYPE_ULONG = 'gulong'
+TYPE_BOOLEAN = Type(target_fundamental='gboolean', ctype='gboolean')
+TYPE_INT8 = Type(target_fundamental='gint8', ctype='gint8')
+TYPE_UINT8 = Type(target_fundamental='guint8', ctype='guint8')
+TYPE_INT16 = Type(target_fundamental='gint16', ctype='gint16')
+TYPE_UINT16 = Type(target_fundamental='guint16', ctype='guint16')
+TYPE_INT32 = Type(target_fundamental='gint32', ctype='gint32')
+TYPE_UINT32 = Type(target_fundamental='guint32', ctype='guint32')
+TYPE_INT64 = Type(target_fundamental='gint64', ctype='gint64')
+TYPE_UINT64 = Type(target_fundamental='guint64', ctype='guint64')
+TYPE_CHAR = Type(target_fundamental='gchar', ctype='gchar')
+TYPE_SHORT = Type(target_fundamental='gshort', ctype='gshort')
+TYPE_USHORT = Type(target_fundamental='gushort', ctype='gushort')
+TYPE_INT = Type(target_fundamental='gint', ctype='gint')
+TYPE_UINT = Type(target_fundamental='guint', ctype='guint')
+TYPE_LONG = Type(target_fundamental='glong', ctype='glong')
+TYPE_ULONG = Type(target_fundamental='gulong', ctype='gulong')
# C99 types
-TYPE_LONG_LONG = 'long long'
-TYPE_LONG_ULONG = 'unsigned long long'
-TYPE_FLOAT = 'gfloat'
-TYPE_DOUBLE = 'gdouble'
+TYPE_LONG_LONG = Type(target_fundamental='long long', ctype='long long')
+TYPE_LONG_ULONG = Type(target_fundamental='unsigned long long',
+ ctype='unsigned long long')
+TYPE_FLOAT = Type(target_fundamental='gfloat', ctype='gfloat')
+TYPE_DOUBLE = Type(target_fundamental='gdouble', ctype='gdouble')
# ?
-TYPE_LONG_DOUBLE = 'long double'
+TYPE_LONG_DOUBLE = Type(target_fundamental='long double',
+ ctype='long double')
+TYPE_UNICHAR = Type(target_fundamental='gunichar', ctype='gunichar')
# C types with semantics overlaid
-TYPE_GTYPE = 'GType'
-TYPE_STRING = 'utf8'
-TYPE_FILENAME = 'filename'
+TYPE_GTYPE = Type(target_fundamental='GType', ctype='GType')
+TYPE_STRING = Type(target_fundamental='utf8', ctype='gchar*')
+TYPE_FILENAME = Type(target_fundamental='filename', ctype='gchar*')
+
+TYPE_VALIST = Type(target_fundamental='va_list', ctype='va_list')
BASIC_GIR_TYPES = [TYPE_BOOLEAN, TYPE_INT8, TYPE_UINT8, TYPE_INT16,
TYPE_UINT16, TYPE_INT32, TYPE_UINT32, TYPE_INT64,
TYPE_UINT64, TYPE_CHAR, TYPE_SHORT, TYPE_USHORT, TYPE_INT,
TYPE_UINT, TYPE_LONG, TYPE_ULONG, TYPE_LONG_LONG,
TYPE_LONG_ULONG, TYPE_FLOAT, TYPE_DOUBLE,
- TYPE_LONG_DOUBLE, TYPE_GTYPE]
+ TYPE_LONG_DOUBLE, TYPE_UNICHAR, TYPE_GTYPE]
GIR_TYPES = [TYPE_NONE, TYPE_ANY]
GIR_TYPES.extend(BASIC_GIR_TYPES)
-GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME])
+GIR_TYPES.extend([TYPE_STRING, TYPE_FILENAME, TYPE_VALIST])
+
+INTROSPECTABLE_BASIC = list(GIR_TYPES)
+for v in [TYPE_NONE, TYPE_ANY,
+ TYPE_LONG_LONG, TYPE_LONG_ULONG,
+ TYPE_LONG_DOUBLE, TYPE_VALIST]:
+ INTROSPECTABLE_BASIC.remove(v)
type_names = {}
for typeval in GIR_TYPES:
- type_names[typeval] = typeval
+ type_names[typeval.target_fundamental] = typeval
+basic_type_names = {}
+for typeval in BASIC_GIR_TYPES:
+ basic_type_names[typeval.target_fundamental] = typeval
# C builtin
type_names['char'] = TYPE_CHAR
@@ -106,13 +216,12 @@ type_names['signed long long'] = TYPE_LONG_LONG
type_names['guchar'] = TYPE_UINT8
type_names['gchararray'] = TYPE_STRING
type_names['gchar*'] = TYPE_STRING
+type_names['goffset'] = TYPE_INT64
+type_names['gunichar2'] = TYPE_UINT16
type_names['gsize'] = TYPE_ULONG
type_names['gssize'] = TYPE_LONG
type_names['gconstpointer'] = TYPE_ANY
-# Some special C types that aren't scriptable, and we just squash
-type_names['va_list'] = TYPE_ANY
-
# C stdio, used in GLib public headers; squash this for now here
# until we move scanning into GLib and can (skip)
type_names['FILE*'] = TYPE_ANY
@@ -142,19 +251,6 @@ type_names['ssize_t'] = TYPE_LONG
# Obj-C
type_names['id'] = TYPE_ANY
-# These types, when seen by reference, are converted into an Array()
-# by default
-default_array_types = {}
-default_array_types['guint8*'] = TYPE_UINT8
-default_array_types['guchar*'] = TYPE_UINT8
-default_array_types['utf8*'] = TYPE_STRING
-default_array_types['char**'] = TYPE_STRING
-default_array_types['gchar**'] = TYPE_STRING
-
-# These types, when seen by reference, are interpreted as out parameters
-default_out_types = (TYPE_SHORT, TYPE_USHORT, TYPE_INT, TYPE_UINT,
- TYPE_LONG, TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE)
-
##
## Parameters
##
@@ -171,24 +267,189 @@ PARAM_TRANSFER_NONE = 'none'
PARAM_TRANSFER_CONTAINER = 'container'
PARAM_TRANSFER_FULL = 'full'
-def type_name_from_ctype(ctype):
- return type_names.get(ctype, ctype)
+class Namespace(object):
+ def __init__(self, name, version,
+ identifier_prefixes=None,
+ symbol_prefixes=None):
+ self.name = name
+ self.version = version
+ if identifier_prefixes:
+ self.identifier_prefixes = identifier_prefixes
+ else:
+ self.identifier_prefixes = [name]
+ if symbol_prefixes:
+ self.symbol_prefixes = symbol_prefixes
+ else:
+ ps = self.identifier_prefixes
+ self.symbol_prefixes = [to_underscores(p).lower() for p in ps]
+ self._names = odict() # Maps from GIName -> node
+ self._aliases = {} # Maps from GIName -> GIName
+ self._type_names = {} # Maps from GTName -> node
+ self._ctypes = {} # Maps from CType -> node
+ self._symbols = {} # Maps from function symbols -> Function
+
+ @property
+ def names(self):
+ return self._names
+
+ @property
+ def aliases(self):
+ return self._aliases
+
+ @property
+ def type_names(self):
+ return self._type_names
+
+ @property
+ def ctypes(self):
+ return self._ctypes
+
+ def type_from_name(self, name, ctype=None):
+ """Backwards compatibility method for older .gir files, which
+only use the 'name' attribute. If name refers to a fundamental type,
+create a Type object referncing it. If name is already a
+fully-qualified GIName like 'Foo.Bar', returns a Type targeting it .
+Otherwise a Type targeting name qualififed with the namespace name is
+returned."""
+ if name in type_names:
+ return Type(target_fundamental=name, ctype=ctype)
+ if '.' in name:
+ target = name
+ else:
+ target = '%s.%s' % (self.name, name)
+ return Type(target_giname=target, ctype=ctype)
+
+ def contains_ident(self, ident):
+ """Return True if this namespace should contain the given C
+identifier string."""
+ return any(ident.startswith(prefix) for prefix in self.identifier_prefixes)
+
+ def append(self, node, replace=False):
+ previous = self._names.get(node.name)
+ if previous is not None:
+ if not replace:
+ raise ValueError("Namespace conflict")
+ self.remove(previous)
+ # A layering violation...but oh well.
+ from .glibast import GLibBoxed
+ if isinstance(node, Alias):
+ self._aliases[node.name] = node
+ elif isinstance(node, (GLibBoxed, Interface, Class)):
+ self._type_names[node.type_name] = node
+ elif isinstance(node, Function):
+ self._symbols[node.symbol] = node
+ assert isinstance(node, Node)
+ assert node.namespace is None
+ node.namespace = self
+ self._names[node.name] = node
+ if hasattr(node, 'ctype'):
+ self._ctypes[node.ctype] = node
+ if hasattr(node, 'symbol'):
+ self._ctypes[node.symbol] = node
+
+ def remove(self, node):
+ from .glibast import GLibBoxed
+ if isinstance(node, Alias):
+ del self._aliases[node.name]
+ elif isinstance(node, (GLibBoxed, Interface, Class)):
+ del self._type_names[node.type_name]
+ del self._names[node.name]
+ node.namespace = None
+ if hasattr(node, 'ctype'):
+ del self._ctypes[node.ctype]
+ if isinstance(node, Function):
+ del self._symbols[node.symbol]
+
+ def float(self, node):
+ """Like remove(), but doesn't unset the node's namespace
+back-reference, and it's still possible to look up
+functions via get_by_symbol()."""
+ if isinstance(node, Function):
+ symbol = node.symbol
+ self.remove(node)
+ self._symbols[symbol] = node
+ node.namespace = self
+
+ def __iter__(self):
+ return iter(self._names)
+
+ def iteritems(self):
+ return self._names.iteritems()
+
+ def itervalues(self):
+ return self._names.itervalues()
+
+ def get(self, name):
+ return self._names.get(name)
+
+ def get_by_ctype(self, ctype):
+ return self._ctypes.get(ctype)
+
+ def get_by_symbol(self, symbol):
+ return self._symbols.get(symbol)
+
+ def walk(self, callback):
+ for node in self.itervalues():
+ node.walk(callback, [])
+
+class Include(object):
+ def __init__(self, name, version):
+ self.name = name
+ self.version = version
-class Node(object):
+ @classmethod
+ def from_string(cls, string):
+ return cls(*string.split('-', 1))
- def __init__(self, name=None):
- self.name = name
+ def __cmp__(self, other):
+ namecmp = cmp(self.name, other.name)
+ if namecmp != 0:
+ return namecmp
+ return cmp(self.version, other.version)
+
+ def __hash__(self):
+ return hash(str(self))
+
+ def __str__(self):
+ return '%s-%s' % (self.name, self.version)
+
+class Annotated(object):
+ """An object which has a few generic metadata
+properties."""
+ def __init__(self):
+ self.version = None
self.skip = False
self.introspectable = True
self.attributes = [] # (key, value)*
self.deprecated = None
self.deprecated_version = None
- self.version = None
+ self.doc = None
+
+class Node(Annotated):
+ """A node is a type of object which is uniquely identified by its
+(namespace, name) pair. When combined with a ., this is called a
+GIName. It's possible for nodes to contain or point to other nodes."""
+
+ c_name = property(lambda self: self.namespace.name + self.name)
+ gi_name = property(lambda self: '%s.%s' % (self.namespace.name, self.name))
+
+ def __init__(self, name=None):
+ Annotated.__init__(self)
+ self.namespace = None # Should be set later by Namespace.append()
+ self.name = name
self.foreign = False
self.file_positions = set()
+ def create_type(self):
+ """Create a Type object referencing this node."""
+ assert self.namespace is not None
+ return Type(target_giname=('%s.%s' % (self.namespace.name, self.name)))
+
def __cmp__(self, other):
+ nscmp = cmp(self.namespace, other.namespace)
+ if nscmp != 0:
+ return nscmp
return cmp(self.name, other.name)
def __repr__(self):
@@ -207,49 +468,17 @@ class Node(object):
if symbol.source_filename:
self.add_file_position(symbol.source_filename, symbol.line, -1)
-class Namespace(Node):
-
- def __init__(self, name, version):
- Node.__init__(self, name)
- self.version = version
- self.nodes = []
-
- def __repr__(self):
- return '%s(%r, %r, %r)' % (self.__class__.__name__, self.name,
- self.version, self.nodes)
-
- def remove_matching(self, pred):
-
- def recursive_pred(node):
- node.remove_matching_children(pred)
- return pred(node)
-
- self.nodes = filter(recursive_pred, self.nodes)
-
-class Include(Node):
-
- def __init__(self, name, version):
- Node.__init__(self, 'include')
- self.name = name
- self.version = version
-
- @classmethod
- def from_string(self, string):
- return Include(*string.split('-', 1))
-
- def __cmp__(self, other):
- if not isinstance(other, Include):
- return cmp(self, other)
- namecmp = cmp(self.name, other.name)
- if namecmp != 0:
- return namecmp
- return cmp(self.version, other.version)
-
- def __hash__(self):
- return hash((self.name, self.version))
+ def walk(self, callback, chain):
+ res = callback(self, chain)
+ assert res in (True, False), "Walk function must return boolean, not %r" % (res, )
+ if not res:
+ return False
+ chain.append(self)
+ self._walk(callback, chain)
+ chain.pop()
- def __str__(self):
- return '%s-%s' % (self.name, self.version)
+ def _walk(self, callback, chain):
+ pass
class Callable(Node):
@@ -258,30 +487,29 @@ class Callable(Node):
self.retval = retval
self.parameters = parameters
self.throws = not not throws
- self.doc = None
-
- def __repr__(self):
- return '%s(%r, %r, %r)' % (self.__class__.__name__,
- self.name, self.retval,
- self.parameters)
-
-class Function(Callable):
-
- def __init__(self, name, retval, parameters, symbol, throws=None):
- Callable.__init__(self, name, retval, parameters, throws)
- self.symbol = symbol
- self.is_method = False
- self.doc = None
def get_parameter_index(self, name):
for i, parameter in enumerate(self.parameters):
- if parameter.name == name:
- return i + int(self.is_method)
+ if parameter.argname == name:
+ return i
+ raise ValueError("Unknown argument %s" % (name, ))
def get_parameter(self, name):
for parameter in self.parameters:
- if parameter.name == name:
+ if parameter.argname == name:
return parameter
+ raise ValueError("Unknown argument %s" % (name, ))
+
+
+class Function(Callable):
+
+ def __init__(self, name, retval, parameters, throws, symbol):
+ Callable.__init__(self, name, retval, parameters, throws)
+ self.symbol = symbol
+ self.is_method = False
+ self.is_constructor = False
+ self.shadowed_by = None # C symbol string
+ self.shadows = None # C symbol string
class VFunction(Callable):
@@ -297,59 +525,72 @@ class VFunction(Callable):
return obj
-class Type(Node):
-
- def __init__(self, name, ctype=None):
- Node.__init__(self, name)
- self.ctype = ctype
- self.resolved = False
- self.is_const = False
- self.canonical = None
- self.derefed_canonical = None
-
class Varargs(Type):
def __init__(self):
- Type.__init__(self, '<varargs>')
+ Type.__init__(self, '<varargs>', target_fundamental='<varargs>')
class Array(Type):
-
- def __init__(self, name, ctype, element_type):
- if name is None:
- name = '<carray>'
- Type.__init__(self, name, ctype)
+ C = '<c>'
+ GLIB_ARRAY = 'GLib.Array'
+ GLIB_BYTEARRAY = 'GLib.ByteArray'
+ GLIB_PTRARRAY = 'GLib.PtrArray'
+
+ def __init__(self, array_type, element_type, **kwargs):
+ Type.__init__(self, target_fundamental='<array>',
+ **kwargs)
+ if (array_type is None or array_type == self.C):
+ self.array_type = self.C
+ else:
+ assert array_type in (self.GLIB_ARRAY,
+ self.GLIB_BYTEARRAY,
+ self.GLIB_PTRARRAY)
+ self.array_type = array_type
+ assert isinstance(element_type, Type)
self.element_type = element_type
self.zeroterminated = True
- self.length_param_index = -1
self.length_param_name = None
self.size = None
- def __repr__(self):
- return 'Array(%r, %r)' % (self.name, self.element_type, )
-
+ def clone(self):
+ arr = Array(self.array_type, self.element_type)
+ arr.element_type = self.element_type
+ arr.zeroterminated = self.zeroterminated
+ arr.length_param_name = self.length_param_name
+ arr.size = self.size
+ return arr
class List(Type):
- def __init__(self, name, ctype, element_type):
- Type.__init__(self, name, ctype)
+ def __init__(self, name, element_type, **kwargs):
+ Type.__init__(self, target_fundamental='<list>',
+ **kwargs)
+ self.name = name
+ assert isinstance(element_type, Type)
self.element_type = element_type
- def __repr__(self):
- return 'List(%r of %r)' % (self.name, self.element_type, )
-
+ def clone(self):
+ l = List(self.name, self.element_type)
+ l.element_type = self.element_type
+ l.zeroterminated = self.zeroterminated
+ l.length_param_name = self.length_param_name
+ l.size = self.size
+ return l
class Map(Type):
- def __init__(self, name, ctype, key_type, value_type):
- Type.__init__(self, name, ctype)
+ def __init__(self, key_type, value_type, **kwargs):
+ Type.__init__(self, target_fundamental='<map>', **kwargs)
+ assert isinstance(key_type, Type)
self.key_type = key_type
+ assert isinstance(value_type, Type)
self.value_type = value_type
- def __repr__(self):
- return 'Map(%r <%r,%r>)' % (self.name, self.key_type, self.value_type)
-
+ def clone(self):
+ m = Map(self.key_type, self.value_type)
+ return m
class Alias(Node):
@@ -358,42 +599,43 @@ class Alias(Node):
self.target = target
self.ctype = ctype
- def __repr__(self):
- return 'Alias(%r, %r)' % (self.name, self.target)
-
-class TypeContainer(Node):
+class TypeContainer(Annotated):
+ """A fundamental base class for Return and Parameter."""
- def __init__(self, name, typenode, transfer):
- Node.__init__(self, name)
+ def __init__(self, typenode, transfer):
+ Annotated.__init__(self)
self.type = typenode
- if transfer in [PARAM_TRANSFER_NONE, PARAM_TRANSFER_CONTAINER,
- PARAM_TRANSFER_FULL]:
+ if transfer is not None:
self.transfer = transfer
+ elif typenode.is_const:
+ self.transfer = PARAM_TRANSFER_NONE
else:
self.transfer = None
class Parameter(TypeContainer):
-
- def __init__(self, name, typenode, direction=None,
- transfer=None, allow_none=False, scope=None):
- TypeContainer.__init__(self, name, typenode, transfer)
- if direction in [PARAM_DIRECTION_IN, PARAM_DIRECTION_OUT,
- PARAM_DIRECTION_INOUT, None]:
- self.direction = direction
- else:
- self.direction = PARAM_DIRECTION_IN
-
- self.caller_allocates = False
+ """An argument to a function."""
+
+ def __init__(self, argname, typenode, direction=None,
+ transfer=None, allow_none=False, scope=None,
+ caller_allocates=False):
+ TypeContainer.__init__(self, typenode, transfer)
+ self.argname = argname
+ self.direction = direction
self.allow_none = allow_none
self.scope = scope
- self.closure_index = -1
- self.destroy_index = -1
- self.doc = None
+ self.caller_allocates = caller_allocates
+ self.closure_name = None
+ self.destroy_name = None
- def __repr__(self):
- return 'Parameter(%r, %r)' % (self.name, self.type)
+
+class Return(TypeContainer):
+ """A return value from a function."""
+
+ def __init__(self, rtype, transfer=None):
+ TypeContainer.__init__(self, rtype, transfer)
+ self.direction = PARAM_DIRECTION_OUT
class Enum(Node):
@@ -402,10 +644,6 @@ class Enum(Node):
Node.__init__(self, name)
self.symbol = symbol
self.members = members
- self.doc = None
-
- def __repr__(self):
- return 'Enum(%r, %r)' % (self.name, self.members)
class Bitfield(Node):
@@ -414,21 +652,18 @@ class Bitfield(Node):
Node.__init__(self, name)
self.symbol = symbol
self.members = members
- self.doc = None
-
- def __repr__(self):
- return 'Bitfield(%r, %r)' % (self.name, self.members)
-class Member(Node):
+class Member(Annotated):
def __init__(self, name, value, symbol):
- Node.__init__(self, name)
+ Annotated.__init__(self)
+ self.name = name
self.value = value
self.symbol = symbol
- def __repr__(self):
- return 'Member(%r, %r)' % (self.name, self.value)
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
class Record(Node):
@@ -439,44 +674,41 @@ class Record(Node):
self.constructors = []
self.symbol = symbol
self.disguised = disguised
- self.doc = None
self.methods = []
+ self.static_methods = []
+
+ def _walk(self, callback, chain):
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for func in self.methods:
+ func.walk(callback, chain)
+ for func in self.static_methods:
+ func.walk(callback, chain)
+ for field in self.fields:
+ if field.anonymous_node is not None:
+ field.anonymous_node.walk(callback, chain)
def remove_matching_children(self, pred):
self.fields = filter(pred, self.fields)
self.constructors = filter(pred, self.constructors)
self.methods = filter(pred, self.methods)
-# BW compat, remove
-Struct = Record
+class Field(Annotated):
-class Field(Node):
-
- def __init__(self, name, typenode, symbol, readable, writable, bits=None):
- Node.__init__(self, name)
+ def __init__(self, name, typenode, readable, writable, bits=None,
+ anonymous_node=None):
+ Annotated.__init__(self)
+ assert (typenode or anonymous_node)
+ self.name = name
self.type = typenode
- self.symbol = symbol
self.readable = readable
self.writable = writable
self.bits = bits
+ self.anonymous_node = anonymous_node
- def __repr__(self):
- if self.bits:
- return 'Field(%r, %r, %r)' % (self.name, self.type, self.bits)
- else:
- return 'Field(%r, %r)' % (self.name, self.type)
-
-
-class Return(TypeContainer):
-
- def __init__(self, rtype, transfer=None):
- TypeContainer.__init__(self, None, rtype, transfer)
- self.direction = PARAM_DIRECTION_OUT
- self.doc = None
-
- def __repr__(self):
- return 'Return(%r)' % (self.type, )
+ def __cmp__(self, other):
+ return cmp(self.name, other.name)
class Class(Node):
@@ -484,7 +716,12 @@ class Class(Node):
def __init__(self, name, parent, is_abstract):
Node.__init__(self, name)
self.ctype = name
+ self.c_symbol_prefix = None
self.parent = parent
+ # When we're in the scanner, we keep around a list
+ # of parents so that we can transparently fall back
+ # if there are 'hidden' parents
+ self.parent_chain = []
self.glib_type_struct = None
self.is_abstract = is_abstract
self.methods = []
@@ -494,7 +731,6 @@ class Class(Node):
self.constructors = []
self.properties = []
self.fields = []
- self.doc = None
def remove_matching_children(self, pred):
self.methods = filter(pred, self.methods)
@@ -502,87 +738,71 @@ class Class(Node):
self.properties = filter(pred, self.properties)
self.fields = filter(pred, self.fields)
- def __repr__(self):
- return '%s(%r, %r, %r)' % (
- self.__class__.__name__,
- self.name, self.parent, self.methods)
-
+ def _walk(self, callback, chain):
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.virtual_methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for field in self.fields:
+ if field.anonymous_node:
+ field.anonymous_node.walk(callback, chain)
class Interface(Node):
def __init__(self, name, parent):
Node.__init__(self, name)
+ self.c_symbol_prefix = None
self.parent = parent
+ self.parent_chain = []
self.methods = []
+ self.static_methods = []
self.virtual_methods = []
self.glib_type_struct = None
self.properties = []
self.fields = []
self.prerequisites = []
- self.doc = None
-
- def __repr__(self):
- return '%s(%r, %r)' % (
- self.__class__.__name__,
- self.name, self.methods)
+ def _walk(self, callback, chain):
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
+ for meth in self.virtual_methods:
+ meth.walk(callback, chain)
+ for field in self.fields:
+ if field.anonymous_node:
+ field.anonymous_node.walk(callback, chain)
class Constant(Node):
- def __init__(self, name, type_name, value):
+ def __init__(self, name, value_type, value):
Node.__init__(self, name)
- self.type = Type(type_name)
+ self.value_type = value_type
self.value = value
- def __repr__(self):
- return 'Constant(%r, %r, %r)' % (
- self.name, self.type, self.value)
+class Property(Node):
-class Property(TypeContainer):
-
- def __init__(self, name, type_name, readable, writable,
- construct, construct_only, ctype=None, transfer=None):
- self.type = Type(type_name, ctype)
- TypeContainer.__init__(self, name, self.type, transfer)
+ def __init__(self, name, typeobj, readable, writable,
+ construct, construct_only, transfer=None):
+ Node.__init__(self, name)
+ self.type = typeobj
self.readable = readable
self.writable = writable
self.construct = construct
self.construct_only = construct_only
- self.doc = None
-
- def __repr__(self):
- return '%s(%r, %r)' % (
- self.__class__.__name__,
- self.name, self.type)
+ self.transfer = PARAM_TRANSFER_NONE
-# FIXME: Inherit from Function
+class Callback(Callable):
-
-class Callback(Node):
-
- def __init__(self, name, retval, parameters, ctype=None):
- Node.__init__(self, name)
- self.retval = retval
- self.parameters = parameters
+ def __init__(self, name, retval, parameters, throws, ctype=None):
+ Callable.__init__(self, name, retval, parameters, throws)
self.ctype = ctype
- self.throws = False
- self.doc = None
-
- def get_parameter_index(self, name):
- for i, parameter in enumerate(self.parameters):
- if parameter.name == name:
- return i
-
- def get_parameter(self, name):
- for parameter in self.parameters:
- if parameter.name == name:
- return parameter
-
- def __repr__(self):
- return 'Callback(%r, %r, %r)' % (
- self.name, self.retval, self.parameters)
class Union(Node):
@@ -592,8 +812,16 @@ class Union(Node):
self.fields = []
self.constructors = []
self.methods = []
+ self.static_methods = []
self.symbol = symbol
- self.doc = None
- def __repr__(self):
- return 'Union(%r, %r)' % (self.name, self.fields, )
+ def _walk(self, callback, chain):
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
+ for field in self.fields:
+ if field.anonymous_node:
+ field.anonymous_node.walk(callback, chain)
diff --git a/giscanner/codegen.py b/giscanner/codegen.py
new file mode 100644
index 00000000..0c0c559b
--- /dev/null
+++ b/giscanner/codegen.py
@@ -0,0 +1,137 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# 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.
+#
+
+from contextlib import contextmanager
+from . import ast
+
+class CCodeGenerator(object):
+ def __init__(self, namespace, out_h_filename, out_c_filename):
+ self.out_h_filename = out_h_filename
+ self.out_c_filename = out_c_filename
+ self._function_bodies = {}
+ self.namespace = namespace
+
+ def gen_symbol(self, name):
+ name = name.replace(' ', '_')
+ return '%s_%s' % (self.namespace.symbol_prefixes[0], name)
+
+ def _typecontainer_to_ctype(self, param):
+ if (isinstance(param, ast.Parameter) and
+ param.direction in (ast.PARAM_DIRECTION_OUT,
+ ast.PARAM_DIRECTION_INOUT)):
+ suffix = '*'
+ else:
+ suffix = ''
+ if (param.type.is_equiv((ast.TYPE_STRING, ast.TYPE_FILENAME)) and
+ param.transfer == ast.PARAM_TRANSFER_NONE):
+ return "const gchar*" + suffix
+ return param.type.ctype + suffix
+
+ def _write_prelude(self, out, func):
+ out.write("""
+%s
+%s (""" % (self._typecontainer_to_ctype(func.retval), func.symbol))
+ l = len(func.parameters)
+ if func.parameters:
+ for i, param in enumerate(func.parameters):
+ ctype = self._typecontainer_to_ctype(param)
+ out.write('%s %s' % (ctype, param.argname))
+ if i < l - 1:
+ out.write(", ")
+ else:
+ out.write('void')
+ out.write(")")
+
+ def _write_prototype(self, func):
+ self._write_prelude(self.out_h, func)
+ self.out_h.write(";\n\n")
+
+ def _write_annotation_transfer(self, transfer):
+ self.out_c.write("(transfer %s)" % (transfer, ))
+
+ def _write_docs(self, func):
+ self.out_c.write("/**\n * %s:\n" % (func.symbol, ))
+ for param in func.parameters:
+ self.out_c.write(" * @%s: " % (param.argname, ))
+ if param.direction in (ast.PARAM_DIRECTION_OUT,
+ ast.PARAM_DIRECTION_INOUT):
+ if param.caller_allocates:
+ allocate_string = ' caller-allocates'
+ else:
+ allocate_string = ''
+ self.out_c.write("(%s%s) " % (param.direction,
+ allocate_string))
+ self._write_annotation_transfer(param.transfer)
+ self.out_c.write(":\n")
+ self.out_c.write(' *\n')
+ self.out_c.write(' * Undocumented.\n')
+ self.out_c.write(' *\n')
+ self.out_c.write(' * Returns: ')
+ self._write_annotation_transfer(func.retval.transfer)
+ self.out_c.write('\n */')
+
+ @contextmanager
+ def _function(self, func):
+ self._write_prototype(func)
+ self._write_docs(func)
+ self._write_prelude(self.out_c, func)
+ self.out_c.write("\n{\n")
+ yield
+ self.out_c.write("}\n\n")
+
+ def _codegen_start(self):
+ warning = '/* GENERATED BY testcodegen.py; DO NOT EDIT */\n\n'
+ self.out_h.write(warning)
+ nsupper = self.namespace.name.upper()
+ self.out_h.write("""
+#ifndef __%s_H__
+#define __%s_H__
+
+#include <glib-object.h>
+""" % (nsupper, nsupper))
+
+ self.out_c.write(warning)
+ self.out_c.write("""#include "%s"\n\n""" % (self.out_h_filename, ))
+
+ def _codegen_end(self):
+ self.out_h.write("""#endif\n""")
+
+ self.out_h.close()
+ self.out_c.close()
+
+ def set_function_body(self, node, body):
+ assert isinstance(node, ast.Function)
+ self._function_bodies[node] = body
+
+ def codegen(self):
+ self.out_h = open(self.out_h_filename, 'w')
+ self.out_c = open(self.out_c_filename, 'w')
+
+ self._codegen_start()
+
+ for node in self.namespace.itervalues():
+ if isinstance(node, ast.Function):
+ with self._function(node):
+ body = self._function_bodies.get(node)
+ if not body:
+ body = ''
+ self.out_c.write(body)
+
+ self._codegen_end()
diff --git a/giscanner/dumper.py b/giscanner/dumper.py
index c30822b5..96f29371 100644
--- a/giscanner/dumper.py
+++ b/giscanner/dumper.py
@@ -23,7 +23,7 @@ import os
import subprocess
import tempfile
-from .glibtransformer import IntrospectionBinary
+from .gdumpparser import IntrospectionBinary
from .utils import get_libtool_command
# bugzilla.gnome.org/558436
diff --git a/giscanner/gdumpparser.py b/giscanner/gdumpparser.py
new file mode 100644
index 00000000..ce270720
--- /dev/null
+++ b/giscanner/gdumpparser.py
@@ -0,0 +1,455 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2008 Johan Dahlin
+#
+# 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.
+#
+
+import os
+import sys
+import tempfile
+import shutil
+import subprocess
+from xml.etree.cElementTree import parse
+
+from . import ast
+from . import glibast
+
+# GParamFlags
+G_PARAM_READABLE = 1 << 0
+G_PARAM_WRITABLE = 1 << 1
+G_PARAM_CONSTRUCT = 1 << 2
+G_PARAM_CONSTRUCT_ONLY = 1 << 3
+G_PARAM_LAX_VALIDATION = 1 << 4
+G_PARAM_STATIC_NAME = 1 << 5
+G_PARAM_STATIC_NICK = 1 << 6
+G_PARAM_STATIC_BLURB = 1 << 7
+
+
+class IntrospectionBinary(object):
+
+ def __init__(self, args, tmpdir=None):
+ self.args = args
+ if tmpdir is None:
+ self.tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
+ else:
+ self.tmpdir = tmpdir
+
+
+class Unresolved(object):
+
+ def __init__(self, target):
+ self.target = target
+
+
+class UnknownTypeError(Exception):
+ pass
+
+
+class GDumpParser(object):
+
+ def __init__(self, transformer):
+ self._transformer = transformer
+ self._namespace = transformer.namespace
+ self._binary = None
+ self._get_type_functions = []
+ self._gtype_data = {}
+ self._boxed_types = {}
+ self._private_internal_types = {}
+
+ # Public API
+
+ def init_parse(self):
+ """Do parsing steps that don't involve the introspection binary
+
+ This does enough work that get_type_functions() can be called.
+
+ """
+
+ self._transformer.parse()
+
+ # First pass: parsing
+ for node in self._namespace.itervalues():
+ if isinstance(node, ast.Function):
+ self._initparse_function(node)
+ if self._namespace.name == 'GObject':
+ for node in self._namespace.itervalues():
+ if isinstance(node, ast.Record):
+ self._initparse_gobject_record(node)
+
+ def get_get_type_functions(self):
+ return self._get_type_functions
+
+ def set_introspection_binary(self, binary):
+ self._binary = binary
+
+ def parse(self):
+ """Do remaining parsing steps requiring introspection binary"""
+
+ # Get all the GObject data by passing our list of get_type
+ # functions to the compiled binary, returning an XML blob.
+ tree = self._execute_binary_get_tree()
+ root = tree.getroot()
+ for child in root:
+ self._gtype_data[child.attrib['name']] = child
+ for child in root:
+ self._introspect_type(child)
+
+ # Pair up boxed types and class records
+ for name, boxed in self._boxed_types.iteritems():
+ self._pair_boxed_type(boxed)
+ for node in self._namespace.itervalues():
+ if isinstance(node, (ast.Class, ast.Interface)):
+ self._find_class_record(node)
+
+ # Clear the _get_type functions out of the namespace;
+ # Anyone who wants them can get them from the ast.Class/Interface/Boxed
+ to_remove = []
+ for name, node in self._namespace.iteritems():
+ if isinstance(node, (ast.Class, ast.Interface, glibast.GLibBoxed,
+ glibast.GLibEnum, glibast.GLibFlags)):
+ get_type_name = node.get_type
+ if get_type_name == 'intern':
+ continue
+ assert get_type_name, node
+ (ns, name) = self._transformer.split_csymbol(get_type_name)
+ assert ns is self._namespace
+ get_type_func = self._namespace.get(name)
+ assert get_type_func, name
+ to_remove.append(get_type_func)
+ for node in to_remove:
+ self._namespace.remove(node)
+
+ # Helper functions
+
+ def _execute_binary_get_tree(self):
+ """Load the library (or executable), returning an XML
+blob containing data gleaned from GObject's primitive introspection."""
+ in_path = os.path.join(self._binary.tmpdir, 'types.txt')
+ f = open(in_path, 'w')
+ # TODO: Introspect GQuark functions
+ for func in self._get_type_functions:
+ f.write(func)
+ f.write('\n')
+ f.close()
+ out_path = os.path.join(self._binary.tmpdir, 'dump.xml')
+
+ args = []
+ args.extend(self._binary.args)
+ args.append('--introspect-dump=%s,%s' % (in_path, out_path))
+
+ # Invoke the binary, having written our get_type functions to types.txt
+ try:
+ try:
+ subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
+ except subprocess.CalledProcessError, e:
+ # Clean up temporaries
+ raise SystemExit(e)
+ return parse(out_path)
+ finally:
+ shutil.rmtree(self._binary.tmpdir)
+
+ def _create_gobject(self, node):
+ type_name = 'G' + node.name
+ if type_name == 'GObject':
+ parent_gitype = None
+ symbol = 'intern'
+ elif type_name == 'GInitiallyUnowned':
+ parent_gitype = ast.Type(target_giname='GLib.Object')
+ symbol = 'g_initially_unowned_get_type'
+ else:
+ assert False
+ gnode = glibast.GLibObject(node.name, parent_gitype, type_name, symbol, 'object', True)
+ if type_name == 'GObject':
+ gnode.fields.extend(node.fields)
+ else:
+ # http://bugzilla.gnome.org/show_bug.cgi?id=569408
+ # GInitiallyUnowned is actually a typedef for GObject, but
+ # that's not reflected in the GIR, where it appears as a
+ # subclass (as it appears in the GType hierarchy). So
+ # what we do here is copy all of the GObject fields into
+ # GInitiallyUnowned so that struct offset computation
+ # works correctly.
+ gnode.fields = self._namespace.get('Object').fields
+ self._namespace.append(gnode, replace=True)
+
+ # Parser
+
+ def _initparse_function(self, func):
+ symbol = func.symbol
+ if symbol.startswith('_'):
+ return
+ elif symbol.endswith('_get_type'):
+ self._initparse_get_type_function(func)
+
+ def _initparse_get_type_function(self, func):
+ if self._namespace.name == 'GLib':
+ # No GObjects in GLib
+ return False
+ if (self._namespace.name == 'GObject' and
+ func.symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
+ # We handle these internally, see _create_gobject
+ return True
+ if func.parameters:
+ return False
+ # GType *_get_type(void)
+ rettype = func.retval.type
+ if not (rettype.is_equiv(ast.TYPE_GTYPE)
+ or rettype.target_giname == 'Gtk.Type'):
+ self._transformer.log_warning("function returns '%r', not a GType"
+ % (func.retval.type, ))
+ return False
+
+ self._get_type_functions.append(func.symbol)
+ return True
+
+ def _initparse_gobject_record(self, record):
+ # Special handling for when we're parsing GObject
+ internal_names = ("Object", 'InitiallyUnowned')
+ if record.name in internal_names:
+ self._create_gobject(record)
+ return
+ if record.name == 'InitiallyUnownedClass':
+ record.fields = self._namespace.get('ObjectClass').fields
+ self._namespace.append(record, replace=True)
+
+ # Introspection over the data we get from the dynamic
+ # GObject/GType system out of the binary
+
+ def _introspect_type(self, xmlnode):
+ if xmlnode.tag in ('enum', 'flags'):
+ self._introspect_enum(xmlnode)
+ elif xmlnode.tag == 'class':
+ self._introspect_object(xmlnode)
+ elif xmlnode.tag == 'interface':
+ self._introspect_interface(xmlnode)
+ elif xmlnode.tag == 'boxed':
+ self._introspect_boxed(xmlnode)
+ elif xmlnode.tag == 'fundamental':
+ self._introspect_fundamental(xmlnode)
+ else:
+ raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
+
+ def _introspect_enum(self, node):
+ members = []
+ for member in node.findall('member'):
+ # Keep the name closer to what we'd take from C by default;
+ # see http://bugzilla.gnome.org/show_bug.cgi?id=575613
+ name = member.attrib['nick'].replace('-', '_')
+ members.append(glibast.GLibEnumMember(name,
+ member.attrib['value'],
+ member.attrib['name'],
+ member.attrib['nick']))
+
+ klass = (glibast.GLibFlags if node.tag == 'flags' else glibast.GLibEnum)
+ type_name = node.attrib['name']
+ enum_name = self._transformer.strip_identifier_or_warn(type_name, fatal=True)
+ node = klass(enum_name, type_name, members, node.attrib['get-type'])
+ self._namespace.append(node, replace=True)
+
+ def _split_type_and_symbol_prefix(self, xmlnode):
+ """Infer the C symbol prefix from the _get_type function."""
+ get_type = xmlnode.attrib['get-type']
+ (ns, name) = self._transformer.split_csymbol(get_type)
+ assert ns is self._namespace
+ assert name.endswith('_get_type')
+ return (get_type, name[:-len('_get_type')])
+
+ def _introspect_object(self, xmlnode):
+ type_name = xmlnode.attrib['name']
+ # We handle this specially above; in 2.16 and below there
+ # was no g_object_get_type, for later versions we need
+ # to skip it
+ if type_name == 'GObject':
+ return
+ is_abstract = bool(xmlnode.attrib.get('abstract', False))
+ (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+ node = glibast.GLibObject(
+ self._transformer.strip_identifier_or_warn(type_name, fatal=True),
+ None,
+ type_name,
+ get_type, c_symbol_prefix, is_abstract)
+ self._parse_parents(xmlnode, node)
+ self._introspect_properties(node, xmlnode)
+ self._introspect_signals(node, xmlnode)
+ self._introspect_implemented_interfaces(node, xmlnode)
+
+ self._add_record_fields(node)
+ self._namespace.append(node, replace=True)
+
+ def _introspect_interface(self, xmlnode):
+ type_name = xmlnode.attrib['name']
+ (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+ node = glibast.GLibInterface(
+ self._transformer.strip_identifier_or_warn(type_name, fatal=True),
+ None,
+ type_name, get_type, c_symbol_prefix)
+ self._introspect_properties(node, xmlnode)
+ self._introspect_signals(node, xmlnode)
+ for child in xmlnode.findall('prerequisite'):
+ name = child.attrib['name']
+ prereq = self._transformer.create_type_from_user_string(name)
+ node.prerequisites.append(prereq)
+ # GtkFileChooserEmbed is an example of a private interface, we
+ # just filter them out
+ if xmlnode.attrib['get-type'].startswith('_'):
+ self._private_internal_types[type_name] = node
+ else:
+ self._namespace.append(node, replace=True)
+
+ def _introspect_boxed(self, xmlnode):
+ type_name = xmlnode.attrib['name']
+ # This one doesn't go in the main namespace; we associate it with
+ # the struct or union
+ (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+ node = glibast.GLibBoxed(type_name, get_type, c_symbol_prefix)
+ self._boxed_types[node.type_name] = node
+
+ def _introspect_implemented_interfaces(self, node, xmlnode):
+ gt_interfaces = []
+ for interface in xmlnode.findall('implements'):
+ gitype = self._transformer.create_type_from_user_string(interface.attrib['name'])
+ gt_interfaces.append(gitype)
+ node.interfaces = gt_interfaces
+
+ def _introspect_properties(self, node, xmlnode):
+ for pspec in xmlnode.findall('property'):
+ ctype = pspec.attrib['type']
+ flags = int(pspec.attrib['flags'])
+ readable = (flags & G_PARAM_READABLE) != 0
+ writable = (flags & G_PARAM_WRITABLE) != 0
+ construct = (flags & G_PARAM_CONSTRUCT) != 0
+ construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
+ node.properties.append(ast.Property(
+ pspec.attrib['name'],
+ self._transformer.create_type_from_user_string(ctype),
+ readable, writable, construct, construct_only,
+ ctype,
+ ))
+ node.properties = node.properties
+
+ def _introspect_signals(self, node, xmlnode):
+ for signal_info in xmlnode.findall('signal'):
+ rctype = signal_info.attrib['return']
+ rtype = self._transformer.create_type_from_user_string(rctype)
+ return_ = ast.Return(rtype)
+ parameters = []
+ for i, parameter in enumerate(signal_info.findall('param')):
+ if i == 0:
+ argname = 'object'
+ else:
+ argname = 'p%s' % (i-1, )
+ pctype = parameter.attrib['type']
+ ptype = self._transformer.create_type_from_user_string(pctype)
+ param = ast.Parameter(argname, ptype)
+ param.transfer = ast.PARAM_TRANSFER_NONE
+ parameters.append(param)
+ signal = glibast.GLibSignal(signal_info.attrib['name'], return_, parameters)
+ node.signals.append(signal)
+ node.signals = node.signals
+
+ def _parse_parents(self, xmlnode, node):
+ if 'parents' in xmlnode.attrib:
+ parent_types = map(lambda s: self._transformer.create_type_from_user_string(s),
+ xmlnode.attrib['parents'].split(','))
+ else:
+ parent_types = []
+ node.parent_chain = parent_types
+
+ def _introspect_fundamental(self, xmlnode):
+ # We only care about types that can be instantiatable, other
+ # fundamental types such as the Clutter.Fixed/CoglFixed registers
+ # are not yet interesting from an introspection perspective and
+ # are ignored
+ if not xmlnode.attrib.get('instantiatable', False):
+ return
+
+ type_name = xmlnode.attrib['name']
+ is_abstract = bool(xmlnode.attrib.get('abstract', False))
+ (get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
+ node = glibast.GLibObject(
+ self._transformer.strip_identifier_or_warn(type_name, fatal=True),
+ None,
+ type_name,
+ get_type, c_symbol_prefix, is_abstract)
+ self._parse_parents(xmlnode, node)
+ node.fundamental = True
+ self._introspect_implemented_interfaces(node, xmlnode)
+
+ self._add_record_fields(node)
+ self._namespace.append(node, replace=True)
+
+ def _add_record_fields(self, node):
+ # add record fields
+ record = self._namespace.get(node.name)
+ if not isinstance(record, ast.Record):
+ return
+ node.fields = record.fields
+ for field in node.fields:
+ if isinstance(field, ast.Field):
+ # Object instance fields are assumed to be read-only
+ # (see also _find_class_record and transformer.py)
+ field.writable = False
+
+ def _pair_boxed_type(self, boxed):
+ name = self._transformer.strip_identifier_or_warn(boxed.type_name, fatal=True)
+ pair_node = self._namespace.get(name)
+ if not pair_node:
+ boxed_item = glibast.GLibBoxedOther(name, boxed.type_name,
+ boxed.get_type,
+ boxed.c_symbol_prefix)
+ elif isinstance(pair_node, ast.Record):
+ boxed_item = glibast.GLibBoxedStruct(pair_node.name, boxed.type_name,
+ boxed.get_type,
+ boxed.c_symbol_prefix)
+ boxed_item.inherit_file_positions(pair_node)
+ boxed_item.fields = pair_node.fields
+ elif isinstance(pair_node, ast.Union):
+ boxed_item = glibast.GLibBoxedUnion(pair_node.name, boxed.type_name,
+ boxed.get_type,
+ boxed.c_symbol_prefix)
+ boxed_item.inherit_file_positions(pair_node)
+ boxed_item.fields = pair_node.fields
+ else:
+ return False
+ self._namespace.append(boxed_item, replace=True)
+
+ def _strip_class_suffix(self, name):
+ if (name.endswith('Class') or
+ name.endswith('Iface')):
+ return name[:-5]
+ elif name.endswith('Interface'):
+ return name[:-9]
+ else:
+ return None
+
+ def _find_class_record(self, cls):
+ pair_record = None
+ if isinstance(cls, ast.Class):
+ pair_record = self._namespace.get(cls.name + 'Class')
+ else:
+ for suffix in ('Iface', 'Interface'):
+ pair_record = self._namespace.get(cls.name + suffix)
+ if pair_record:
+ break
+ if not (pair_record and isinstance(pair_record, ast.Record)):
+ return
+
+ gclass_struct = glibast.GLibRecord.from_record(pair_record)
+ self._namespace.append(gclass_struct, replace=True)
+ cls.glib_type_struct = gclass_struct.create_type()
+ cls.inherit_file_positions(pair_record)
+ gclass_struct.is_gtype_struct_for = cls.create_type()
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index 9fee1fc1..2fc0a355 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -22,13 +22,8 @@ import os
from xml.etree.cElementTree import parse
-from .ast import (Alias, Array, Callback, Constant, Enum, Function, Field,
- Namespace, Parameter, Property, Return, Union, Struct, Type,
- Varargs, Include)
-from .glibast import (GLibEnum, GLibEnumMember, GLibFlags,
- GLibInterface, GLibObject, GLibBoxedStruct,
- GLibBoxedUnion, GLibBoxedOther)
-
+from . import ast
+from . import glibast
from .girwriter import COMPATIBLE_GIR_VERSION
CORE_NS = "http://www.gtk.org/introspection/core/1.0"
@@ -51,7 +46,6 @@ def _cns(tag):
class GIRParser(object):
def __init__(self):
- self._include_parsing = False
self._shared_libraries = []
self._includes = set()
self._pkgconfig_packages = set()
@@ -72,6 +66,8 @@ class GIRParser(object):
self._namespace = None
self._shared_libraries = []
self._pkgconfig_packages = set()
+ self._c_includes = set()
+ self._c_prefix = None
self._parse_api(tree.getroot())
def get_namespace(self):
@@ -83,6 +79,12 @@ class GIRParser(object):
def get_includes(self):
return self._includes
+ def get_c_includes(self):
+ return self._c_includes
+
+ def get_c_prefix(self):
+ return self._c_prefix
+
def get_pkgconfig_packages(self):
if not hasattr(self, '_pkgconfig_packages'):
self._pkgconfig_packages = []
@@ -91,11 +93,17 @@ class GIRParser(object):
def get_doc(self):
return parse(self._filename)
- def set_include_parsing(self, include_parsing):
- self._include_parsing = include_parsing
-
# Private
+ def _find_first_child(self, node, name):
+ for child in node.getchildren():
+ if child.tag == name:
+ return child
+ return None
+
+ def _find_children(self, node, name):
+ return [child for child in node.getchildren() if child.tag == name]
+
def _get_current_file(self):
if not self._filename_stack:
return None
@@ -105,9 +113,6 @@ class GIRParser(object):
return curfile[len(cwd):]
return curfile
- def _add_node(self, node):
- self._namespace.nodes.append(node)
-
def _parse_api(self, root):
assert root.tag == _corens('repository')
version = root.attrib['version']
@@ -121,11 +126,21 @@ class GIRParser(object):
self._parse_include(node)
elif node.tag == _corens('package'):
self._parse_pkgconfig_package(node)
+ elif node.tag == _cns('include'):
+ self._parse_c_include(node)
ns = root.find(_corens('namespace'))
assert ns is not None
- self._namespace = Namespace(ns.attrib['name'],
- ns.attrib['version'])
+ identifier_prefixes = ns.attrib.get(_cns('identifier-prefixes'))
+ if identifier_prefixes:
+ identifier_prefixes = identifier_prefixes.split(',')
+ symbol_prefixes = ns.attrib.get(_cns('symbol-prefixes'))
+ if symbol_prefixes:
+ symbol_prefixes = symbol_prefixes.split(',')
+ self._namespace = ast.Namespace(ns.attrib['name'],
+ ns.attrib['version'],
+ identifier_prefixes=identifier_prefixes,
+ symbol_prefixes=symbol_prefixes)
if 'shared-library' in ns.attrib:
self._shared_libraries.extend(
ns.attrib['shared-library'].split(','))
@@ -141,7 +156,7 @@ class GIRParser(object):
_corens('interface'): self._parse_object_interface,
_corens('record'): self._parse_record,
_corens('union'): self._parse_union,
- _corens('boxed'): self._parse_boxed,
+ _glibns('boxed'): self._parse_boxed,
}
for node in ns.getchildren():
@@ -150,31 +165,54 @@ class GIRParser(object):
method(node)
def _parse_include(self, node):
- include = Include(node.attrib['name'],
+ include = ast.Include(node.attrib['name'],
node.attrib['version'])
self._includes.add(include)
def _parse_pkgconfig_package(self, node):
- if not hasattr(self, '_pkgconfig_packages'):
- self._pkgconfig_packages = []
self._pkgconfig_packages.add(node.attrib['name'])
+ def _parse_c_include(self, node):
+ self._c_includes.add(node.attrib['name'])
+
def _parse_alias(self, node):
typeval = self._parse_type(node)
- alias = Alias(node.attrib['name'],
+ alias = ast.Alias(node.attrib['name'],
typeval,
node.attrib.get(_cns('type')))
- self._add_node(alias)
+ self._namespace.append(alias)
+
+ def _parse_generic_attribs(self, node, obj):
+ assert isinstance(obj, ast.Annotated)
+ doc = node.find(_corens('doc'))
+ if doc is not None:
+ obj.doc = doc.text
+ version = node.attrib.get('version')
+ if version:
+ obj.version = version
+ deprecated = node.attrib.get('deprecated')
+ if deprecated:
+ obj.deprecated = deprecated
+ introspectable = node.attrib.get('introspectable')
+ if introspectable:
+ obj.introspectable = int(introspectable) > 0
def _parse_object_interface(self, node):
+ parent = node.attrib.get('parent')
+ if parent:
+ parent_type = self._namespace.type_from_name(parent)
+ else:
+ parent_type = None
+
ctor_args = [node.attrib['name'],
- node.attrib.get('parent'),
+ parent_type,
node.attrib[_glibns('type-name')],
- node.attrib[_glibns('get-type')]]
+ node.attrib[_glibns('get-type')],
+ node.attrib.get(_cns('symbol-prefix'))]
if node.tag == _corens('interface'):
- klass = GLibInterface
+ klass = glibast.GLibInterface
elif node.tag == _corens('class'):
- klass = GLibObject
+ klass = glibast.GLibObject
is_abstract = node.attrib.get('abstract')
is_abstract = is_abstract and is_abstract != '0'
ctor_args.append(is_abstract)
@@ -182,38 +220,49 @@ class GIRParser(object):
raise AssertionError(node)
obj = klass(*ctor_args)
- self._add_node(obj)
+ self._parse_generic_attribs(node, obj)
+ type_struct = node.attrib.get(_glibns('type-struct'))
+ if type_struct:
+ obj.glib_type_struct = self._namespace.type_from_name(type_struct)
+ self._namespace.append(obj)
- if self._include_parsing:
- return
ctor_args.append(node.attrib.get(_cns('type')))
- for iface in node.findall(_corens('implements')):
- obj.interfaces.append(iface.attrib['name'])
- for iface in node.findall(_corens('prerequisites')):
- obj.prerequisities.append(iface.attrib['name'])
- for method in node.findall(_corens('method')):
- func = self._parse_function_common(method, Function)
+ for iface in self._find_children(node, _corens('implements')):
+ obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
+ for iface in self._find_children(node, _corens('prerequisite')):
+ obj.prerequisites.append(self._namespace.type_from_name(iface.attrib['name']))
+ for func_node in self._find_children(node, _corens('function')):
+ func = self._parse_function_common(func_node, ast.Function)
+ obj.static_methods.append(func)
+ for method in self._find_children(node, _corens('method')):
+ func = self._parse_function_common(method, ast.Function)
func.is_method = True
obj.methods.append(func)
- for ctor in node.findall(_corens('constructor')):
- obj.constructors.append(
- self._parse_function_common(ctor, Function))
- for callback in node.findall(_corens('callback')):
- obj.fields.append(self._parse_function_common(callback, Callback))
- for field in node.findall(_corens('field')):
- obj.fields.append(self._parse_field(field))
- for prop in node.findall(_corens('property')):
+ for method in self._find_children(node, _corens('virtual-method')):
+ func = self._parse_function_common(method, ast.VFunction)
+ self._parse_generic_attribs(method, func)
+ func.is_method = True
+ func.invoker = method.get('invoker')
+ obj.virtual_methods.append(func)
+ for ctor in self._find_children(node, _corens('constructor')):
+ func = self._parse_function_common(ctor, ast.Function)
+ func.is_constructor = True
+ obj.constructors.append(func)
+ obj.fields.extend(self._parse_fields(node))
+ for prop in self._find_children(node, _corens('property')):
obj.properties.append(self._parse_property(prop))
- for signal in node.findall(_glibns('signal')):
- obj.signals.append(self._parse_function_common(signal, Function))
+ for signal in self._find_children(node, _glibns('signal')):
+ obj.signals.append(self._parse_function_common(signal, ast.Function))
def _parse_callback(self, node):
- callback = self._parse_function_common(node, Callback)
- self._add_node(callback)
+ callback = self._parse_function_common(node, ast.Callback)
+ self._namespace.append(callback)
def _parse_function(self, node):
- function = self._parse_function_common(node, Function)
- self._add_node(function)
+ function = self._parse_function_common(node, ast.Function)
+ function.shadows = node.attrib.get('shadows', None)
+ function.shadowed_by = node.attrib.get('shadowed-by', None)
+ self._namespace.append(function)
def _parse_function_common(self, node, klass):
name = node.attrib['name']
@@ -221,187 +270,297 @@ class GIRParser(object):
if not returnnode:
raise ValueError('node %r has no return-value' % (name, ))
transfer = returnnode.attrib.get('transfer-ownership')
- retval = Return(self._parse_type(returnnode), transfer)
+ retval = ast.Return(self._parse_type(returnnode), transfer)
+ self._parse_generic_attribs(returnnode, retval)
parameters = []
- if klass is Callback:
- func = klass(name, retval, parameters,
+ throws = (node.attrib.get('throws') == '1')
+
+ if klass is ast.Callback:
+ func = klass(name, retval, parameters, throws,
node.attrib.get(_cns('type')))
- else:
+ elif klass is ast.Function:
identifier = node.attrib.get(_cns('identifier'))
- throws = (node.attrib.get('throws') == '1')
- func = klass(name, retval, parameters, identifier, throws)
-
- if self._include_parsing:
- return func
+ func = klass(name, retval, parameters, throws, identifier)
+ elif klass is ast.VFunction:
+ func = klass(name, retval, parameters, throws)
+ else:
+ assert False
parameters_node = node.find(_corens('parameters'))
if (parameters_node is not None):
- for paramnode in parameters_node.findall(_corens('parameter')):
- param = Parameter(paramnode.attrib.get('name'),
- self._parse_type(paramnode),
- paramnode.attrib.get('direction'),
+ for paramnode in self._find_children(parameters_node, _corens('parameter')):
+ typeval = self._parse_type(paramnode)
+ param = ast.Parameter(paramnode.attrib.get('name'),
+ typeval,
+ paramnode.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
paramnode.attrib.get('transfer-ownership'),
- paramnode.attrib.get('allow-none') == '1')
+ paramnode.attrib.get('allow-none') == '1',
+ paramnode.attrib.get('scope'),
+ paramnode.attrib.get('caller-allocates') == '1')
+ self._parse_generic_attribs(paramnode, param)
parameters.append(param)
+ for i, paramnode in enumerate(self._find_children(parameters_node,
+ _corens('parameter'))):
+ param = parameters[i]
+ self._parse_type_second_pass(func, paramnode, param.type)
+ closure = paramnode.attrib.get('closure')
+ if closure:
+ idx = int(closure)
+ assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
+ param.closure_name = parameters[idx].argname
+ destroy = paramnode.attrib.get('destroy')
+ if destroy:
+ idx = int(destroy)
+ assert idx < len(parameters), "%d >= %d" % (idx, len(parameters))
+ param.destroy_name = parameters[idx].argname
+
+ self._parse_type_second_pass(func, returnnode, retval.type)
+
+ self._parse_generic_attribs(node, func)
return func
- def _parse_record(self, node):
+ def _parse_fields(self, node):
+ res = []
+ names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
+ for child in node.getchildren():
+ if child.tag in names:
+ fieldobj = self._parse_field(child)
+ res.append(fieldobj)
+ return res
+
+ def _parse_record(self, node, anonymous=False):
if _glibns('type-name') in node.attrib:
- struct = GLibBoxedStruct(node.attrib['name'],
+ struct = glibast.GLibBoxedStruct(node.attrib['name'],
node.attrib[_glibns('type-name')],
node.attrib[_glibns('get-type')],
+ node.attrib.get(_cns('symbol-prefix')),
node.attrib.get(_cns('type')))
+ elif _glibns('is-gtype-struct-for') in node.attrib:
+ struct = glibast.GLibRecord(node.attrib['name'],
+ node.attrib.get(_cns('type')),
+ disguised=node.attrib.get('disguised') == '1')
+ is_gtype_struct_for = node.attrib[_glibns('is-gtype-struct-for')]
+ struct.is_gtype_struct_for = self._namespace.type_from_name(is_gtype_struct_for)
else:
- disguised = node.attrib.get('disguised') == '1'
- struct = Struct(node.attrib['name'],
+ struct = ast.Record(node.attrib.get('name'),
node.attrib.get(_cns('type')),
- disguised=disguised)
- self._add_node(struct)
-
- if self._include_parsing:
- return
- for field in node.findall(_corens('field')):
- struct.fields.append(self._parse_field(field))
- for callback in node.findall(_corens('callback')):
- struct.fields.append(
- self._parse_function_common(callback, Callback))
- for method in node.findall(_corens('method')):
- struct.fields.append(
- self._parse_function_common(method, Function))
- for ctor in node.findall(_corens('constructor')):
+ disguised=node.attrib.get('disguised') == '1')
+ if node.attrib.get('foreign') == '1':
+ struct.foreign = True
+ self._parse_generic_attribs(node, struct)
+ if not anonymous:
+ self._namespace.append(struct)
+
+ struct.fields.extend(self._parse_fields(node))
+ for method in self._find_children(node, _corens('method')):
+ struct.methods.append(
+ self._parse_function_common(method, ast.Function))
+ for func in self._find_children(node, _corens('function')):
+ struct.static_methods.append(
+ self._parse_function_common(func, ast.Function))
+ for ctor in self._find_children(node, _corens('constructor')):
struct.constructors.append(
- self._parse_function_common(ctor, Function))
+ self._parse_function_common(ctor, ast.Function))
+ return struct
- def _parse_union(self, node):
+ def _parse_union(self, node, anonymous=False):
if _glibns('type-name') in node.attrib:
- union = GLibBoxedUnion(node.attrib['name'],
+ union = glibast.GLibBoxedUnion(node.attrib['name'],
node.attrib[_glibns('type-name')],
node.attrib[_glibns('get-type')],
+ node.attrib.get(_cns('symbol-prefix')),
node.attrib.get(_cns('type')))
else:
- union = Union(node.attrib['name'],
+ union = ast.Union(node.attrib.get('name'),
node.attrib.get(_cns('type')))
- self._add_node(union)
+ if not anonymous:
+ self._namespace.append(union)
- if self._include_parsing:
- return
- for callback in node.findall(_corens('callback')):
+ for callback in self._find_children(node, _corens('callback')):
union.fields.append(
- self._parse_function_common(callback, Callback))
- for field in node.findall(_corens('field')):
- union.fields.append(self._parse_field(field))
- for method in node.findall(_corens('method')):
- union.fields.append(
- self._parse_function_common(method, Function))
- for ctor in node.findall(_corens('constructor')):
+ self._parse_function_common(callback, ast.Callback))
+ union.fields.extend(self._parse_fields(node))
+ for method in self._find_children(node, _corens('method')):
+ union.methods.append(
+ self._parse_function_common(method, ast.Function))
+ for func in self._find_children(node, _corens('function')):
+ union.static_methods.append(
+ self._parse_function_common(func, ast.Function))
+ for ctor in self._find_children(node, _corens('constructor')):
union.constructors.append(
- self._parse_function_common(ctor, Function))
-
- def _parse_type(self, node):
- typenode = node.find(_corens('type'))
- if typenode is not None:
- return Type(typenode.attrib['name'],
- typenode.attrib.get(_cns('type')))
-
- typenode = node.find(_corens('array'))
- if typenode is not None:
-
- array_type = typenode.attrib.get(_cns('type'))
- if array_type.startswith('GArray*') or \
- array_type.startswith('GPtrArray*') or \
- array_type.startswith('GByteArray*'):
- element_type = None
- else:
- element_type = self._parse_type(typenode)
-
- ret = Array(None, array_type, element_type)
+ self._parse_function_common(ctor, ast.Function))
+ return union
+
+ def _parse_type_simple(self, typenode):
+ # ast.Fields can contain inline callbacks
+ if typenode.tag == _corens('callback'):
+ typeval = self._namespace.type_from_name(typenode.attrib['name'])
+ typeval.ctype = typenode.attrib.get(_cns('type'))
+ return typeval
+ # ast.Arrays have their own toplevel XML
+ elif typenode.tag == _corens('array'):
+ array_type = typenode.attrib.get('name')
+ element_type = self._parse_type(typenode)
+ array_ctype = typenode.attrib.get(_cns('type'))
+ ret = ast.Array(array_type, element_type, ctype=array_ctype)
+ # zero-terminated defaults to true...
+ zero = typenode.attrib.get('zero-terminated')
+ if zero and zero == '0':
+ ret.zeroterminated = False
+ fixed_size = typenode.attrib.get('fixed-size')
+ if fixed_size:
+ ret.size = int(fixed_size)
- lenidx = typenode.attrib.get('length')
- if lenidx:
- ret.length_param_index = int(lenidx)
return ret
+ elif typenode.tag == _corens('varargs'):
+ return ast.Varargs()
+ elif typenode.tag == _corens('type'):
+ name = typenode.attrib.get('name')
+ ctype = typenode.attrib.get(_cns('type'))
+ if name is None:
+ if ctype is None:
+ return ast.TypeUnknown()
+ return ast.Type(ctype=ctype)
+ elif name in ['GLib.List', 'GLib.SList']:
+ subchild = self._find_first_child(typenode, _corens('type'))
+ if subchild is not None:
+ element_type = self._parse_type(typenode)
+ else:
+ element_type = ast.TYPE_ANY
+ return ast.List(name, element_type, ctype=ctype)
+ elif name == 'GLib.HashTable':
+ subchildren = self._find_children(typenode, _corens('type'))
+ subchildren_types = map(self._parse_type_simple, subchildren)
+ while len(subchildren_types) < 2:
+ subchildren_types.append(ast.TYPE_ANY)
+ return ast.Map(subchildren_types[0],
+ subchildren_types[1],
+ ctype=ctype)
+ else:
+ return self._namespace.type_from_name(name, ctype)
+ else:
+ assert False, "Failed to parse inner type"
- typenode = node.find(_corens('varargs'))
- if typenode is not None:
- return Varargs()
-
- raise ValueError("Couldn't parse type of node %r; children=%r",
- node, list(node))
+ def _parse_type(self, node):
+ for name in map(_corens, ('callback', 'array', 'varargs', 'type')):
+ typenode = node.find(name)
+ if typenode is not None:
+ return self._parse_type_simple(typenode)
+ assert False, "Failed to parse toplevel type"
+
+ def _parse_type_second_pass(self, parent, node, typeval):
+ """A hack necessary to handle the integer parameter indexes on
+ array types."""
+ typenode = node.find(_corens('array'))
+ if typenode is None:
+ return
+ lenidx = typenode.attrib.get('length')
+ if lenidx is not None:
+ idx = int(lenidx)
+ assert idx < len(parent.parameters), "%r %d >= %d" \
+ % (parent, idx, len(parent.parameters))
+ typeval.length_param_name = parent.parameters[idx].argname
def _parse_boxed(self, node):
- obj = GLibBoxedOther(node.attrib[_glibns('name')],
+ obj = glibast.GLibBoxedOther(node.attrib[_glibns('name')],
node.attrib[_glibns('type-name')],
- node.attrib[_glibns('get-type')])
- self._add_node(obj)
- if self._include_parsing:
- return
- for method in node.findall(_corens('method')):
- func = self._parse_function_common(method, Function)
+ node.attrib[_glibns('get-type')],
+ node.attrib.get(_cns('symbol-prefix')))
+ self._parse_generic_attribs(node, obj)
+ self._namespace.append(obj)
+ for method in self._find_children(node, _corens('method')):
+ func = self._parse_function_common(method, ast.Function)
func.is_method = True
obj.methods.append(func)
- for ctor in node.findall(_corens('constructor')):
+ for ctor in self._find_children(node, _corens('constructor')):
obj.constructors.append(
- self._parse_function_common(ctor, Function))
- for callback in node.findall(_corens('callback')):
+ self._parse_function_common(ctor, ast.Function))
+ for callback in self._find_children(node, _corens('callback')):
obj.fields.append(
- self._parse_function_common(callback, Callback))
+ self._parse_function_common(callback, ast.Callback))
def _parse_field(self, node):
- type_node = self._parse_type(node)
- return Field(node.attrib['name'],
- type_node,
- type_node.ctype,
- node.attrib.get('readable') != '0',
- node.attrib.get('writable') == '1',
- node.attrib.get('bits'))
+ type_node = None
+ anonymous_node = None
+ if node.tag in map(_corens, ('record', 'union')):
+ anonymous_elt = node
+ else:
+ anonymous_elt = self._find_first_child(node, _corens('callback'))
+ if anonymous_elt is not None:
+ if anonymous_elt.tag == _corens('callback'):
+ anonymous_node = self._parse_function_common(anonymous_elt, ast.Callback)
+ elif anonymous_elt.tag == _corens('record'):
+ anonymous_node = self._parse_record(anonymous_elt, anonymous=True)
+ elif anonymous_elt.tag == _corens('union'):
+ anonymous_node = self._parse_union(anonymous_elt, anonymous=True)
+ else:
+ assert False, anonymous_elt.tag
+ else:
+ assert node.tag == _corens('field'), node.tag
+ type_node = self._parse_type(node)
+ field = ast.Field(node.attrib.get('name'),
+ type_node,
+ node.attrib.get('readable') != '0',
+ node.attrib.get('writable') == '1',
+ node.attrib.get('bits'),
+ anonymous_node=anonymous_node)
+ self._parse_generic_attribs(node, field)
+ return field
def _parse_property(self, node):
- type_node = self._parse_type(node)
- return Property(node.attrib['name'],
- type_node.name,
+ prop = ast.Property(node.attrib['name'],
+ self._parse_type(node),
node.attrib.get('readable') != '0',
node.attrib.get('writable') == '1',
node.attrib.get('construct') == '1',
- node.attrib.get('construct-only') == '1',
- type_node.ctype)
+ node.attrib.get('construct-only') == '1')
+ self._parse_generic_attribs(node, prop)
+ return prop
def _parse_member(self, node):
- return GLibEnumMember(node.attrib['name'],
- node.attrib['value'],
- node.attrib.get(_cns('identifier')),
- node.attrib.get(_glibns('nick')))
+ member = glibast.GLibEnumMember(node.attrib['name'],
+ node.attrib['value'],
+ node.attrib.get(_cns('identifier')),
+ node.attrib.get(_glibns('nick')))
+ self._parse_generic_attribs(node, member)
+ return member
def _parse_constant(self, node):
type_node = self._parse_type(node)
- constant = Constant(node.attrib['name'],
- type_node.name,
+ constant = ast.Constant(node.attrib['name'],
+ type_node,
node.attrib['value'])
- self._add_node(constant)
+ self._parse_generic_attribs(node, constant)
+ self._namespace.append(constant)
def _parse_enumeration_bitfield(self, node):
name = node.attrib.get('name')
ctype = node.attrib.get(_cns('type'))
get_type = node.attrib.get(_glibns('get-type'))
type_name = node.attrib.get(_glibns('type-name'))
- if get_type:
+ glib_error_quark = node.attrib.get(_glibns('error-quark'))
+ if get_type or glib_error_quark:
if node.tag == _corens('bitfield'):
- klass = GLibFlags
+ klass = glibast.GLibFlags
else:
- klass = GLibEnum
+ klass = glibast.GLibEnum
else:
- klass = Enum
+ if node.tag == _corens('bitfield'):
+ klass = ast.Bitfield
+ else:
+ klass = ast.Enum
type_name = ctype
members = []
- if klass is Enum:
+ if klass in (ast.Enum, ast.Bitfield):
obj = klass(name, type_name, members)
else:
obj = klass(name, type_name, members, get_type)
+ obj.error_quark = glib_error_quark
obj.ctype = ctype
- self._add_node(obj)
+ self._parse_generic_attribs(node, obj)
+ self._namespace.append(obj)
- if self._include_parsing:
- return
- for member in node.findall(_corens('member')):
+ for member in self._find_children(node, _corens('member')):
members.append(self._parse_member(member))
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index c8103494..f2074df3 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -22,8 +22,8 @@
from __future__ import with_statement
from .ast import (Alias, Array, Bitfield, Callback, Class, Constant, Enum,
- Function, Interface, List, Map, Member, Struct, Union,
- Varargs, Type, TYPE_ANY)
+ Function, Interface, List, Map, Member, Record, Union,
+ Varargs, Type)
from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember,
GLibFlags, GLibObject, GLibInterface,
GLibRecord)
@@ -31,21 +31,21 @@ from .xmlwriter import XMLWriter
# Bump this for *incompatible* changes to the .gir.
# Compatible changes we just make inline
-COMPATIBLE_GIR_VERSION = '1.1'
+COMPATIBLE_GIR_VERSION = '1.2'
class GIRWriter(XMLWriter):
- def __init__(self, namespace, shlibs, includes, pkgs, c_includes, cprefix):
+ def __init__(self, namespace, shlibs, includes, pkgs, c_includes):
super(GIRWriter, self).__init__()
self.write_comment(
'''This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. ''')
self._write_repository(namespace, shlibs, includes, pkgs,
- c_includes, cprefix)
+ c_includes)
def _write_repository(self, namespace, shlibs, includes=None,
- packages=None, c_includes=None, cprefix=None):
+ packages=None, c_includes=None):
if includes is None:
includes = frozenset()
if packages is None:
@@ -65,7 +65,9 @@ and/or use gtk-doc annotations. ''')
self._write_pkgconfig_pkg(pkg)
for c_include in sorted(set(c_includes)):
self._write_c_include(c_include)
- self._write_namespace(namespace, shlibs, cprefix)
+ self._namespace = namespace
+ self._write_namespace(namespace, shlibs)
+ self._namespace = None
def _write_include(self, include):
attrs = [('name', include.name), ('version', include.version)]
@@ -79,11 +81,12 @@ and/or use gtk-doc annotations. ''')
attrs = [('name', c_include)]
self.write_tag('c:include', attrs)
- def _write_namespace(self, namespace, shlibs, cprefix):
+ def _write_namespace(self, namespace, shlibs):
attrs = [('name', namespace.name),
('version', namespace.version),
('shared-library', ','.join(shlibs)),
- ('c:prefix', cprefix)]
+ ('c:identifier-prefixes', ','.join(namespace.identifier_prefixes)),
+ ('c:symbol-prefixes', ','.join(namespace.symbol_prefixes))]
with self.tagcontext('namespace', attrs):
# We define a custom sorting function here because
# we want aliases to be first. They're a bit
@@ -98,7 +101,7 @@ and/or use gtk-doc annotations. ''')
return 1
else:
return cmp(a, b)
- for node in sorted(namespace.nodes, cmp=nscmp):
+ for node in sorted(namespace.itervalues(), cmp=nscmp):
self._write_node(node)
def _write_node(self, node):
@@ -112,7 +115,7 @@ and/or use gtk-doc annotations. ''')
self._write_class(node)
elif isinstance(node, Callback):
self._write_callback(node)
- elif isinstance(node, Struct):
+ elif isinstance(node, Record):
self._write_record(node)
elif isinstance(node, Union):
self._write_union(node)
@@ -167,11 +170,17 @@ and/or use gtk-doc annotations. ''')
self._append_throws(callable, attrs)
with self.tagcontext(tag_name, attrs):
self._write_generic(callable)
- self._write_return_type(callable.retval)
- self._write_parameters(callable.parameters)
+ self._write_return_type(callable.retval, parent=callable)
+ self._write_parameters(callable, callable.parameters)
def _write_function(self, func, tag_name='function'):
- attrs = [('c:identifier', func.symbol)]
+ attrs = []
+ if hasattr(func, 'symbol'):
+ attrs.append(('c:identifier', func.symbol))
+ if func.shadowed_by:
+ attrs.append(('shadowed-by', func.shadowed_by))
+ elif func.shadows:
+ attrs.append(('shadows', func.shadows))
self._write_callable(func, tag_name, attrs)
def _write_method(self, method):
@@ -183,105 +192,100 @@ and/or use gtk-doc annotations. ''')
def _write_constructor(self, method):
self._write_function(method, tag_name='constructor')
- def _write_return_type(self, return_):
+ def _write_return_type(self, return_, parent=None):
if not return_:
return
- assert return_.transfer is not None, return_
-
attrs = []
- attrs.append(('transfer-ownership', return_.transfer))
+ if return_.transfer:
+ attrs.append(('transfer-ownership', return_.transfer))
with self.tagcontext('return-value', attrs):
self._write_generic(return_)
- self._write_type(return_.type)
+ self._write_type(return_.type, function=parent)
- def _write_parameters(self, parameters):
+ def _write_parameters(self, parent, parameters):
if not parameters:
return
with self.tagcontext('parameters'):
for parameter in parameters:
- self._write_parameter(parameter)
-
- def _write_parameter(self, parameter):
- assert parameter.transfer is not None, parameter
+ self._write_parameter(parent, parameter)
+ def _write_parameter(self, parent, parameter):
attrs = []
- if parameter.name is not None:
- attrs.append(('name', parameter.name))
- if parameter.direction != 'in':
+ if parameter.argname is not None:
+ attrs.append(('name', parameter.argname))
+ if (parameter.direction is not None) and (parameter.direction != 'in'):
attrs.append(('direction', parameter.direction))
attrs.append(('caller-allocates',
'1' if parameter.caller_allocates else '0'))
- attrs.append(('transfer-ownership',
- parameter.transfer))
+ if parameter.transfer:
+ attrs.append(('transfer-ownership',
+ parameter.transfer))
if parameter.allow_none:
attrs.append(('allow-none', '1'))
if parameter.scope:
attrs.append(('scope', parameter.scope))
- if parameter.closure_index >= 0:
- attrs.append(('closure', '%d' % parameter.closure_index))
- if parameter.destroy_index >= 0:
- attrs.append(('destroy', '%d' % parameter.destroy_index))
+ if parameter.closure_name is not None:
+ idx = parent.get_parameter_index(parameter.closure_name)
+ attrs.append(('closure', '%d' % (idx, )))
+ if parameter.destroy_name is not None:
+ idx = parent.get_parameter_index(parameter.destroy_name)
+ attrs.append(('destroy', '%d' % (idx, )))
with self.tagcontext('parameter', attrs):
self._write_generic(parameter)
- self._write_type(parameter.type)
-
- def _type_to_string(self, ntype):
- if isinstance(ntype, basestring):
- return ntype
- return ntype.name
-
- def _write_type(self, ntype, relation=None):
- if isinstance(ntype, basestring):
- typename = ntype
- type_cname = None
- else:
- typename = ntype.name
- type_cname = ntype.ctype
+ self._write_type(parameter.type, function=parent)
+
+ def _type_to_name(self, typeval):
+ if not typeval.resolved:
+ raise AssertionError("Caught unresolved type %r (ctype=%r)" % (typeval, typeval.ctype))
+ assert typeval.target_giname is not None
+ prefix = self._namespace.name + '.'
+ if typeval.target_giname.startswith(prefix):
+ return typeval.target_giname[len(prefix):]
+ return typeval.target_giname
+
+ def _write_type(self, ntype, relation=None, function=None):
+ assert isinstance(ntype, Type), ntype
+ attrs = []
+ if ntype.ctype:
+ attrs.append(('c:type', ntype.ctype))
if isinstance(ntype, Varargs):
with self.tagcontext('varargs', []):
pass
- return
- if isinstance(ntype, Array):
- attrs = []
+ elif isinstance(ntype, Array):
+ if ntype.array_type != Array.C:
+ attrs.insert(0, ('name', ntype.array_type))
if not ntype.zeroterminated:
- attrs.append(('zero-terminated', '0'))
- if ntype.length_param_index >= 0:
- attrs.append(('length', '%d' % (ntype.length_param_index, )))
- if ntype.name in ['GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray']:
- attrs.append(('name', ntype.name))
- attrs.append(('c:type', ntype.ctype))
+ attrs.insert(0, ('zero-terminated', '0'))
if ntype.size is not None:
- attrs.append(('fixed-size', ntype.size))
+ attrs.append(('fixed-size', '%d' % (ntype.size, )))
+ if ntype.length_param_name is not None:
+ assert function
+ attrs.insert(0, ('length', '%d'
+ % (function.get_parameter_index(ntype.length_param_name, ))))
with self.tagcontext('array', attrs):
- if ntype.element_type is not None:
- self._write_type(ntype.element_type)
- else:
- self._write_type(Type(TYPE_ANY, ctype='gpointer'))
- return
- attrs = [('name', self._type_to_string(ntype))]
- # FIXME: figure out if type references a basic type
- # or a boxed/class/interface etc. and skip
- # writing the ctype if the latter.
- if type_cname is not None:
- attrs.append(('c:type', type_cname))
- if (isinstance(ntype, List)
- or typename in ('GLib.List',
- 'GLib.SList')):
+ self._write_type(ntype.element_type)
+ elif isinstance(ntype, List):
+ if ntype.name:
+ attrs.insert(0, ('name', ntype.name))
with self.tagcontext('type', attrs):
- if isinstance(ntype, List) and ntype.element_type:
- self._write_type(ntype.element_type)
- else:
- self._write_type(Type(TYPE_ANY, ctype='gpointer'))
- return
- if isinstance(ntype, Map) and ntype.key_type:
+ self._write_type(ntype.element_type)
+ elif isinstance(ntype, Map):
+ attrs.insert(0, ('name', 'GLib.HashTable'))
with self.tagcontext('type', attrs):
self._write_type(ntype.key_type)
self._write_type(ntype.value_type)
- return
- # Not a special type, just write it out
- self.write_tag('type', attrs)
+ else:
+ # REWRITEFIXME - enable this for 1.2
+ if ntype.target_giname:
+ attrs.insert(0, ('name', self._type_to_name(ntype)))
+ elif ntype.target_fundamental:
+ # attrs = [('fundamental', ntype.target_fundamental)]
+ attrs.insert(0, ('name', ntype.target_fundamental))
+ elif ntype.target_foreign:
+ attrs.insert(0, ('foreign', '1'))
+ self.write_tag('type', attrs)
def _write_enum(self, enum):
attrs = [('name', enum.name)]
@@ -325,20 +329,21 @@ and/or use gtk-doc annotations. ''')
self.write_tag('member', attrs)
def _write_constant(self, constant):
- attrs = [('name', constant.name),
- ('value', str(constant.value))]
+ attrs = [('name', constant.name), ('value', constant.value)]
with self.tagcontext('constant', attrs):
- self._write_type(constant.type)
+ self._write_type(constant.value_type)
def _write_class(self, node):
attrs = [('name', node.name),
+ ('c:symbol-prefix', node.c_symbol_prefix),
('c:type', node.ctype)]
self._append_version(node, attrs)
self._append_node_generic(node, attrs)
if isinstance(node, Class):
tag_name = 'class'
if node.parent is not None:
- attrs.append(('parent', node.parent))
+ attrs.append(('parent',
+ self._type_to_name(node.parent)))
if node.is_abstract:
attrs.append(('abstract', '1'))
else:
@@ -348,7 +353,8 @@ and/or use gtk-doc annotations. ''')
if node.get_type:
attrs.append(('glib:get-type', node.get_type))
if node.glib_type_struct:
- attrs.append(('glib:type-struct', node.glib_type_struct.name))
+ attrs.append(('glib:type-struct',
+ self._type_to_name(node.glib_type_struct)))
if isinstance(node, GLibObject):
if node.fundamental:
attrs.append(('glib:fundamental', '1'))
@@ -364,10 +370,12 @@ and/or use gtk-doc annotations. ''')
self._write_generic(node)
if isinstance(node, GLibObject):
for iface in sorted(node.interfaces):
- self.write_tag('implements', [('name', iface)])
+ self.write_tag('implements',
+ [('name', self._type_to_name(iface))])
if isinstance(node, Interface):
for iface in sorted(node.prerequisites):
- self.write_tag('prerequisite', [('name', iface)])
+ self.write_tag('prerequisite',
+ [('name', self._type_to_name(iface))])
if isinstance(node, Class):
for method in sorted(node.constructors):
self._write_constructor(method)
@@ -394,6 +402,8 @@ and/or use gtk-doc annotations. ''')
self._write_constructor(method)
for method in sorted(boxed.methods):
self._write_method(method)
+ for method in sorted(boxed.static_methods):
+ self._write_static_method(method)
def _write_property(self, prop):
attrs = [('name', prop.name)]
@@ -408,7 +418,8 @@ and/or use gtk-doc annotations. ''')
attrs.append(('construct', '1'))
if prop.construct_only:
attrs.append(('construct-only', '1'))
- attrs.append(('transfer-ownership', prop.transfer))
+ if prop.transfer:
+ attrs.append(('transfer-ownership', prop.transfer))
with self.tagcontext('property', attrs):
self._write_generic(prop)
self._write_type(prop.type)
@@ -416,16 +427,19 @@ and/or use gtk-doc annotations. ''')
def _write_vfunc(self, vf):
attrs = []
if vf.invoker:
- attrs.append(('invoker', vf.invoker.name))
+ attrs.append(('invoker', vf.invoker))
self._write_callable(vf, 'virtual-method', attrs)
def _write_callback(self, callback):
- attrs = [('c:type', callback.ctype)]
+ attrs = []
+ if callback.namespace:
+ attrs.append(('c:type', callback.c_name))
self._write_callable(callback, 'callback', attrs)
def _boxed_attrs(self, boxed):
return [('glib:type-name', boxed.type_name),
- ('glib:get-type', boxed.get_type)]
+ ('glib:get-type', boxed.get_type),
+ ('c:symbol-prefix', boxed.c_symbol_prefix)]
def _write_record(self, record, extra_attrs=[]):
is_gtype_struct = False
@@ -442,7 +456,7 @@ and/or use gtk-doc annotations. ''')
if record.is_gtype_struct_for:
is_gtype_struct = True
attrs.append(('glib:is-gtype-struct-for',
- record.is_gtype_struct_for))
+ self._type_to_name(record.is_gtype_struct_for)))
self._append_version(record, attrs)
self._append_node_generic(record, attrs)
if isinstance(record, GLibBoxed):
@@ -456,6 +470,8 @@ and/or use gtk-doc annotations. ''')
self._write_constructor(method)
for method in sorted(record.methods):
self._write_method(method)
+ for method in sorted(record.static_methods):
+ self._write_static_method(method)
def _write_union(self, union):
attrs = []
@@ -476,27 +492,26 @@ and/or use gtk-doc annotations. ''')
self._write_constructor(method)
for method in sorted(union.methods):
self._write_method(method)
+ for method in sorted(union.static_methods):
+ self._write_static_method(method)
def _write_field(self, field, is_gtype_struct=False):
- if isinstance(field, Function):
- self._write_method(field)
- return
-
- if isinstance(field, Callback):
- attrs = [('name', field.name)]
- with self.tagcontext('field', attrs):
- self._write_generic(field)
- if is_gtype_struct:
- self._write_callback(field)
- else:
- attrs = [('name', TYPE_ANY), ('c:type', 'pointer')]
- self.write_tag('type', attrs)
- elif isinstance(field, Struct):
- self._write_record(field)
- elif isinstance(field, Union):
- self._write_union(field)
+ if field.anonymous_node:
+ if isinstance(field.anonymous_node, Callback):
+ attrs = [('name', field.name)]
+ self._append_node_generic(field, attrs)
+ with self.tagcontext('field', attrs):
+ self._write_callback(field.anonymous_node)
+ elif isinstance(field.anonymous_node, Record):
+ self._write_record(field.anonymous_node)
+ elif isinstance(field.anonymous_node, Union):
+ self._write_union(field.anonymous_node)
+ else:
+ raise AssertionError("Unknown field anonymous: %r" \
+ % (field.anonymous_node, ))
else:
attrs = [('name', field.name)]
+ self._append_node_generic(field, attrs)
# Fields are assumed to be read-only
# (see also girparser.c and generate.c)
if not field.readable:
@@ -516,4 +531,4 @@ and/or use gtk-doc annotations. ''')
with self.tagcontext('glib:signal', attrs):
self._write_generic(signal)
self._write_return_type(signal.retval)
- self._write_parameters(signal.parameters)
+ self._write_parameters(signal, signal.parameters)
diff --git a/giscanner/glibast.py b/giscanner/glibast.py
index ad87926f..85092b89 100644
--- a/giscanner/glibast.py
+++ b/giscanner/glibast.py
@@ -18,12 +18,11 @@
# Boston, MA 02111-1307, USA.
#
-from .ast import (Bitfield, Class, Enum, Interface, Member, Node,
- Property, Union, Record)
+from . import ast
-class GLibRecord(Record):
+class GLibRecord(ast.Record):
def __init__(self, *args, **kwargs):
- Record.__init__(self, *args, **kwargs)
+ ast.Record.__init__(self, *args, **kwargs)
@classmethod
def from_record(cls, record):
@@ -38,10 +37,10 @@ class GLibRecord(Record):
obj.is_gtype_struct_for = False
return obj
-class GLibEnum(Enum):
+class GLibEnum(ast.Enum):
def __init__(self, name, type_name, members, get_type):
- Enum.__init__(self, name, type_name, members)
+ ast.Enum.__init__(self, name, type_name, members)
self.ctype = type_name
self.type_name = type_name
self.get_type = get_type
@@ -52,10 +51,10 @@ class GLibEnum(Enum):
self.get_type)
-class GLibFlags(Bitfield):
+class GLibFlags(ast.Bitfield):
def __init__(self, name, type_name, members, get_type):
- Bitfield.__init__(self, name, type_name, members)
+ ast.Bitfield.__init__(self, name, type_name, members)
self.ctype = type_name
self.type_name = type_name
self.get_type = get_type
@@ -65,20 +64,21 @@ class GLibFlags(Bitfield):
self.get_type)
-class GLibEnumMember(Member):
+class GLibEnumMember(ast.Member):
def __init__(self, name, value, symbol, nick):
- Member.__init__(self, name, value, symbol)
+ ast.Member.__init__(self, name, value, symbol)
self.nick = nick
-class GLibObject(Class):
+class GLibObject(ast.Class):
def __init__(self, name, parent, type_name, get_type,
- is_abstract, ctype=None):
- Class.__init__(self, name, parent, is_abstract)
+ c_symbol_prefix, is_abstract, ctype=None):
+ ast.Class.__init__(self, name, parent, is_abstract)
self.type_name = type_name
self.get_type = get_type
+ self.c_symbol_prefix = c_symbol_prefix
self.fundamental = False
self.unref_func = None
self.ref_func = None
@@ -87,60 +87,75 @@ class GLibObject(Class):
self.signals = []
self.ctype = ctype or type_name
+ def _walk(self, callback, chain):
+ super(GLibObject, self)._walk(callback, chain)
+ for sig in self.signals:
+ sig.walk(callback, chain)
+
class GLibBoxed:
- def __init__(self, type_name, get_type):
+ def __init__(self, type_name, get_type, c_symbol_prefix):
self.type_name = type_name
self.get_type = get_type
+ self.c_symbol_prefix = c_symbol_prefix
+class GLibBoxedStruct(ast.Record, GLibBoxed):
-
-class GLibBoxedStruct(Record, GLibBoxed):
-
- def __init__(self, name, type_name, get_type, ctype=None):
- Record.__init__(self, name, ctype or type_name)
- GLibBoxed.__init__(self, type_name, get_type)
+ def __init__(self, name, type_name, get_type, c_symbol_prefix, ctype=None):
+ ast.Record.__init__(self, name, ctype or type_name)
+ GLibBoxed.__init__(self, type_name, get_type, c_symbol_prefix)
-class GLibBoxedUnion(Union, GLibBoxed):
+class GLibBoxedUnion(ast.Union, GLibBoxed):
- def __init__(self, name, type_name, get_type, ctype=None):
- Union.__init__(self, name, ctype or type_name)
- GLibBoxed.__init__(self, type_name, get_type)
+ def __init__(self, name, type_name, get_type, c_symbol_prefix, ctype=None):
+ ast.Union.__init__(self, name, ctype or type_name)
+ GLibBoxed.__init__(self, type_name, get_type, c_symbol_prefix)
-class GLibBoxedOther(Node, GLibBoxed):
+class GLibBoxedOther(ast.Node, GLibBoxed):
- def __init__(self, name, type_name, get_type):
- Node.__init__(self, name)
- GLibBoxed.__init__(self, type_name, get_type)
+ def __init__(self, name, type_name, get_type, c_symbol_prefix):
+ ast.Node.__init__(self, name)
+ GLibBoxed.__init__(self, type_name, get_type, c_symbol_prefix)
self.constructors = []
self.methods = []
+ self.static_methods = []
self.ctype = type_name
self.doc = None
+ def _walk(self, callback, chain):
+ for ctor in self.constructors:
+ ctor.walk(callback, chain)
+ for meth in self.methods:
+ meth.walk(callback, chain)
+ for meth in self.static_methods:
+ meth.walk(callback, chain)
-class GLibInterface(Interface):
+
+class GLibInterface(ast.Interface):
def __init__(self, name, parent, type_name, get_type,
- ctype=None):
- Interface.__init__(self, name, parent)
+ c_symbol_prefix, ctype=None):
+ ast.Interface.__init__(self, name, parent)
self.type_name = type_name
self.get_type = get_type
+ self.c_symbol_prefix = c_symbol_prefix
self.signals = []
self.ctype = ctype or type_name
+ def _walk(self, callback, chain):
+ super(GLibInterface, self)._walk(callback, chain)
+ for sig in self.signals:
+ sig.walk(callback, chain)
-class GLibProperty(Property):
+class GLibProperty(ast.Property):
pass
-class GLibSignal(Node):
+class GLibSignal(ast.Callable):
- def __init__(self, name, retval):
- Node.__init__(self, name)
- self.retval = retval
- self.parameters = []
- self.doc = None
+ def __init__(self, name, retval, parameters):
+ ast.Callable.__init__(self, name, retval, parameters, False)
diff --git a/giscanner/glibtransformer.py b/giscanner/glibtransformer.py
deleted file mode 100644
index 560be915..00000000
--- a/giscanner/glibtransformer.py
+++ /dev/null
@@ -1,1205 +0,0 @@
-# -*- Mode: Python -*-
-# GObject-Introspection - a framework for introspecting GObject libraries
-# Copyright (C) 2008 Johan Dahlin
-#
-# 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.
-#
-
-import os
-import sys
-import re
-import tempfile
-import shutil
-import subprocess
-
-from .ast import (Alias, Bitfield, Callable, Callback, Class, Constant, Enum,
- Function, Interface, Member, Namespace, Node, Parameter,
- Property, Record, Return, Type, TypeContainer, Union,
- Field, VFunction, type_name_from_ctype, default_array_types,
- TYPE_UINT8, PARAM_TRANSFER_FULL, Array, List,
- TYPE_LONG_LONG, TYPE_LONG_DOUBLE,
- Map, Varargs, type_names)
-from .transformer import Names
-from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
- GLibInterface, GLibObject, GLibSignal, GLibBoxedStruct,
- GLibBoxedUnion, GLibBoxedOther, GLibRecord)
-from .utils import to_underscores, to_underscores_noprefix
-
-default_array_types['guchar*'] = TYPE_UINT8
-
-# GParamFlags
-G_PARAM_READABLE = 1 << 0
-G_PARAM_WRITABLE = 1 << 1
-G_PARAM_CONSTRUCT = 1 << 2
-G_PARAM_CONSTRUCT_ONLY = 1 << 3
-G_PARAM_LAX_VALIDATION = 1 << 4
-G_PARAM_STATIC_NAME = 1 << 5
-G_PARAM_STATIC_NICK = 1 << 6
-G_PARAM_STATIC_BLURB = 1 << 7
-
-SYMBOL_BLACKLIST = [
- # These ones break GError conventions
- 'g_simple_async_result_new_from_error',
- 'g_simple_async_result_set_from_error',
- 'g_simple_async_result_propagate_error',
- 'g_simple_async_result_report_error_in_idle',
- 'gtk_print_operation_get_error',
-]
-
-SYMBOL_BLACKLIST_RE = [re.compile(x) for x in \
- [r'\w+_marshal_[A-Z]+__', ]]
-
-GET_TYPE_OVERRIDES = {
- # this is a special case, from glibtransforer.py:create_gobject
- 'intern': 'g_object_get_type',
- # this is presumably a typo, should be fixed upstream
- 'g_gstring_get_type': 'g_string_get_type',
- # this is historical cruft: there's a deprecated
- # #define gdk_window_get_type gdk_window_get_window_type
- # upstream; this method can be renamed properly upstream once
- # that deprecated alias is removed (in some future release)
- 'gdk_window_object_get_type': 'gdk_window_get_type',
-}
-
-
-class IntrospectionBinary(object):
-
- def __init__(self, args, tmpdir=None):
- self.args = args
- if tmpdir is None:
- self.tmpdir = tempfile.mkdtemp('', 'tmp-introspect')
- else:
- self.tmpdir = tmpdir
-
-
-class Unresolved(object):
-
- def __init__(self, target):
- self.target = target
-
-
-class UnknownTypeError(Exception):
- pass
-
-
-class GLibTransformer(object):
-
- def __init__(self, transformer, noclosure=False):
- self._transformer = transformer
- self._noclosure = noclosure
- self._namespace_name = None
- self._names = Names()
- self._uscore_type_names = {}
- self._binary = None
- self._get_type_functions = []
- self._error_quark_functions = []
- self._gtype_data = {}
- self._failed_types = {}
- self._boxed_types = {}
- self._private_internal_types = {}
- self._validating = False
-
- # Public API
-
- def init_parse(self):
- """Do parsing steps that don't involve the introspection binary
-
- This does enough work that get_type_functions() can be called.
-
- """
-
- namespace = self._transformer.parse()
- self._namespace_name = namespace.name
- self._namespace_version = namespace.version
-
- # First pass: parsing
- for node in namespace.nodes:
- self._parse_node(node)
-
- # We don't want an alias for this - it's handled specially in
- # the typelib compiler.
- if namespace.name == 'GObject':
- del self._names.aliases['Type']
-
- def get_get_type_functions(self):
- return self._get_type_functions
-
- def set_introspection_binary(self, binary):
- self._binary = binary
-
- def parse(self):
- """Do remaining parsing steps requiring introspection binary"""
-
- # Get all the GObject data by passing our list of get_type
- # functions to the compiled binary
-
- self._execute_binary()
-
- # Introspection is done from within parsing
-
- # Second pass: pair boxed structures
- for boxed in self._boxed_types.itervalues():
- self._pair_boxed_type(boxed)
- # Third pass: delete class structures, resolve
- # all types we now know about
- nodes = list(self._names.names.itervalues())
- for (ns, node) in nodes:
- try:
- self._resolve_node(node)
- except KeyError, e:
- self._transformer.log_node_warning(node,
-"""Unresolvable entry %r""" % (e, ))
- self._remove_attribute(node.name)
- # Another pass, since we need to have the methods parsed
- # in order to correctly modify them after class/record
- # pairing
- for (ns, node) in nodes:
- # associate GtkButtonClass with GtkButton
- if isinstance(node, Record):
- self._pair_class_record(node)
- for (ns, alias) in self._names.aliases.itervalues():
- self._resolve_alias(alias)
-
- self._resolve_quarks()
-
- # Our final pass replacing types
- self._resolve_types(nodes)
-
- # Create a new namespace with what we found
- namespace = Namespace(self._namespace_name, self._namespace_version)
- namespace.nodes = map(lambda x: x[1], self._names.aliases.itervalues())
- for (ns, x) in self._names.names.itervalues():
- namespace.nodes.append(x)
- return namespace
-
- # Private
-
- def _add_attribute(self, node, replace=False):
- node_name = node.name
- if (not replace) and node_name in self._names.names:
- return
- self._names.names[node_name] = (None, node)
-
- def _remove_attribute(self, name):
- del self._names.names[name]
-
- def _get_attribute(self, name):
- node = self._names.names.get(name)
- if node:
- return node[1]
- return None
-
- def _lookup_node(self, name):
- if name in type_names:
- return None
- node = self._get_attribute(name)
- if node is None:
- node = self._transformer.get_names().names.get(name)
- if node:
- return node[1]
- return node
-
- def _get_no_uscore_prefixed_name(self, type_name):
- # Besides the straight underscore conversion, we also try
- # removing the underscores from the namespace as a possible C
- # mapping; e.g. it's webkit_web_view, not web_kit_web_view
- suffix = self._transformer.remove_prefix(type_name)
- prefix = type_name[:-len(suffix)]
- return (prefix + '_' + to_underscores(suffix)).lower()
-
- def _register_internal_type(self, type_name, node):
- self._names.type_names[type_name] = (None, node)
- uscored = to_underscores(type_name).lower()
- # prefer the prefix of the get_type method, if there is one
- if hasattr(node, 'get_type'):
- uscored = GET_TYPE_OVERRIDES.get(node.get_type, node.get_type)
- uscored = uscored[:-len('_get_type')]
- self._uscore_type_names[uscored] = node
-
- no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
- # since this is a guess, don't overwrite any 'real' prefix
- if no_uscore_prefixed not in self._uscore_type_names:
- self._uscore_type_names[no_uscore_prefixed] = node
-
- def _resolve_quarks(self):
- # self._uscore_type_names is an authoritative mapping of types
- # to underscored versions, since it is based on get_type() methods;
- # but only covers enums that are registered as GObject enums.
- # Create a fallback mapping based on all known enums in this module.
- uscore_enums = {}
- for enum in self._transformer.iter_enums():
- type_name = enum.symbol
- uscored = to_underscores(type_name).lower()
-
- uscore_enums[uscored] = enum
-
- no_uscore_prefixed = self._get_no_uscore_prefixed_name(type_name)
- if no_uscore_prefixed not in uscore_enums:
- uscore_enums[no_uscore_prefixed] = enum
-
- for node in self._error_quark_functions:
- short = node.symbol[:-len('_quark')]
- if short == "g_io_error":
- # Special case; GIOError was already taken forcing GIOErrorEnum
- enum = self._names.type_names["GIOErrorEnum"][1]
- else:
- enum = self._uscore_type_names.get(short)
- if enum is None:
- enum = uscore_enums.get(short)
- if enum is not None:
- enum.error_quark = node.symbol
- else:
- self._transformer.log_node_warning(node,
-"""Couldn't find corresponding enumeration""")
-
- # Helper functions
-
- def _resolve_gtypename(self, gtype_name):
- try:
- return self._transformer.gtypename_to_giname(gtype_name,
- self._names)
- except KeyError, e:
- return Unresolved(gtype_name)
-
- def _resolve_gtypename_chain(self, gtype_names):
- """Like _resolve_gtypename, but grab the first one that resolves.
- If none of them do, return an Unresolved for the first."""
- for gtype_name in gtype_names:
- try:
- return self._transformer.gtypename_to_giname(gtype_name,
- self._names)
- except KeyError, e:
- continue
- return Unresolved(gtype_names[0])
-
- def _execute_binary(self):
- in_path = os.path.join(self._binary.tmpdir, 'types.txt')
- f = open(in_path, 'w')
- # TODO: Introspect GQuark functions
- for func in self._get_type_functions:
- f.write(func)
- f.write('\n')
- f.close()
- out_path = os.path.join(self._binary.tmpdir, 'dump.xml')
-
- args = []
- args.extend(self._binary.args)
- args.append('--introspect-dump=%s,%s' % (in_path, out_path))
-
- # Invoke the binary, having written our get_type functions to types.txt
- try:
- subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
- except subprocess.CalledProcessError, e:
- raise SystemExit(e)
- self._read_introspect_dump(out_path)
-
- # Clean up temporaries
- shutil.rmtree(self._binary.tmpdir)
-
- def _read_introspect_dump(self, xmlpath):
- from xml.etree.cElementTree import parse
- tree = parse(xmlpath)
- root = tree.getroot()
- for child in root:
- self._gtype_data[child.attrib['name']] = child
- for child in root:
- self._introspect_type(child)
-
- def _create_gobject(self, node):
- type_name = 'G' + node.name
- if type_name == 'GObject':
- parent_gitype = None
- symbol = 'intern'
- elif type_name == 'GInitiallyUnowned':
- parent_type_name = 'GObject'
- parent_gitype = self._resolve_gtypename(parent_type_name)
- symbol = 'g_initially_unowned_get_type'
- else:
- assert False
- gnode = GLibObject(node.name, parent_gitype, type_name, symbol, True)
- if type_name == 'GObject':
- gnode.fields.extend(node.fields)
- else:
- # http://bugzilla.gnome.org/show_bug.cgi?id=569408
- # GInitiallyUnowned is actually a typedef for GObject, but
- # that's not reflected in the GIR, where it appears as a
- # subclass (as it appears in the GType hierarchy). So
- # what we do here is copy all of the GObject fields into
- # GInitiallyUnowned so that struct offset computation
- # works correctly.
- gnode.fields = self._names.names['Object'][1].fields
- self._add_attribute(gnode)
- self._register_internal_type(type_name, gnode)
-
- # Parser
-
- def _parse_node(self, node):
- if isinstance(node, Enum):
- self._parse_enum(node)
- elif isinstance(node, Bitfield):
- self._parse_bitfield(node)
- elif isinstance(node, Function):
- self._parse_function(node)
- elif isinstance(node, Record):
- self._parse_record(node)
- elif isinstance(node, Callback):
- self._parse_callback(node)
- elif isinstance(node, Alias):
- self._parse_alias(node)
- elif isinstance(node, Member):
- # FIXME: atk_misc_instance singletons
- pass
- elif isinstance(node, Union):
- self._parse_union(node)
- elif isinstance(node, Constant):
- self._parse_constant(node)
- else:
- print 'GLIB Transformer: Unhandled node:', node
-
- def _parse_alias(self, alias):
- self._names.aliases[alias.name] = (None, alias)
-
- def _parse_enum(self, enum):
- self._add_attribute(enum)
-
- def _parse_bitfield(self, enum):
- self._add_attribute(enum)
-
- def _parse_constant(self, constant):
- self._add_attribute(constant)
-
- def _parse_function(self, func):
- if func.symbol in SYMBOL_BLACKLIST:
- return
- if func.symbol.startswith('_'):
- return
- for regexp in SYMBOL_BLACKLIST_RE:
- if regexp.match(func.symbol):
- return
- if self._parse_get_type_function(func):
- return
- if self._parse_error_quark_function(func):
- return
-
- self._add_attribute(func)
-
- def _parse_get_type_function(self, func):
- symbol = func.symbol
- if not symbol.endswith('_get_type'):
- return False
- if self._namespace_name == 'GLib':
- # No GObjects in GLib
- return False
- if (self._namespace_name == 'GObject' and
- symbol in ('g_object_get_type', 'g_initially_unowned_get_type')):
- # We handle these internally, see _create_gobject
- return True
- if func.parameters:
- return False
- # GType *_get_type(void)
- if func.retval.type.name not in ['Type',
- 'GType',
- 'GObject.Type',
- 'Gtk.Type']:
- self._transformer.log_("Warning: *_get_type function returns '%r'"
- ", not GObject.Type") % (func.retval.type.name, )
- return False
-
- self._get_type_functions.append(symbol)
- return True
-
- def _parse_error_quark_function(self, func):
- if not func.symbol.endswith('_error_quark'):
- return False
- if func.parameters:
- return False
- if (func.retval.type.name != 'GLib.Quark' and
- func.retval.type.ctype != 'GQuark'):
- return False
-
- self._error_quark_functions.append(func)
- return True
-
- def _name_is_internal_gtype(self, giname):
- try:
- node = self._get_attribute(giname)
- return isinstance(node, (GLibObject, GLibInterface,
- GLibBoxed, GLibEnum, GLibFlags))
- except KeyError, e:
- return False
-
- def _parse_static_method(self, func):
- components = func.symbol.split('_')
- if len(components) < 2:
- return None
- target_klass = None
- prefix_components = None
- methname = None
- for i in xrange(1, len(components)):
- prefix_components = '_'.join(components[0:-i])
- methname = '_'.join(components[-i:])
- target_klass = self._uscore_type_names.get(prefix_components)
- if target_klass and isinstance(target_klass, GLibObject):
- break
- target_klass = None
- if not target_klass:
- return None
- self._remove_attribute(func.name)
- func.name = methname
- target_klass.static_methods.append(func)
- func.is_method = True
- return func
-
- def _parse_method(self, func):
- if not func.parameters:
- return False
- return self._parse_method_common(func, True)
-
- def _parse_constructor(self, func):
- return self._parse_method_common(func, False)
-
- def _parse_method_common(self, func, is_method):
- # Skip _get_type functions, we processed them
- # already
- if func.symbol.endswith('_get_type'):
- return None
-
- if not is_method:
- target_arg = func.retval
- else:
- target_arg = func.parameters[0]
-
- if is_method:
- # Methods require their first arg to be a known class
- # Look at the original C type (before namespace stripping), without
- # pointers: GtkButton -> gtk_button_, so we can figure out the
- # method name
- argtype = target_arg.type.ctype.replace('*', '')
- name = self._transformer.remove_prefix(argtype)
- name_uscore = to_underscores_noprefix(name).lower()
- # prefer the prefix of the _get_type method, if there is one
- if argtype in self._names.type_names:
- node = self._names.type_names[argtype][1]
- if hasattr(node, 'get_type'):
- name_uscore = GET_TYPE_OVERRIDES.get(node.get_type,
- node.get_type)
- name_uscore = name_uscore[:-len('_get_type')]
- name_offset = func.symbol.find(name_uscore + '_')
- if name_offset < 0:
- return None
- prefix = func.symbol[:name_offset+len(name_uscore)]
- else:
- # Constructors must have _new
- # Take everything before that as class name
- new_idx = func.symbol.find('_new')
- if new_idx < 0:
- return None
- # Constructors don't return basic types
- derefed = self._transformer.follow_aliases(target_arg.type.name,
- self._names)
- if derefed in type_names:
- #print "NOTE: Rejecting constructor returning basic: %r" \
- # % (func.symbol, )
- return None
- prefix = func.symbol[:new_idx]
-
- klass = self._uscore_type_names.get(prefix)
- if klass is None:
- #print "NOTE: No valid matching class for likely "+\
- # "method or constructor: %r" % (func.symbol, )
- return None
- # Enums can't have ctors or methods
- if isinstance(klass, (GLibEnum, GLibFlags)):
- return None
-
- # The _uscore_type_names member holds the plain GLibBoxed
- # object; we want to actually use the struct/record associated
- if isinstance(klass, (Record, Union)):
- remove_prefix = klass.symbol
- else:
- remove_prefix = klass.type_name
-
- name = self._transformer.remove_prefix(remove_prefix)
- klass = self._get_attribute(name)
- if klass is None:
- return
-
- if not is_method:
- # Interfaces can't have constructors, punt to global scope
- if isinstance(klass, GLibInterface):
- #print "NOTE: Rejecting constructor for"+\
- # " interface type: %r" % (func.symbol, )
- return None
- # TODO - check that the return type is a subclass of the
- # class from the prefix
- # But for now, ensure that constructor returns are always
- # the most concrete class
- name = self._transformer.remove_prefix(remove_prefix)
- func.retval.type = Type(name, func.retval.type.ctype)
-
- self._remove_attribute(func.name)
- # Strip namespace and object prefix: gtk_window_new -> new
- func.name = func.symbol[len(prefix)+1:]
- if is_method:
- # We don't need the "this" parameter
- del func.parameters[0]
- klass.methods.append(func)
- func.is_method = True
- else:
- klass.constructors.append(func)
- return func
-
- def _parse_record(self, record):
- # This is a hack, but GObject is a rather fundamental piece so.
- internal_names = ["Object", 'InitiallyUnowned']
- g_internal_names = ["G" + x for x in internal_names]
- if (self._namespace_name == 'GObject' and
- record.name in internal_names):
- self._create_gobject(record)
- return
- elif record.name in g_internal_names:
- # Avoid duplicates
- return
- if record.name == 'InitiallyUnownedClass':
- record.fields = self._names.names['ObjectClass'][1].fields
- node = self._names.names.get(record.name)
- if node is None:
- self._add_attribute(record, replace=True)
- self._register_internal_type(record.symbol, record)
- return
- (ns, node) = node
- node.fields = record.fields[:]
-
- def _parse_union(self, union):
- node = self._names.names.get(union.name)
- if node is None:
- self._add_attribute(union, replace=True)
- self._register_internal_type(union.symbol, union)
- return
- (ns, node) = node
- node.fields = union.fields[:]
-
- def _parse_callback(self, callback):
- self._add_attribute(callback)
-
- def _strip_class_suffix(self, name):
- if (name.endswith('Class') or
- name.endswith('Iface')):
- return name[:-5]
- elif name.endswith('Interface'):
- return name[:-9]
- else:
- return name
-
- def _arg_is_failed(self, param):
- ctype = self._transformer.ctype_of(param).replace('*', '')
- uscored = to_underscores(self._strip_class_suffix(ctype)).lower()
- if uscored in self._failed_types:
- print "Warning: failed type: %r" % (param, )
- return True
- return False
-
- def _pair_class_record(self, maybe_class):
- name = self._strip_class_suffix(maybe_class.name)
- if name == maybe_class.name:
- return
-
- class_struct = maybe_class
- if self._arg_is_failed(class_struct):
- print "WARNING: deleting no-type %r" % (class_struct.name, )
- del self._names.names[class_struct.name]
- return
-
- pair_class = self._get_attribute(name)
- if (not pair_class or
- not isinstance(pair_class, (GLibObject, GLibInterface))):
- return
-
- # Object class fields are assumed to be read-only
- # (see also _introspect_object and transformer.py)
- for field in maybe_class.fields:
- if isinstance(field, Field):
- field.writable = False
-
- # Loop through fields to determine which are virtual
- # functions and which are signal slots by
- # assuming everything that doesn't share a name
- # with a known signal is a virtual slot.
- for field in maybe_class.fields:
- if not isinstance(field, Callback):
- continue
- # Check the first parameter is the object
- if len(field.parameters) == 0:
- continue
- firstparam_type = field.parameters[0].type
- if firstparam_type != pair_class:
- continue
- # Also double check we don't have a signal with this
- # name.
- matched_signal = False
- for signal in pair_class.signals:
- if signal.name.replace('-', '_') == field.name:
- matched_signal = True
- break
- if matched_signal:
- continue
- vfunc = VFunction.from_callback(field)
- vfunc.inherit_file_positions(field)
- pair_class.virtual_methods.append(vfunc)
-
- # Take the set of virtual methods we found, and try
- # to pair up with any matching methods using the
- # name+signature.
- for vfunc in pair_class.virtual_methods:
- for method in pair_class.methods:
- if (method.name != vfunc.name or
- method.retval != vfunc.retval or
- method.parameters != vfunc.parameters):
- continue
- vfunc.invoker = method
-
- gclass_struct = GLibRecord.from_record(class_struct)
- self._remove_attribute(class_struct.name)
- self._add_attribute(gclass_struct, True)
- pair_class.glib_type_struct = gclass_struct
- pair_class.inherit_file_positions(class_struct)
- gclass_struct.is_gtype_struct_for = name
-
- # Introspection
-
- def _introspect_type(self, xmlnode):
- if xmlnode.tag in ('enum', 'flags'):
- self._introspect_enum(xmlnode)
- elif xmlnode.tag == 'class':
- self._introspect_object(xmlnode)
- elif xmlnode.tag == 'interface':
- self._introspect_interface(xmlnode)
- elif xmlnode.tag == 'boxed':
- self._introspect_boxed(xmlnode)
- elif xmlnode.tag == 'fundamental':
- self._introspect_fundamental(xmlnode)
- else:
- raise ValueError("Unhandled introspection XML tag %s", xmlnode.tag)
-
- def _introspect_enum(self, node):
- members = []
- for member in node.findall('member'):
- # Keep the name closer to what we'd take from C by default;
- # see http://bugzilla.gnome.org/show_bug.cgi?id=575613
- name = member.attrib['nick'].replace('-', '_')
- members.append(GLibEnumMember(name,
- member.attrib['value'],
- member.attrib['name'],
- member.attrib['nick']))
-
- klass = (GLibFlags if node.tag == 'flags' else GLibEnum)
- type_name = node.attrib['name']
- enum_name = self._transformer.remove_prefix(type_name)
- node = klass(enum_name, type_name, members, node.attrib['get-type'])
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
-
- def _introspect_object(self, xmlnode):
- type_name = xmlnode.attrib['name']
- # We handle this specially above; in 2.16 and below there
- # was no g_object_get_type, for later versions we need
- # to skip it
- if type_name == 'GObject':
- return
- # Get a list of parents here; some of them may be hidden, and what
- # we really want to do is use the most-derived one that we know of.
- #
- parent_type_names = xmlnode.attrib['parents'].split(',')
- parent_gitype = self._resolve_gtypename_chain(parent_type_names)
- is_abstract = bool(xmlnode.attrib.get('abstract', False))
- node = GLibObject(
- self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name,
- xmlnode.attrib['get-type'], is_abstract)
- self._introspect_properties(node, xmlnode)
- self._introspect_signals(node, xmlnode)
- self._introspect_implemented_interfaces(node, xmlnode)
-
- self._add_record_fields(node)
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
-
- def _introspect_interface(self, xmlnode):
- type_name = xmlnode.attrib['name']
- node = GLibInterface(
- self._transformer.remove_prefix(type_name),
- None,
- type_name, xmlnode.attrib['get-type'])
- self._introspect_properties(node, xmlnode)
- self._introspect_signals(node, xmlnode)
- for child in xmlnode.findall('prerequisite'):
- name = child.attrib['name']
- prereq = self._resolve_gtypename(name)
- node.prerequisites.append(prereq)
- # GtkFileChooserEmbed is an example of a private interface, we
- # just filter them out
- if xmlnode.attrib['get-type'].startswith('_'):
- print "NOTICE: Marking %s as internal type" % (type_name, )
- self._private_internal_types[type_name] = node
- else:
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
-
- def _introspect_boxed(self, xmlnode):
- type_name = xmlnode.attrib['name']
- # This one doesn't go in the main namespace; we associate it with
- # the struct or union
- node = GLibBoxed(type_name, xmlnode.attrib['get-type'])
- self._boxed_types[node.type_name] = node
- self._register_internal_type(type_name, node)
-
- def _introspect_implemented_interfaces(self, node, xmlnode):
- gt_interfaces = []
- for interface in xmlnode.findall('implements'):
- gitype = self._resolve_gtypename(interface.attrib['name'])
- gt_interfaces.append(gitype)
- node.interfaces = gt_interfaces
-
- def _introspect_properties(self, node, xmlnode):
- for pspec in xmlnode.findall('property'):
- ctype = pspec.attrib['type']
- flags = int(pspec.attrib['flags'])
- readable = (flags & G_PARAM_READABLE) != 0
- writable = (flags & G_PARAM_WRITABLE) != 0
- construct = (flags & G_PARAM_CONSTRUCT) != 0
- construct_only = (flags & G_PARAM_CONSTRUCT_ONLY) != 0
- node.properties.append(Property(
- pspec.attrib['name'],
- type_name_from_ctype(ctype),
- readable, writable, construct, construct_only,
- ctype,
- ))
- node.properties = node.properties
-
- def _introspect_signals(self, node, xmlnode):
- for signal_info in xmlnode.findall('signal'):
- rctype = signal_info.attrib['return']
- rtype = Type(self._transformer.parse_ctype(rctype), rctype)
- return_ = Return(rtype, signal_info.attrib['return'])
- return_.transfer = PARAM_TRANSFER_FULL
- signal = GLibSignal(signal_info.attrib['name'], return_)
- for i, parameter in enumerate(signal_info.findall('param')):
- if i == 0:
- name = 'object'
- else:
- name = 'p%s' % (i-1, )
- pctype = parameter.attrib['type']
- ptype = Type(self._transformer.parse_ctype(pctype), pctype)
- param = Parameter(name, ptype)
- param.transfer = 'none'
- signal.parameters.append(param)
- node.signals.append(signal)
- node.signals = node.signals
-
- def _introspect_fundamental(self, xmlnode):
- # We only care about types that can be instantiatable, other
- # fundamental types such as the Clutter.Fixed/CoglFixed registers
- # are not yet interesting from an introspection perspective and
- # are ignored
- if not xmlnode.attrib.get('instantiatable', False):
- return
-
- type_name = xmlnode.attrib['name']
-
- # Get a list of parents here; some of them may be hidden, and what
- # we really want to do is use the most-derived one that we know of.
- if 'parents' in xmlnode.attrib:
- parent_type_names = xmlnode.attrib['parents'].split(',')
- parent_gitype = self._resolve_gtypename_chain(parent_type_names)
- else:
- parent_gitype = None
- is_abstract = bool(xmlnode.attrib.get('abstract', False))
- node = GLibObject(
- self._transformer.remove_prefix(type_name),
- parent_gitype,
- type_name,
- xmlnode.attrib['get-type'], is_abstract)
- node.fundamental = True
- self._introspect_implemented_interfaces(node, xmlnode)
-
- self._add_record_fields(node)
- self._add_attribute(node, replace=True)
- self._register_internal_type(type_name, node)
-
- def _add_record_fields(self, node):
- # add record fields
- record = self._get_attribute(node.name)
- if record is None:
- return
- node.fields = record.fields
- for field in node.fields:
- if isinstance(field, Field):
- # Object instance fields are assumed to be read-only
- # (see also _pair_class_record and transformer.py)
- field.writable = False
-
- def _pair_boxed_type(self, boxed):
- name = self._transformer.remove_prefix(boxed.type_name)
- pair_node = self._get_attribute(name)
- if not pair_node:
- boxed_item = GLibBoxedOther(name, boxed.type_name,
- boxed.get_type)
- elif isinstance(pair_node, Record):
- boxed_item = GLibBoxedStruct(pair_node.name, boxed.type_name,
- boxed.get_type)
- boxed_item.inherit_file_positions(pair_node)
- boxed_item.fields = pair_node.fields
- elif isinstance(pair_node, Union):
- boxed_item = GLibBoxedUnion(pair_node.name, boxed.type_name,
- boxed.get_type)
- boxed_item.inherit_file_positions(pair_node)
- boxed_item.fields = pair_node.fields
- else:
- return False
- self._add_attribute(boxed_item, replace=True)
-
- # Node walking
-
- def _walk(self, node, callback, chain):
- if not isinstance(node, Node):
- return
- if not callback(node, chain):
- return
- chain.append(node)
- def _subwalk(subnode):
- self._walk(subnode, callback, chain)
- if isinstance(node, (Callback, Callable)):
- _subwalk(node.retval)
- for parameter in node.parameters:
- _subwalk(parameter)
- elif isinstance(node, (Array, List)):
- _subwalk(node.element_type)
- elif isinstance(node, Map):
- _subwalk(node.key_type)
- _subwalk(node.value_type)
- elif isinstance(node, Bitfield):
- pass
- elif isinstance(node, Record):
- for ctor in node.constructors:
- _subwalk(ctor)
- for func in node.methods:
- _subwalk(func)
- elif isinstance(node, Field):
- _subwalk(node.type)
- elif isinstance(node, Class):
- for meth in node.methods:
- _subwalk(meth)
- for meth in node.virtual_methods:
- _subwalk(meth)
- for meth in node.static_methods:
- _subwalk(meth)
- for ctor in node.constructors:
- _subwalk(ctor)
- for prop in node.properties:
- _subwalk(prop)
- for field in node.fields:
- _subwalk(field)
- elif isinstance(node, Interface):
- for meth in node.methods:
- _subwalk(meth)
- for meth in node.virtual_methods:
- _subwalk(meth)
- for prop in node.properties:
- _subwalk(prop)
- for field in node.fields:
- _subwalk(field)
- elif isinstance(node, Constant):
- _subwalk(node.type)
- elif isinstance(node, Union):
- for ctor in node.constructors:
- _subwalk(ctor)
- for meth in node.methods:
- _subwalk(meth)
- elif isinstance(node, GLibBoxed):
- for ctor in node.constructors:
- _subwalk(ctor)
- for meth in node.methods:
- _subwalk(meth)
-
- if isinstance(node, (GLibObject, GLibInterface)):
- for sig in node.signals:
- _subwalk(sig)
-
- chain.pop()
-
- # Resolver
-
- def _resolve_type_name(self, type_name, ctype=None):
- # Workaround glib bug #548689, to be included in 2.18.0
- if type_name == "GParam":
- type_name = "GObject.ParamSpec"
- res = self._transformer.resolve_type_name_full
- try:
- return res(type_name, ctype, self._names)
- except KeyError, e:
- return self._transformer.resolve_type_name(type_name, ctype)
-
- def _resolve_param_type(self, ptype, **kwargs):
- # Workaround glib bug #548689, to be included in 2.18.0
- if ptype.name == "GParam":
- ptype.name = "GObject.ParamSpec"
- elif ptype.name == "GObject.Strv":
- return Array(None, ptype.ctype, Type('utf8'))
-
- return self._transformer.resolve_param_type_full(ptype,
- self._names,
- **kwargs)
-
- def _resolve_node(self, node):
- if isinstance(node, Function):
- self._resolve_function_toplevel(node)
-
- elif isinstance(node, Callback):
- self._resolve_function(node)
- elif isinstance(node, GLibObject):
- self._resolve_glib_object(node)
- elif isinstance(node, GLibInterface):
- self._resolve_glib_interface(node)
- elif isinstance(node, Record):
- self._resolve_record(node)
- elif isinstance(node, Union):
- self._resolve_union(node)
- elif isinstance(node, Alias):
- self._resolve_alias(node)
-
- def _resolve_function_toplevel(self, func):
- for parser in [self._parse_constructor,
- self._parse_method,
- self._parse_static_method]:
- newfunc = parser(func)
- if newfunc:
- self._resolve_function(newfunc)
- return
- self._resolve_function(func)
-
- def _resolve_record(self, node):
- for field in node.fields:
- self._resolve_field(field)
-
- def _resolve_union(self, node):
- for field in node.fields:
- self._resolve_field(field)
-
- def _force_resolve(self, item, allow_unknown=False):
- if isinstance(item, Unresolved):
- if item.target in self._private_internal_types:
- return None
- try:
- return self._transformer.gtypename_to_giname(item.target,
- self._names)
- except KeyError, e:
- if allow_unknown:
- self._transformer.log_warning(
-"""Skipping unknown interface %s""" % (item.target, ))
- return None
- else:
- raise
- if item in self._private_internal_types:
- return None
- return item
-
- def _resolve_glib_interface(self, node):
- node.parent = self._force_resolve(node.parent)
- self._resolve_methods(node.methods)
- self._resolve_properties(node.properties, node)
- self._resolve_signals(node.signals)
- node.prerequisites = filter(None,
- [self._force_resolve(x, allow_unknown=True)
- for x in node.prerequisites])
-
- def _resolve_glib_object(self, node):
- # If we can't find the parent class, just drop back to GObject.
- # This supports hidden parent classes.
- # http://bugzilla.gnome.org/show_bug.cgi?id=561360
- try:
- node.parent = self._force_resolve(node.parent)
- except KeyError, e:
- #print ("WARNING: Parent %r of class %r" +\
- # " not found; using GObject") % (node.parent.target,
- # node.name)
- node.parent = self._transformer.gtypename_to_giname("GObject",
- self._names)
- node.interfaces = filter(None,
- [self._force_resolve(x, allow_unknown=True)
- for x in node.interfaces])
- self._resolve_constructors(node.constructors)
- self._resolve_methods(node.methods)
- self._resolve_methods(node.static_methods)
- self._resolve_properties(node.properties, node)
- self._resolve_signals(node.signals)
- for field in node.fields:
- self._resolve_field(field)
-
- def _resolve_glib_boxed(self, node):
- self._resolve_constructors(node.constructors)
- self._resolve_methods(node.methods)
-
- def _resolve_constructors(self, constructors):
- for ctor in constructors:
- self._resolve_function(ctor)
-
- def _resolve_methods(self, methods):
- for method in methods:
- self._resolve_function(method)
-
- def _resolve_signals(self, signals):
- for signal in signals:
- self._resolve_function(signal)
-
- def _resolve_properties(self, properties, context):
- failed = []
- for prop in properties:
- try:
- self._resolve_property(prop)
- except KeyError, e:
- failed.append(prop)
- for fail in failed:
- #print ("WARNING: Deleting object property %r (of %r) "
- # "with unknown type") % (fail, context)
- properties.remove(fail)
-
- def _resolve_property(self, prop):
- prop.type = self._resolve_param_type(prop.type, allow_invalid=False)
-
- def _adjust_throws(self, func):
- if func.parameters == []:
- return
-
- last_param = func.parameters.pop()
-
- # Checking type.name=='GLib.Error' generates false positives
- # on methods that take a 'GError *'
- if last_param.type.ctype == 'GError**':
- 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)
-
- def _resolve_parameters(self, parameters):
- for parameter in parameters:
- parameter.type = self._resolve_param_type(parameter.type)
-
- def _resolve_field(self, field):
- if isinstance(field, Callback):
- self._resolve_function(field)
- elif isinstance(field, Record): # non-typedef'd struct
- self._resolve_record(field)
- elif isinstance(field, Union): # non-typedef'd union
- self._resolve_union(field)
- else:
- field.type = self._resolve_param_type(field.type)
-
- def _resolve_alias(self, alias):
- alias.target = self._resolve_type_name(alias.target, alias.target)
-
- def _resolve_types(self, nodes):
- nodes = list(self._names.names.itervalues())
- i = 0
- self._validating = True
- while True:
- initlen = len(nodes)
-
- nodes = list(self._names.names.itervalues())
- for node in nodes:
- try:
- self._resolve_node(node)
- except UnknownTypeError, e:
- print "WARNING: %s: Deleting %r" % (e, node)
- self._remove_attribute(node.name)
- if len(nodes) == initlen:
- break
- i += 1
- self._validating = False
-
- # Validation
-
- def _interface_vfunc_check(self, node, stack):
- if isinstance(node, GLibInterface):
- for vfunc in node.virtual_methods:
- if not vfunc.invoker:
- self._transformer.log_node_warning(vfunc,
-"""Virtual function %r has no known invoker""" % (vfunc.name, ),
- context=node)
-
- def _is_unannotated_list(self, node):
- # Already annotated
- if isinstance(node.type, List):
- return False
- if (node.type.name == 'GLib.List' or
- node.type.name == 'GLib.SList'):
- return True
- if (self._transformer._namespace.name == 'GLib' and
- (node.type.name == 'List' or
- node.type.name == 'SList')):
- return True
- return False
-
- def _introspectable_analysis(self, node, stack):
- if isinstance(node, TypeContainer):
- parent = stack[-1]
- if node.type.name in [TYPE_LONG_LONG, TYPE_LONG_DOUBLE]:
- parent.introspectable = False
- elif isinstance(node.type, Varargs):
- parent.introspectable = False
- elif self._is_unannotated_list(node):
- if isinstance(node, Parameter):
- self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on argument %r""" % (node.name, ),
- context=parent)
- else:
- self._transformer.log_node_warning(parent,
-"""Missing (element-type) annotation on return value""", context=parent)
- parent.introspectable = False
-
- def _analyze_node(self, node, stack):
- if node.skip:
- return False
- # Combine one-pass checks here
- self._interface_vfunc_check(node, stack)
- # Our first pass for scriptability
- self._introspectable_analysis(node, stack)
- return True
-
- def _introspectable_pass2(self, node, stack):
- if node.skip:
- return False
- # In the second introspectable pass, we propagate introspectablity;
- # for example, a varargs callback as an argument to a function
- # makes the whole function unintrospectable
- if isinstance(node, TypeContainer):
- parent = stack[-1]
- target = self._lookup_node(node.type.name)
- if target and not target.introspectable:
- parent.introspectable = False
- return True
-
- # This function is called at the very end, before we hand back the
- # completed namespace to the writer. Add static analysis checks here.
- def final_analyze(self):
- for (ns, node) in self._names.names.itervalues():
- self._walk(node, self._analyze_node, [])
- for (ns, node) in self._names.names.itervalues():
- self._walk(node, self._introspectable_pass2, [])
diff --git a/giscanner/introspectablepass.py b/giscanner/introspectablepass.py
new file mode 100644
index 00000000..a8809120
--- /dev/null
+++ b/giscanner/introspectablepass.py
@@ -0,0 +1,159 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# 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.
+#
+
+from . import ast
+from . import glibast
+
+class IntrospectablePass(object):
+
+ def __init__(self, transformer):
+ self._transformer = transformer
+ self._namespace = transformer.namespace
+
+ # Public API
+
+ def validate(self):
+ self._namespace.walk(self._analyze_node)
+ self._namespace.walk(self._introspectable_callable_analysis)
+ self._namespace.walk(self._introspectable_callable_analysis)
+ self._namespace.walk(self._introspectable_pass3)
+
+ def _interface_vfunc_check(self, node, stack):
+ if isinstance(node, glibast.GLibInterface):
+ for vfunc in node.virtual_methods:
+ if not vfunc.invoker:
+ self._transformer.log_node_warning(vfunc,
+"""Virtual function %r has no known invoker""" % (vfunc.name, ),
+ context=node)
+
+ def _parameter_warning(self, parent, param, text, *args):
+ if hasattr(parent, 'symbol'):
+ prefix = '%s: ' % (parent.symbol, )
+ else:
+ prefix = ''
+ if isinstance(param, ast.Parameter):
+ context = "argument %s: " % (param.argname, )
+ else:
+ context = "return value: "
+ self._transformer.log_node_warning(parent, prefix + context + text, *args)
+
+ def _introspectable_param_analysis(self, parent, node):
+ if not node.type.resolved:
+ self._parameter_warning(parent, node, "Unresolved ctype: %r" % (node.type.ctype, ))
+ parent.introspectable = False
+ elif isinstance(node.type, ast.Varargs):
+ parent.introspectable = False
+ elif not isinstance(node.type, ast.List) and \
+ (node.type.target_giname == 'GLib.List'):
+ self._parameter_warning(parent, node, "Missing (element-type) annotation")
+ parent.introspectable = False
+ elif node.transfer is None:
+ self._parameter_warning(parent, node, "Missing (transfer) annotation")
+ parent.introspectable = False
+
+ if isinstance(node, ast.Parameter) and node.type.target_giname:
+ target = self._transformer.lookup_typenode(node.type)
+ if (isinstance(target, ast.Callback)
+ and not target.create_type().target_giname in ('GLib.DestroyNotify',
+ 'Gio.AsyncReadyCallback')
+ and node.scope is None):
+ self._parameter_warning(parent, node,
+ ("Missing (scope) annotation for callback" +
+ " without GDestroyNotify (valid: %s, %s)")
+ % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
+ parent.introspectable = False
+
+ def _type_is_introspectable(self, typeval, warn=False):
+ if not typeval.resolved:
+ return False
+ if isinstance(typeval, (ast.Array, ast.List)):
+ return self._type_is_introspectable(typeval.element_type)
+ elif isinstance(typeval, ast.Map):
+ return (self._type_is_introspectable(typeval.key_type)
+ and self._type_is_introspectable(typeval.value_type))
+ if typeval.target_foreign:
+ return True
+ if typeval.target_fundamental:
+ if typeval.is_equiv(ast.TYPE_VALIST):
+ return False
+ # Mark UCHAR as not introspectable temporarily until
+ # we're ready to land the typelib changes
+ if typeval.is_equiv(ast.TYPE_UNICHAR):
+ return False
+ # These are not introspectable pending us adding
+ # larger type tags to the typelib (in theory these could
+ # be 128 bit or larger)
+ if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
+ ast.TYPE_LONG_DOUBLE)):
+ return False
+ return True
+ target = self._transformer.lookup_typenode(typeval)
+ if not target:
+ return False
+ return target.introspectable
+
+ def _analyze_node(self, obj, stack):
+ if obj.skip:
+ return False
+ # Combine one-pass checks here
+ self._interface_vfunc_check(obj, stack)
+ # Our first pass for scriptability
+ if isinstance(obj, ast.Callable):
+ for param in obj.parameters:
+ self._introspectable_param_analysis(obj, param)
+ self._introspectable_param_analysis(obj, obj.retval)
+ if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in obj.fields:
+ if field.type:
+ if not self._type_is_introspectable(field.type):
+ field.introspectable = False
+ return True
+
+ def _introspectable_callable_analysis(self, obj, stack):
+ if obj.skip:
+ return True
+ # Propagate introspectability of parameters to entire functions
+ if isinstance(obj, ast.Callable):
+ for param in obj.parameters:
+ if not self._type_is_introspectable(param.type):
+ obj.introspectable = False
+ return True
+ if not self._type_is_introspectable(obj.retval.type):
+ obj.introspectable = False
+ return True
+ return True
+
+ def _introspectable_pass3(self, obj, stack):
+ if obj.skip:
+ return True
+ # Propagate introspectability for fields
+ if isinstance(obj, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in obj.fields:
+ if field.anonymous_node:
+ if not field.anonymous_node.introspectable:
+ field.introspectable = False
+ else:
+ if not self._type_is_introspectable(field.type):
+ field.introspectable = False
+ # Propagate introspectability for properties
+ if isinstance(obj, (ast.Class, ast.Interface)):
+ for prop in obj.properties:
+ if not self._type_is_introspectable(prop.type):
+ prop.introspectable = False
+ return True
diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py
new file mode 100644
index 00000000..ac180155
--- /dev/null
+++ b/giscanner/maintransformer.py
@@ -0,0 +1,933 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# 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.
+#
+
+import re
+
+from . import ast
+from . import glibast
+from .annotationparser import (TAG_VFUNC, TAG_SINCE, TAG_DEPRECATED, TAG_RETURNS,
+ TAG_ATTRIBUTES, TAG_RENAME_TO, TAG_TYPE, TAG_TRANSFER,
+ TAG_UNREF_FUNC, TAG_REF_FUNC, TAG_SET_VALUE_FUNC,
+ TAG_GET_VALUE_FUNC)
+from .annotationparser import (OPT_ALLOW_NONE,
+ OPT_ARRAY, OPT_ELEMENT_TYPE, OPT_IN, OPT_INOUT,
+ OPT_INOUT_ALT, OPT_OUT, OPT_SCOPE,
+ OPT_TYPE, OPT_CLOSURE, OPT_DESTROY, OPT_SKIP,
+ OPT_FOREIGN, OPT_ARRAY_FIXED_SIZE,
+ OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED)
+from .annotationparser import AnnotationParser
+from .utils import to_underscores, to_underscores_noprefix
+
+class MainTransformer(object):
+
+ def __init__(self, transformer, blocks):
+ self._transformer = transformer
+ self._blocks = blocks
+ self._namespace = transformer.namespace
+ self._uscore_type_names = {}
+
+ # Public API
+
+ def transform(self):
+ # We have a rough tree which should have most of of the types
+ # we know about. Let's attempt closure; walk over all of the
+ # Type() types and see if they match up with something.
+ self._namespace.walk(self._pass_type_resolution)
+
+ # Determine some default values for transfer etc.
+ # based on the current tree.
+ self._namespace.walk(self._pass_callable_defaults)
+
+ # Read in most annotations now.
+ self._namespace.walk(self._pass_read_annotations)
+
+ # Now that we've possibly seen more types from annotations,
+ # do another type resolution pass.
+ self._namespace.walk(self._pass_type_resolution)
+
+ # Generate a reverse mapping "bar_baz" -> BarBaz
+ for node in self._namespace.itervalues():
+ if isinstance(node, (ast.Class, ast.Interface, glibast.GLibBoxed)):
+ self._uscore_type_names[node.c_symbol_prefix] = node
+ elif isinstance(node, (ast.Record, ast.Union)):
+ uscored = to_underscores_noprefix(node.name).lower()
+ self._uscore_type_names[uscored] = node
+
+ for node in list(self._namespace.itervalues()):
+ if isinstance(node, ast.Function):
+ # Discover which toplevel functions are actually methods
+ self._pair_function(node)
+ if isinstance(node, (ast.Class, ast.Interface)):
+ self._pair_class_virtuals(node)
+
+ # Some annotations need to be post function pairing
+ self._namespace.walk(self._pass_read_annotations2)
+
+ # Another type resolution pass after we've parsed virtuals, etc.
+ self._namespace.walk(self._pass_type_resolution)
+
+ self._namespace.walk(self._pass3)
+
+ # TODO - merge into pass3
+ self._resolve_quarks()
+
+ # Private
+
+ def _get_validate_parameter_name(self, parent, param_name, origin):
+ try:
+ param = parent.get_parameter(param_name)
+ except ValueError, e:
+ param = None
+ if param is None:
+ if isinstance(origin, ast.Parameter):
+ origin_name = 'parameter %s' % (origin.argname, )
+ else:
+ origin_name = 'return value'
+ self._transformer.log_node_warning(parent,
+ "can't find parameter %s referenced by %s of %r"
+ % (param_name, origin_name, parent.name), fatal=True)
+
+ return param.argname
+
+ def _apply_annotation_rename_to(self, node, chain, block):
+ if not block:
+ return
+ rename_to = block.get(TAG_RENAME_TO)
+ if not rename_to:
+ return
+ rename_to = rename_to.value
+ target = self._namespace.get_by_symbol(rename_to)
+ if not target:
+ self._transformer.log_node_warning(node,
+"Can't find symbol %r referenced by Rename annotation" % (rename_to, ))
+ elif target.shadowed_by:
+ self._transformer.log_node_warning(node,
+"Function %r already shadowed by %r, can't overwrite with %r" % (target.symbol,
+ target.shadowed_by,
+ rename_to))
+ elif target.shadows:
+ self._transformer.log_node_warning(node,
+"Function %r already shadows %r, can't multiply shadow with %r" % (target.symbol,
+ target.shadows,
+ rename_to))
+ else:
+ target.shadows = node.symbol
+ node.shadowed_by = target.symbol
+
+ def _apply_annotations_function(self, node, chain):
+ block = self._blocks.get(node.symbol)
+ self._apply_annotations_callable(node, chain, block)
+ self._apply_annotation_rename_to(node, chain, block)
+
+ def _pass_callable_defaults(self, node, chain):
+ if isinstance(node, (ast.Callable, glibast.GLibSignal)):
+ for param in node.parameters:
+ if param.transfer is None:
+ param.transfer = self._get_transfer_default(node, param)
+ if node.retval.transfer is None:
+ node.retval.transfer = self._get_transfer_default(node, node.retval)
+ return True
+
+ def _pass_read_annotations(self, node, chain):
+ if not node.namespace:
+ return False
+ if isinstance(node, ast.Function):
+ self._apply_annotations_function(node, chain)
+ if isinstance(node, ast.Callback):
+ block = self._blocks.get(node.c_name)
+ self._apply_annotations_callable(node, chain, block)
+ if isinstance(node, (ast.Class, ast.Interface, ast.Record,
+ ast.Union, ast.Enum, ast.Bitfield,
+ ast.Callback)):
+ block = self._blocks.get(node.c_name)
+ self._apply_annotations_annotated(node, block)
+ if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in node.fields:
+ self._blocks.get('%s::%s' % (node.c_name, field.name))
+ self._apply_annotations_field(node, block, field)
+ if isinstance(node, (ast.Class, ast.Interface)):
+ for prop in node.properties:
+ self._apply_annotations_property(node, prop)
+ for sig in node.signals:
+ self._apply_annotations_signal(node, sig)
+ if isinstance(node, ast.Class):
+ block = self._blocks.get(node.c_name)
+ if block:
+ tag = block.get(TAG_UNREF_FUNC)
+ node.unref_func = tag.value if tag else None
+ tag = block.get(TAG_REF_FUNC)
+ node.ref_func = tag.value if tag else None
+ tag = block.get(TAG_SET_VALUE_FUNC)
+ node.set_value_func = tag.value if tag else None
+ tag = block.get(TAG_GET_VALUE_FUNC)
+ node.get_value_func = tag.value if tag else None
+ return True
+
+ def _adjust_container_type(self, parent, node, options):
+ has_element_type = OPT_ELEMENT_TYPE in options
+ has_array = OPT_ARRAY in options
+
+ if has_array:
+ self._apply_annotations_array(parent, node, options)
+ elif has_element_type:
+ self._apply_annotations_element_type(parent, node, options)
+
+ def _resolve(self, type_str, orig_node=None):
+ def grab_one(type_str, resolver, top_combiner, combiner):
+ """Return a complete type, and the trailing string part after it.
+ Use resolver() on each identifier, and combiner() on the parts of
+ each complete type. (top_combiner is used on the top-most type.)"""
+ bits = re.split(r'([,<>])', type_str, 1)
+ first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+ args = [resolver(first)]
+ if sep == '<':
+ while sep != '>':
+ next, rest = grab_one(rest, resolver, combiner, combiner)
+ args.append(next)
+ sep, rest = rest[0], rest[1:]
+ else:
+ rest = sep + rest
+ return top_combiner(*args), rest
+ def resolver(ident):
+ res = self._transformer.create_type_from_user_string(ident)
+ return res
+ def combiner(base, *rest):
+ if not rest:
+ return base
+ if isinstance(base, ast.List) and len(rest) == 1:
+ return ast.List(base.name, *rest)
+ if isinstance(base, ast.Map) and len(rest) == 2:
+ return ast.Map(*rest)
+ self._transformer.log_warning(
+"Too many parameters in type specification %r" % (type_str, ))
+ return base
+ def top_combiner(base, *rest):
+ if orig_node is not None:
+ base.is_const = orig_node.is_const
+ return combiner(base, *rest)
+
+ result, rest = grab_one(type_str, resolver, top_combiner, combiner)
+ if rest:
+ self._transformer.log_warning(
+"Trailing components in type specification %r" % (type_str, ))
+ return result
+
+ def _apply_annotations_array(self, parent, node, options):
+ array_opt = options.get(OPT_ARRAY)
+ if array_opt:
+ array_values = array_opt.all()
+ else:
+ array_values = {}
+
+ element_type = options.get(OPT_ELEMENT_TYPE)
+ if element_type is not None:
+ element_type_node = self._resolve(element_type.one())
+ elif isinstance(node.type, ast.Array):
+ element_type_node = node.type.element_type
+ else:
+ # We're assuming here that Foo* with an (array) annotation
+ # and no (element-type) means array of Foo
+ element_type_node = node.type.clone()
+ # Explicitly erase ctype since it's no longer valid
+ element_type_node.ctype = None
+
+ if isinstance(node.type, ast.Array):
+ array_type = node.type.array_type
+ else:
+ array_type = None
+ container_type = ast.Array(array_type, element_type_node,
+ ctype=node.type.ctype,
+ is_const=node.type.is_const)
+ if OPT_ARRAY_ZERO_TERMINATED in array_values:
+ container_type.zeroterminated = array_values.get(
+ OPT_ARRAY_ZERO_TERMINATED) == '1'
+ length = array_values.get(OPT_ARRAY_LENGTH)
+ if length is not None:
+ paramname = self._get_validate_parameter_name(parent, length, node)
+ if paramname:
+ param = parent.get_parameter(paramname)
+ param.direction = node.direction
+ if param.direction == ast.PARAM_DIRECTION_OUT:
+ param.transfer = ast.PARAM_TRANSFER_FULL
+ container_type.length_param_name = param.argname
+ fixed = array_values.get(OPT_ARRAY_FIXED_SIZE)
+ if fixed:
+ container_type.size = int(fixed)
+ node.type = container_type
+
+ def _apply_annotations_element_type(self, parent, node, options):
+ element_type_opt = options.get(OPT_ELEMENT_TYPE)
+ element_type = element_type_opt.flat()
+ if isinstance(node.type, ast.List):
+ assert len(element_type) == 1
+ node.type.element_type = self._resolve(element_type[0])
+ elif isinstance(node.type, ast.Map):
+ assert len(element_type) == 2
+ node.type.key_type = self._resolve(element_type[0])
+ node.type.value_type = self._resolve(element_type[1])
+ elif isinstance(node.type, ast.Array):
+ node.type.element_type = self._resolve(element_type[0])
+ else:
+ self._transformer.log_node_warning(parent,
+ "Unknown container %r for element-type annotation" % (node.type, ))
+
+ def _get_transfer_default_param(self, parent, node):
+ if node.direction in [ast.PARAM_DIRECTION_INOUT,
+ ast.PARAM_DIRECTION_OUT]:
+ if node.caller_allocates:
+ return ast.PARAM_TRANSFER_NONE
+ return ast.PARAM_TRANSFER_FULL
+ return ast.PARAM_TRANSFER_NONE
+
+ def _get_transfer_default_returntype_basic(self, typeval):
+ if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
+ or typeval.is_const
+ or typeval.is_equiv(ast.TYPE_NONE)):
+ return ast.PARAM_TRANSFER_NONE
+ elif typeval.is_equiv(ast.TYPE_STRING):
+ # Non-const strings default to FULL
+ return ast.PARAM_TRANSFER_FULL
+ elif typeval.target_fundamental:
+ # This looks like just GType right now
+ return None
+ return None
+
+ def _is_gi_subclass(self, typeval, supercls_type):
+ cls = self._transformer.lookup_typenode(typeval)
+ assert cls, str(typeval)
+ supercls = self._transformer.lookup_typenode(supercls_type)
+ assert supercls
+ if cls is supercls:
+ return True
+ if cls.parent:
+ return self._is_gi_subclass(cls.parent, supercls_type)
+ return False
+
+ def _get_transfer_default_return(self, parent, node):
+ typeval = node.type
+ basic = self._get_transfer_default_returntype_basic(typeval)
+ if basic:
+ return basic
+ if not typeval.target_giname:
+ return None
+ target = self._transformer.lookup_typenode(typeval)
+ if isinstance(target, ast.Alias):
+ return self._get_transfer_default_returntype_basic(target.target)
+ elif isinstance(target, glibast.GLibBoxed):
+ return ast.PARAM_TRANSFER_FULL
+ elif isinstance(target, (ast.Enum, ast.Bitfield)):
+ return ast.PARAM_TRANSFER_NONE
+ # Handle constructors specially here
+ elif isinstance(parent, ast.Function) and parent.is_constructor:
+ if isinstance(target, ast.Class):
+ initially_unowned_type = ast.Type(target_giname='GObject.InitiallyUnowned')
+ initially_unowned = self._transformer.lookup_typenode(initially_unowned_type)
+ if initially_unowned and self._is_gi_subclass(typeval, initially_unowned_type):
+ return ast.PARAM_TRANSFER_NONE
+ else:
+ return ast.PARAM_TRANSFER_FULL
+ elif isinstance(target, (ast.Record, ast.Union)):
+ return ast.PARAM_TRANSFER_FULL
+ else:
+ assert False, "Invalid constructor"
+ elif isinstance(target, (ast.Class, ast.Record, ast.Union)):
+ # Explicitly no default for these
+ return None
+ else:
+ return None
+
+ def _get_transfer_default(self, parent, node):
+ if node.type.is_equiv(ast.TYPE_NONE) or isinstance(node.type, ast.Varargs):
+ return ast.PARAM_TRANSFER_NONE
+ elif isinstance(node, ast.Parameter):
+ return self._get_transfer_default_param(parent, node)
+ elif isinstance(node, ast.Return):
+ return self._get_transfer_default_return(parent, node)
+ elif isinstance(node, ast.Field):
+ return ast.PARAM_TRANSFER_NONE
+ elif isinstance(node, ast.Property):
+ return ast.PARAM_TRANSFER_NONE
+ else:
+ raise AssertionError(node)
+
+ def _apply_annotations_param_ret_common(self, parent, node, tag):
+ options = getattr(tag, 'options', {})
+
+ param_type = options.get(OPT_TYPE)
+ if param_type:
+ node.type = self._resolve(param_type.one(), node.type)
+
+ caller_allocates = False
+ annotated_direction = None
+ if (OPT_INOUT in options or
+ OPT_INOUT_ALT in options):
+ annotated_direction = ast.PARAM_DIRECTION_INOUT
+ elif OPT_OUT in options:
+ subtype = options[OPT_OUT]
+ if subtype is not None:
+ subtype = subtype.one()
+ annotated_direction = ast.PARAM_DIRECTION_OUT
+ if subtype in (None, ''):
+ if node.type.target_giname and node.type.ctype:
+ caller_allocates = '**' not in node.type.ctype
+ else:
+ caller_allocates = False
+ elif subtype == 'caller-allocates':
+ caller_allocates = True
+ elif subtype == 'callee-allocates':
+ caller_allocates = False
+ else:
+ self._transformer.log_warning(
+"out allocation for %s is invalid (%r)" % (node, subtype), fatal=True)
+ elif OPT_IN in options:
+ annotated_direction = ast.PARAM_DIRECTION_IN
+
+ if (annotated_direction is not None) and (annotated_direction != node.direction):
+ node.direction = annotated_direction
+ node.caller_allocates = caller_allocates
+ # Also reset the transfer default if we're toggling direction
+ node.transfer = self._get_transfer_default(parent, node)
+
+ transfer_tag = options.get(TAG_TRANSFER)
+ if transfer_tag:
+ node.transfer = transfer_tag.one()
+
+ self._adjust_container_type(parent, node, options)
+
+ if (OPT_ALLOW_NONE in options or
+ node.type.target_giname == 'Gio.Cancellable'):
+ node.allow_none = True
+
+ if tag is not None and tag.comment is not None:
+ node.doc = tag.comment
+
+ for key in options:
+ if '.' in key:
+ value = options.get(key)
+ if value:
+ node.attributes.append((key, value.one()))
+
+ def _apply_annotations_annotated(self, node, block):
+ if block is None:
+ return
+
+ node.doc = block.comment
+
+ since_tag = block.get(TAG_SINCE)
+ if since_tag is not None:
+ node.version = since_tag.value
+
+ deprecated_tag = block.get(TAG_DEPRECATED)
+ if deprecated_tag is not None:
+ value = deprecated_tag.value
+ if ': ' in value:
+ version, desc = value.split(': ')
+ else:
+ desc = value
+ version = None
+ node.deprecated = desc
+ if version is not None:
+ node.deprecated_version = version
+
+ annos_tag = block.get(TAG_ATTRIBUTES)
+ if annos_tag is not None:
+ options = AnnotationParser.parse_options(annos_tag.value)
+ for key, value in options.iteritems():
+ if value:
+ node.attributes.append((key, value.one()))
+
+ if OPT_SKIP in block.options:
+ node.skip = True
+
+ if OPT_FOREIGN in block.options:
+ node.foreign = True
+
+ def _apply_annotations_param(self, parent, param, tag):
+ if tag:
+ options = tag.options
+ else:
+ options = {}
+ if isinstance(parent, ast.Function):
+ scope = options.get(OPT_SCOPE)
+ if scope:
+ scope = scope.one()
+ if scope not in [ast.PARAM_SCOPE_CALL,
+ ast.PARAM_SCOPE_ASYNC,
+ ast.PARAM_SCOPE_NOTIFIED]:
+ self._transformer.log_warning(parent,
+"Invalid scope %r for parameter %r" % (scope, param.name))
+ else:
+ param.scope = scope
+ param.transfer = ast.PARAM_TRANSFER_NONE
+
+ destroy = options.get(OPT_DESTROY)
+ if destroy:
+ param.destroy_name = self._get_validate_parameter_name(parent,
+ destroy.one(),
+ param)
+ if param.destroy_name is not None:
+ param.scope = ast.PARAM_SCOPE_NOTIFIED
+ destroy_param = parent.get_parameter(param.destroy_name)
+ # This is technically bogus; we're setting the scope on the destroy
+ # itself. But this helps avoid tripping a warning from finaltransformer,
+ # since we don't have a way right now to flag this callback a destroy.
+ destroy_param.scope = ast.PARAM_SCOPE_NOTIFIED
+ closure = options.get(OPT_CLOSURE)
+ if closure:
+ param.closure_name = self._get_validate_parameter_name(parent,
+ closure.one(),
+ param)
+ elif isinstance(parent, ast.Callback):
+ if OPT_CLOSURE in options:
+ # For callbacks, (closure) appears without an
+ # argument, and tags a parameter that is a closure. We
+ # represent it (weirdly) in the gir and typelib by
+ # setting param.closure_name to itself.
+ param.closure_name = param.argname
+
+ self._apply_annotations_param_ret_common(parent, param, tag)
+
+ def _apply_annotations_return(self, parent, return_, block):
+ if block:
+ tag = block.get(TAG_RETURNS)
+ else:
+ tag = None
+ self._apply_annotations_param_ret_common(parent, return_, tag)
+
+ def _apply_annotations_params(self, parent, params, block):
+ for param in params:
+ if block:
+ tag = block.get(param.argname)
+ else:
+ tag = None
+ self._apply_annotations_param(parent, param, tag)
+
+ def _apply_annotations_callable(self, node, chain, block):
+ self._apply_annotations_annotated(node, block)
+ self._apply_annotations_params(node, node.parameters, block)
+ self._apply_annotations_return(node, node.retval, block)
+
+ def _check_arg_annotations(self, parent, params, block):
+ if block is None:
+ return
+ for tag in block.tags.keys():
+ if tag == TAG_RETURNS:
+ continue
+ for param in params:
+ if param.argname == tag:
+ break
+ else:
+ self._transformer.log_warning(
+"Annotation for '%s' refers to unknown argument '%s'"
+% (parent.name, tag))
+
+ def _apply_annotations_field(self, parent, block, field):
+ if not block:
+ return
+ tag = block.get(field.name)
+ if not tag:
+ return
+ t = tag.options.get('type')
+ if not t:
+ return
+ field.type = self._transformer.create_type_from_user_string(t.one())
+
+ def _apply_annotations_property(self, parent, prop):
+ block = self._blocks.get('%s:%s' % (parent.type_name, prop.name))
+ self._apply_annotations_annotated(prop, block)
+ if not block:
+ return
+ transfer_tag = block.get(TAG_TRANSFER)
+ if transfer_tag is not None:
+ prop.transfer = transfer_tag.value
+ else:
+ prop.transfer = self._get_transfer_default(parent, prop)
+ type_tag = block.get(TAG_TYPE)
+ if type_tag:
+ prop.type = self._resolve(type_tag.value, prop.type)
+
+ def _apply_annotations_signal(self, parent, signal):
+ block = self._blocks.get('%s::%s' % (parent.type_name, signal.name))
+ self._apply_annotations_annotated(signal, block)
+ # We're only attempting to name the signal parameters if
+ # the number of parameter tags (@foo) is the same or greater
+ # than the number of signal parameters
+ if block and len(block.tags) > len(signal.parameters):
+ names = block.tags.items()
+ else:
+ names = []
+ for i, param in enumerate(signal.parameters):
+ if names:
+ name, tag = names[i+1]
+ param.name = name
+ options = getattr(tag, 'options', {})
+ param_type = options.get(OPT_TYPE)
+ if param_type:
+ param.type = self._resolve(param_type.one(), param.type)
+ else:
+ tag = None
+ self._apply_annotations_param(signal, param, tag)
+ self._apply_annotations_return(signal, signal.retval, block)
+
+ def _pass_read_annotations2(self, node, chain):
+ if isinstance(node, ast.Function):
+ self._apply_annotations2_function(node, chain)
+ return True
+
+ def _apply_annotations2_function(self, node, chain):
+ # Handle virtual invokers
+ parent = chain[-1] if chain else None
+ block = self._blocks.get(node.symbol)
+ if not (block and parent):
+ return
+ virtual = block.get(TAG_VFUNC)
+ if not virtual:
+ return
+ invoker_name = virtual.value
+ matched = False
+ for vfunc in parent.virtual_methods:
+ if vfunc.name == invoker_name:
+ matched = True
+ vfunc.invoker = node.name
+ # Also merge in annotations
+ self._apply_annotations_callable(vfunc, [parent], block)
+ break
+ if not matched:
+ self._transformer.log_symbol_warning(node.symbol,
+ "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
+
+ def _pass_type_resolution(self, node, chain):
+ if isinstance(node, ast.Alias):
+ self._transformer.resolve_type(node.target)
+ if isinstance(node, ast.Callable):
+ for parameter in node.parameters:
+ self._transformer.resolve_type(parameter.type)
+ self._transformer.resolve_type(node.retval.type)
+ if isinstance(node, ast.Constant):
+ self._transformer.resolve_type(node.value_type)
+ if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in node.fields:
+ if field.anonymous_node:
+ pass
+ else:
+ self._transformer.resolve_type(field.type)
+ if isinstance(node, (ast.Class, ast.Interface)):
+ resolved_parent = None
+ for parent in node.parent_chain:
+ try:
+ self._transformer.resolve_type(parent)
+ except ValueError, e:
+ continue
+ target = self._transformer.lookup_typenode(parent)
+ if target:
+ node.parent = parent
+ break
+ for prop in node.properties:
+ self._transformer.resolve_type(prop.type)
+ for sig in node.signals:
+ for param in sig.parameters:
+ self._transformer.resolve_type(param.type)
+ if isinstance(node, ast.Class):
+ for iface in node.interfaces:
+ self._transformer.resolve_type(iface)
+ if isinstance(node, ast.Interface):
+ for iface in node.prerequisites:
+ self._transformer.resolve_type(iface)
+ return True
+
+ def _resolve_quarks(self):
+ # self._uscore_type_names is an authoritative mapping of types
+ # to underscored versions, since it is based on get_type() methods;
+ # but only covers enums that are registered as GObject enums.
+ # Create a fallback mapping based on all known enums in this module.
+ uscore_enums = {}
+ for enum in self._namespace.itervalues():
+ if not isinstance(enum, ast.Enum):
+ continue
+ type_name = enum.symbol
+ uscored = to_underscores(type_name).lower()
+
+ uscore_enums[uscored] = enum
+
+ no_uscore_prefixed = self._transformer.strip_identifier_or_warn(type_name)
+ if no_uscore_prefixed not in uscore_enums:
+ uscore_enums[no_uscore_prefixed] = enum
+
+ for node in self._namespace.itervalues():
+ if not isinstance(node, ast.Function):
+ continue
+ if node.retval.type.target_giname != 'GLib.Quark':
+ continue
+ short = node.symbol[:-len('_quark')]
+ if short == "g_io_error":
+ # Special case; GIOError was already taken forcing GIOErrorEnum
+ assert self._namespace.name == 'Gio'
+ enum = self._namespace.get('IOErrorEnum')
+ else:
+ enum = self._uscore_type_names.get(short)
+ if enum is None:
+ enum = uscore_enums.get(short)
+ if enum is not None:
+ enum.error_quark = node.symbol
+ else:
+ self._transformer.log_node_warning(node,
+"""%s: Couldn't find corresponding enumeration""" % (node.symbol, ))
+
+ def _split_uscored_by_type(self, uscored):
+ """'uscored' should be an un-prefixed uscore string. This
+function searches through the namespace for the longest type which
+prefixes uscored, and returns (type, suffix). Example, assuming
+namespace Gtk, type is TextBuffer:
+
+_split_uscored_by_type(text_buffer_try_new) -> (ast.Class(TextBuffer), 'try_new')"""
+ node = None
+ count = 0
+ prev_split_count = -1
+ while True:
+ components = uscored.rsplit('_', count)
+ if len(components) == prev_split_count:
+ return None
+ prev_split_count = len(components)
+ type_string = components[0]
+ node = self._uscore_type_names.get(type_string)
+ if node:
+ return (node, '_'.join(components[1:]))
+ count += 1
+
+ def _pair_function(self, func):
+ """Check to see whether a toplevel function should be a
+method or constructor of some type."""
+ if func.symbol.endswith('_get_type') or func.symbol.startswith('_'):
+ return
+ (ns, subsymbol) = self._transformer.split_csymbol(func.symbol)
+ assert ns == self._namespace
+ if self._pair_constructor(func, subsymbol):
+ return
+ elif self._pair_method(func, subsymbol):
+ return
+ elif self._pair_static_method(func, subsymbol):
+ return
+
+ def _uscored_identifier_for_type(self, typeval):
+ """Given a Type(target_giname='Foo.BarBaz'), return 'bar_baz'."""
+ name = typeval.get_giname()
+ return to_underscores_noprefix(name).lower()
+
+ def _pair_method(self, func, subsymbol):
+ if not func.parameters:
+ return False
+ first = func.parameters[0]
+ target = self._transformer.lookup_typenode(first.type)
+ if not isinstance(target, (ast.Class, ast.Interface,
+ ast.Record, ast.Union,
+ glibast.GLibBoxedOther)):
+ return False
+
+ # A quick hack here...in the future we should catch C signature/GI signature
+ # mismatches in a general way in finaltransformer
+ if first.type.ctype.count('*') != 1:
+ return False
+
+ uscored = self._uscored_identifier_for_type(first.type)
+ if not subsymbol.startswith(uscored):
+ return False
+ del func.parameters[0]
+ subsym_idx = func.symbol.find(subsymbol)
+ self._namespace.float(func)
+ func.name = func.symbol[(subsym_idx + len(uscored) + 1):]
+ target.methods.append(func)
+ func.is_method = True
+ return True
+
+ def _pair_static_method(self, func, subsymbol):
+ split = self._split_uscored_by_type(subsymbol)
+ if split is None:
+ return False
+ (node, funcname) = split
+ if not isinstance(node, (ast.Class, ast.Interface,
+ ast.Record, ast.Union, glibast.GLibBoxedOther)):
+ return False
+ self._namespace.float(func)
+ func.name = funcname
+ node.static_methods.append(func)
+
+ def _pair_constructor(self, func, subsymbol):
+ if not (func.symbol.find('_new_') >= 0 or func.symbol.endswith('_new')):
+ return False
+ target = self._transformer.lookup_typenode(func.retval.type)
+ if not isinstance(target, (ast.Class, ast.Record, ast.Union, glibast.GLibBoxedOther)):
+ return False
+ new_idx = func.symbol.rfind('_new')
+ assert (new_idx >= 0)
+ prefix = func.symbol[:new_idx]
+ split = self._split_uscored_by_type(subsymbol)
+ if split is None:
+ # TODO - need a e.g. (method) annotation
+ self._transformer.log_node_warning(func,
+ "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
+ return False
+ (origin_node, funcname) = split
+ if isinstance(target, ast.Class):
+ parent = origin_node
+ while parent:
+ if parent == target:
+ break
+ if parent.parent:
+ parent = self._transformer.lookup_typenode(parent.parent)
+ else:
+ parent = None
+ if parent is None:
+ self._transformer.log_node_warning(func,
+"Return value is not superclass for constructor; symbol=%r constructed=%r return=%r"
+% (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+ return False
+ else:
+ if origin_node != target:
+ self._transformer.log_node_warning(func,
+"Constructor return type mismatch symbol=%r constructed=%r return=%r"
+% (func.symbol, str(origin_node.create_type()), str(func.retval.type)))
+ return False
+ self._namespace.float(func)
+ func.name = funcname
+ func.is_constructor = True
+ target.constructors.append(func)
+ # Constructors have default return semantics
+ if not func.retval.transfer:
+ func.retval.transfer = self._get_transfer_default_return(func, func.retval)
+ return True
+
+ def _pair_class_virtuals(self, node):
+ """Look for virtual methods from the class structure."""
+ if not node.glib_type_struct:
+ self._transformer.log_node_warning(node,
+ "Failed to find class structure for %r" % (node.name, ))
+ return
+
+ node_type = node.create_type()
+ class_struct = self._transformer.lookup_typenode(node.glib_type_struct)
+
+ # Object class fields are assumed to be read-only
+ # (see also _introspect_object and transformer.py)
+ for field in class_struct.fields:
+ if isinstance(field, ast.Field):
+ field.writable = False
+
+ # Loop through fields to determine which are virtual
+ # functions and which are signal slots by
+ # assuming everything that doesn't share a name
+ # with a known signal is a virtual slot.
+ for field in class_struct.fields:
+ if not isinstance(field.anonymous_node, ast.Callback):
+ continue
+ callback = field.anonymous_node
+ # Check the first parameter is the object
+ if len(callback.parameters) == 0:
+ continue
+ firstparam_type = callback.parameters[0].type
+ if firstparam_type != node_type:
+ continue
+ # Also double check we don't have a signal with this
+ # name.
+ matched_signal = False
+ for signal in node.signals:
+ if signal.name.replace('-', '_') == callback.name:
+ matched_signal = True
+ break
+ if matched_signal:
+ continue
+ vfunc = ast.VFunction.from_callback(callback)
+ vfunc.inherit_file_positions(callback)
+ node.virtual_methods.append(vfunc)
+
+ # Take the set of virtual methods we found, and try
+ # to pair up with any matching methods using the
+ # name+signature.
+ for vfunc in node.virtual_methods:
+ for method in node.methods:
+ if method.name != vfunc.name:
+ continue
+ if method.retval.type != vfunc.retval.type:
+ continue
+ if len(method.parameters) != len(vfunc.parameters):
+ continue
+ for i in xrange(len(method.parameters)):
+ m_type = method.parameters[i].type
+ v_type = vfunc.parameters[i].type
+ if m_type != v_type:
+ continue
+ vfunc.invoker = method.name
+ # Apply any annotations we have from the invoker to
+ # the vfunc
+ block = self._blocks.get(method.symbol)
+ self._apply_annotations_callable(vfunc, [], block)
+
+ def _pass3(self, node, chain):
+ """Pass 3 is after we've loaded GType data and performed type
+ closure."""
+ if isinstance(node, ast.Callable):
+ self._pass3_callable_callbacks(node)
+ self._pass3_callable_throws(node)
+ return True
+
+ def _pass3_callable_callbacks(self, node):
+ """Check to see if we have anything that looks like a
+ callback+user_data+GDestroyNotify set."""
+
+ params = node.parameters
+
+ # First, do defaults for well-known callback types
+ for i, param in enumerate(params):
+ argnode = self._transformer.lookup_typenode(param.type)
+ if isinstance(argnode, ast.Callback):
+ if param.type.target_giname in ('Gio.AsyncReadyCallback',
+ 'GLib.DestroyNotify'):
+ param.scope = ast.PARAM_SCOPE_ASYNC
+ param.transfer = ast.PARAM_TRANSFER_NONE
+
+ callback_param = None
+ for i, param in enumerate(params):
+ argnode = self._transformer.lookup_typenode(param.type)
+ is_destroynotify = False
+ if isinstance(argnode, ast.Callback):
+ if param.type.target_giname == 'GLib.DestroyNotify':
+ is_destroynotify = True
+ else:
+ callback_param = param
+ continue
+ if callback_param is None:
+ continue
+ if is_destroynotify:
+ callback_param.destroy_name = param.argname
+ callback_param.scope = ast.PARAM_SCOPE_NOTIFIED
+ callback_param.transfer = ast.PARAM_TRANSFER_NONE
+ elif (param.type.is_equiv(ast.TYPE_ANY) and
+ param.argname.endswith('data')):
+ callback_param.closure_name = param.argname
+
+ def _pass3_callable_throws(self, node):
+ """Check to see if we have anything that looks like a
+ callback+user_data+GDestroyNotify set."""
+ if not node.parameters:
+ return
+ last_param = node.parameters[-1]
+ # Checking type.name=='GLib.Error' generates false positives
+ # on methods that take a 'GError *'
+ if last_param.type.ctype == 'GError**':
+ node.parameters.pop()
+ node.throws = True
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 36942826..ec2302ec 100644
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -21,19 +21,25 @@
#
import subprocess
+import tempfile
import optparse
import os
import sys
-from giscanner.annotationparser import AnnotationParser, InvalidAnnotationError
+from giscanner.annotationparser import AnnotationParser
from giscanner.ast import Include
from giscanner.cachestore import CacheStore
from giscanner.dumper import compile_introspection_binary
-from giscanner.glibtransformer import GLibTransformer, IntrospectionBinary
-from giscanner.minixpath import myxpath, xpath_assert
+from giscanner.gdumpparser import GDumpParser, IntrospectionBinary
+from giscanner.minixpath import xpath_assert
from giscanner.sourcescanner import SourceScanner
from giscanner.shlibs import resolve_shlibs
from giscanner.transformer import Transformer
+from giscanner.maintransformer import MainTransformer
+from giscanner.introspectablepass import IntrospectablePass
+from giscanner.girparser import GIRParser
+from giscanner.girwriter import GIRWriter
+from giscanner.utils import files_are_identical
def _get_option_parser():
parser = optparse.OptionParser('%prog [options] sources')
@@ -49,6 +55,15 @@ def _get_option_parser():
parser.add_option("-i", "--include",
action="append", dest="includes", default=[],
help="include types for other gidls")
+ parser.add_option('', "--generate-typelib-tests",
+ action="store", dest="test_codegen", default=None,
+ help="Generate test code for given namespace,output.h,output.c")
+ parser.add_option('', "--passthrough-gir",
+ action="store", dest="passthrough_gir", default=None,
+ help="Parse and re-output the specified GIR")
+ parser.add_option('', "--reparse-validate",
+ action="store_true", dest="reparse_validate_gir", default=False,
+ help="After generating the GIR, re-parse it to ensure validity")
parser.add_option("", "--add-include-path",
action="append", dest="include_paths", default=[],
help="include paths for other GIR files")
@@ -73,13 +88,18 @@ def _get_option_parser():
parser.add_option("-n", "--namespace",
action="store", dest="namespace_name",
help=("name of namespace for this unit, also "
- "used as --strip-prefix default"))
+ "used to compute --identifier-prefix and --symbol-prefix"))
parser.add_option("", "--nsversion",
action="store", dest="namespace_version",
help="version of namespace for this unit")
- parser.add_option("", "--strip-prefix",
- action="store", dest="strip_prefix", default=None,
- help="remove this prefix from objects and functions")
+ parser.add_option("", "--identifier-prefix",
+ action="append", dest="identifier_prefixes", default=[],
+ help="""Remove this prefix from C identifiers (structure typedefs, etc.).
+May be specified multiple times. This is also used as the default for --symbol-prefix if
+the latter is not specified.""")
+ parser.add_option("", "--symbol-prefix",
+ action="append", dest="symbol_prefixes", default=[],
+ help="Remove this prefix from C symbols (function names)")
parser.add_option("", "--add-init-section",
action="append", dest="init_sections", default=[],
help="add extra initialization code in the introspection program")
@@ -101,15 +121,9 @@ def _get_option_parser():
parser.add_option("-v", "--verbose",
action="store_true", dest="verbose",
help="be verbose")
- parser.add_option("", "--noclosure",
- action="store_true", dest="noclosure",
- help="do not delete unknown types")
parser.add_option("", "--typelib-xml",
action="store_true", dest="typelib_xml",
help="Just convert GIR to typelib XML")
- parser.add_option("", "--inject",
- action="store_true", dest="inject",
- help="Inject additional components into GIR XML")
parser.add_option("", "--xpath-assertions",
action="store", dest="xpath_assertions",
help="Use given file to create assertions on GIR content")
@@ -136,53 +150,25 @@ def _get_option_parser():
def _error(msg):
raise SystemExit('ERROR: %s' % (msg, ))
-def typelib_xml_strip(path):
- from giscanner.girparser import GIRParser
- from giscanner.girwriter import GIRWriter
- from giscanner.girparser import C_NS
- from xml.etree.cElementTree import parse
-
- c_ns_key = '{%s}' % (C_NS, )
-
- tree = parse(path)
- root = tree.getroot()
- for node in root.getiterator():
- for attrib in list(node.attrib):
- if attrib.startswith(c_ns_key):
- del node.attrib[attrib]
+def passthrough_gir(path, f):
parser = GIRParser()
- parser.parse_tree(tree)
+ parser.parse(path)
writer = GIRWriter(parser.get_namespace(),
parser.get_shared_libraries(),
- parser.get_includes())
- sys.stdout.write(writer.get_xml())
- return 0
-
-def inject(path, additions, outpath):
- from giscanner.girparser import GIRParser
- from giscanner.girwriter import GIRWriter
- from xml.etree.cElementTree import parse
-
- tree = parse(path)
- root = tree.getroot()
- injectDoc = parse(open(additions))
- for node in injectDoc.getroot():
- injectPath = node.attrib['path']
- target = myxpath(root, injectPath)
- if not target:
- raise ValueError("Couldn't find path %r" % (injectPath, ))
- for child in node:
- target.append(child)
-
- parser = GIRParser()
- parser.parse_tree(tree)
- writer = GIRWriter(parser.get_namespace(),
- parser.get_shared_libraries(),
- parser.get_includes())
- outf = open(outpath, 'w')
- outf.write(writer.get_xml())
- outf.close()
+ parser.get_includes(),
+ parser.get_pkgconfig_packages(),
+ parser.get_c_includes())
+ f.write(writer.get_xml())
+
+def test_codegen(optstring):
+ (namespace, out_h_filename, out_c_filename) = optstring.split(',')
+ if namespace == 'Everything':
+ from .testcodegen import EverythingCodeGenerator
+ gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
+ gen.write()
+ else:
+ raise ValueError("Invaild namespace %r" % (namespace, ))
return 0
def validate(assertions, path):
@@ -237,18 +223,14 @@ def scanner_main(args):
parser = _get_option_parser()
(options, args) = parser.parse_args(args)
+ if options.passthrough_gir:
+ passthrough_gir(options.passthrough_gir, sys.stdout)
+ if options.test_codegen:
+ return test_codegen(options.test_codegen)
+
if len(args) <= 1:
_error('Need at least one filename')
- if options.typelib_xml:
- return typelib_xml_strip(args[1])
-
- if options.inject:
- if len(args) != 4:
- _error('Need three filenames; e.g. g-ir-scanner '
- '--inject Source.gir Additions.xml SourceOut.gir')
- return inject(*args[1:4])
-
if options.xpath_assertions:
return validate(options.xpath_assertions, args[1])
@@ -281,11 +263,9 @@ def scanner_main(args):
cachestore = CacheStore()
transformer = Transformer(cachestore,
options.namespace_name,
- options.namespace_version)
- if options.strip_prefix:
- transformer.set_strip_prefix(options.strip_prefix)
- else:
- transformer.set_strip_prefix(options.namespace_name)
+ options.namespace_version,
+ options.identifier_prefixes,
+ options.symbol_prefixes)
if options.warn_all:
transformer.enable_warnings(True)
transformer.set_include_paths(options.include_paths)
@@ -318,12 +298,11 @@ def scanner_main(args):
# Transform the C AST nodes into higher level
# GLib/GObject nodes
- glibtransformer = GLibTransformer(transformer,
- noclosure=options.noclosure)
+ gdump_parser = GDumpParser(transformer)
# Do enough parsing that we have the get_type() functions to reference
# when creating the introspection binary
- glibtransformer.init_parse()
+ gdump_parser.init_parse()
if options.program:
args=[options.program]
@@ -331,38 +310,50 @@ def scanner_main(args):
binary = IntrospectionBinary(args)
else:
binary = compile_introspection_binary(options,
- glibtransformer.get_get_type_functions())
+ gdump_parser.get_get_type_functions())
shlibs = resolve_shlibs(options, binary, libraries)
- glibtransformer.set_introspection_binary(binary)
+ gdump_parser.set_introspection_binary(binary)
+ gdump_parser.parse()
- namespace = glibtransformer.parse()
+ ap = AnnotationParser(ss)
+ blocks = ap.parse()
- ap = AnnotationParser(namespace, ss, transformer)
- try:
- ap.parse()
- except InvalidAnnotationError, e:
- raise SystemExit("ERROR in annotation: %s" % (str(e), ))
+ main = MainTransformer(transformer, blocks)
+ main.transform()
- glibtransformer.final_analyze()
+ final = IntrospectablePass(transformer)
+ final.validate()
if options.warn_fatal and transformer.did_warn():
- return 1
+ transformer.log_warning("warnings configured as fatal", fatal=True)
+ # Redundant sys.exit here, just in case
+ sys.exit(1)
# Write out AST
if options.packages_export:
exported_packages = options.packages_export
else:
exported_packages = options.packages
- writer = Writer(namespace, shlibs, transformer.get_includes(),
- exported_packages, options.c_includes,
- transformer.get_strip_prefix())
+ writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
+ exported_packages, options.c_includes)
data = writer.get_xml()
if options.output:
- fd = open(options.output, "w")
- fd.write(data)
+ tempdir = os.path.dirname(options.output) or os.getcwd()
+ main_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
+ main_f.write(data)
+ main_f.close()
+ if options.reparse_validate_gir:
+ temp_f = tempfile.NamedTemporaryFile(suffix='.gir', dir=tempdir, delete=False)
+ passthrough_gir(main_f.name, temp_f)
+ temp_f.close()
+ if not files_are_identical(main_f.name, temp_f.name):
+ raise SystemExit(
+"Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_f.name, temp_f.name))
+ os.unlink(temp_f.name)
+ os.rename(main_f.name, options.output)
else:
- print data
+ sys.stdout.write(data)
return 0
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index cd7a4321..a2db2e74 100644
--- a/giscanner/sourcescanner.py
+++ b/giscanner/sourcescanner.py
@@ -154,10 +154,16 @@ class SourceSymbol(object):
self._symbol = symbol
def __repr__(self):
- return '<%s type=%r ident=%r>' % (
+ src = self.source_filename
+ if src:
+ line = self.line
+ if line:
+ src += ':%r' % (line, )
+ return '<%s type=%r ident=%r src=%r>' % (
self.__class__.__name__,
symbol_type_name(self.type),
- self.ident)
+ self.ident,
+ src)
@property
def const_int(self):
diff --git a/giscanner/testcodegen.py b/giscanner/testcodegen.py
new file mode 100644
index 00000000..f304dc7a
--- /dev/null
+++ b/giscanner/testcodegen.py
@@ -0,0 +1,119 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2010 Red Hat, Inc.
+#
+# 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.
+#
+
+from StringIO import StringIO
+from . import ast
+from .codegen import CCodeGenerator
+
+DEFAULT_C_VALUES = {ast.TYPE_ANY: 'NULL',
+ ast.TYPE_STRING: '""',
+ ast.TYPE_FILENAME: '""',
+ ast.TYPE_GTYPE: 'g_object_get_type ()'}
+
+def get_default_for_typeval(typeval):
+ default = DEFAULT_C_VALUES.get(typeval)
+ if default:
+ return default
+ return "0"
+
+def uscore_from_type(typeval):
+ if typeval.target_fundamental:
+ return typeval.target_fundamental.replace(' ', '_')
+ elif typeval.target_giname:
+ return typeval.target_giname.replace('.', '').lower()
+ else:
+ assert False, typeval
+
+class EverythingCodeGenerator(object):
+
+ def __init__(self, out_h_filename, out_c_filename):
+ self.namespace = ast.Namespace('Everything', '1.0')
+ self.gen = CCodeGenerator(self.namespace, out_h_filename, out_c_filename)
+
+ def write(self):
+ func = ast.Function('nullfunc',
+ ast.Return(ast.TYPE_NONE, transfer=ast.PARAM_TRANSFER_NONE),
+ [], False, self.gen.gen_symbol('nullfunc'))
+ self.namespace.append(func)
+ body = " return;\n"
+ self.gen.set_function_body(func, body)
+
+ # First pass, generate constant returns
+ prefix = 'const return '
+ for typeval in ast.INTROSPECTABLE_BASIC:
+ name = prefix + uscore_from_type(typeval)
+ sym = self.gen.gen_symbol(name)
+ func = ast.Function(name,
+ ast.Return(typeval, transfer=ast.PARAM_TRANSFER_NONE),
+ [], False, sym)
+ self.namespace.append(func)
+ default = get_default_for_typeval(typeval)
+ body = " return %s;\n" % (default, )
+ self.gen.set_function_body(func, body)
+
+ # Void return, one parameter
+ prefix = 'oneparam '
+ for typeval in ast.INTROSPECTABLE_BASIC:
+ if typeval is ast.TYPE_NONE:
+ continue
+ name = prefix + uscore_from_type(typeval)
+ sym = self.gen.gen_symbol(name)
+ func = ast.Function(name,
+ ast.Return(ast.TYPE_NONE, transfer=ast.PARAM_TRANSFER_NONE),
+ [ast.Parameter('arg0', typeval, transfer=ast.PARAM_TRANSFER_NONE,
+ direction=ast.PARAM_DIRECTION_IN)], False, sym)
+ self.namespace.append(func)
+ self.gen.set_function_body(func, " return;\n")
+
+ # Void return, one (out) parameter
+ prefix = 'one_outparam '
+ for typeval in ast.INTROSPECTABLE_BASIC:
+ if typeval is ast.TYPE_NONE:
+ continue
+ name = prefix + uscore_from_type(typeval)
+ sym = self.gen.gen_symbol(name)
+ func = ast.Function(name,
+ ast.Return(ast.TYPE_NONE, transfer=ast.PARAM_TRANSFER_NONE),
+ [ast.Parameter('arg0', typeval, transfer=ast.PARAM_TRANSFER_NONE,
+ direction=ast.PARAM_DIRECTION_OUT)], False, sym)
+ self.namespace.append(func)
+ body = StringIO('w')
+ default = get_default_for_typeval(func.retval)
+ body.write(" *arg0 = %s;\n" % (default, ))
+ body.write(" return;\n")
+ self.gen.set_function_body(func, body.getvalue())
+
+ # Passthrough one parameter
+ prefix = 'passthrough_one '
+ for typeval in ast.INTROSPECTABLE_BASIC:
+ if typeval is ast.TYPE_NONE:
+ continue
+ name = prefix + uscore_from_type(typeval)
+ sym = self.gen.gen_symbol(name)
+ func = ast.Function(name, ast.Return(typeval, transfer=ast.PARAM_TRANSFER_NONE),
+ [ast.Parameter('arg0', typeval, transfer=ast.PARAM_TRANSFER_NONE,
+ direction=ast.PARAM_DIRECTION_IN)], False, sym)
+ self.namespace.append(func)
+ body = StringIO('w')
+ default = get_default_for_typeval(func.retval)
+ body.write(" return arg0;\n")
+ self.gen.set_function_body(func, body.getvalue())
+
+ self.gen.codegen()
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index e2a22049..f6639a61 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -20,17 +20,11 @@
import os
import sys
+import re
-from .ast import (Bitfield, Callback, Enum, Function, Namespace, Member,
- Parameter, Return, Struct, Field,
- Type, Array, Alias, Interface, Class, Node, Union,
- Varargs, Constant, type_name_from_ctype,
- type_names, TYPE_ANY, TYPE_STRING,
- BASIC_GIR_TYPES)
+from . import ast
from .config import DATADIR, GIR_DIR, GIR_SUFFIX
-from .glibast import GLibBoxed
from .girparser import GIRParser
-from .odict import odict
from .sourcescanner import (
SourceSymbol, ctype_name, CTYPE_POINTER,
CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
@@ -39,57 +33,36 @@ from .sourcescanner import (
CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
TYPE_QUALIFIER_CONST)
-from .utils import to_underscores
-_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
- + [DATADIR, '/usr/share'] if x]
-
-
-class SkipError(Exception):
+class TypeResolutionException(Exception):
pass
-
-class Names(object):
- names = property(lambda self: self._names)
- aliases = property(lambda self: self._aliases)
- type_names = property(lambda self: self._type_names)
- ctypes = property(lambda self: self._ctypes)
-
- def __init__(self):
- super(Names, self).__init__()
- self._names = odict() # Maps from GIName -> (namespace, node)
- self._aliases = {} # Maps from GIName -> GIName
- self._type_names = {} # Maps from GTName -> (namespace, node)
- self._ctypes = {} # Maps from CType -> (namespace, node)
-
+_xdg_data_dirs = [x for x in os.environ.get('XDG_DATA_DIRS', '').split(':') \
+ + [DATADIR, '/usr/share'] if x]
class Transformer(object):
+ namespace = property(lambda self: self._namespace)
- def __init__(self, cachestore, namespace_name, namespace_version):
+ UCASE_CONSTANT_RE = re.compile(r'[_A-Z0-9]+')
+
+ def __init__(self, cachestore, namespace_name, namespace_version,
+ identifier_prefixes=None, symbol_prefixes=None):
self._cwd = os.getcwd() + os.sep
self._cachestore = cachestore
self.generator = None
- self._namespace = Namespace(namespace_name, namespace_version)
- self._names = Names()
+ self._namespace = ast.Namespace(namespace_name, namespace_version,
+ identifier_prefixes=identifier_prefixes,
+ symbol_prefixes=symbol_prefixes)
self._pkg_config_packages = set()
self._typedefs_ns = {}
- self._strip_prefix = ''
self._enable_warnings = False
self._warned = False
- self._includes = set()
+ self._includes = {}
+ self._include_names = set()
self._includepaths = []
- def get_names(self):
- return self._names
-
def get_includes(self):
- return self._includes
-
- def set_strip_prefix(self, strip_prefix):
- self._strip_prefix = strip_prefix
-
- def get_strip_prefix(self):
- return self._strip_prefix
+ return self._include_names
def enable_warnings(self, enable):
self._enable_warnings = enable
@@ -103,39 +76,97 @@ class Transformer(object):
def set_source_ast(self, src_ast):
self.generator = src_ast
+ def _append_new_node(self, node):
+ original = self._namespace.get(node.name)
+ # Special case constants here; we allow duplication to sort-of
+ # handle #ifdef. But this introduces an arch-dependency in the .gir
+ # file. So far this has only come up scanning glib - in theory, other
+ # modules will just depend on that.
+ if isinstance(original, ast.Constant) and isinstance(node, ast.Constant):
+ pass
+ elif original:
+ positions = set()
+ positions.update(original.file_positions)
+ positions.update(node.file_positions)
+ self.log_warning("Namespace conflict for '%s'" % (node.name, ),
+ positions, fatal=True)
+ else:
+ self._namespace.append(node)
+
def parse(self):
- nodes = []
for symbol in self.generator.get_symbols():
- try:
- node = self._traverse_one(symbol)
- except SkipError:
- continue
- self._add_node(node)
- return self._namespace
+ node = self._traverse_one(symbol)
+ if node:
+ self._append_new_node(node)
+
+ # Now look through the namespace for things like
+ # typedef struct _Foo Foo;
+ # where we've never seen the struct _Foo. Just create
+ # an empty structure for these as "disguised"
+ # If we do have a class/interface, merge fields
+ for typedef, compound in self._typedefs_ns.iteritems():
+ ns_compound = self._namespace.get(compound.name)
+ if not ns_compound:
+ ns_compound = self._namespace.get('_' + compound.name)
+ if (not ns_compound and isinstance(compound, (ast.Record, ast.Union))
+ and len(compound.fields) == 0):
+ disguised = ast.Record(compound.name, typedef, disguised=True)
+ self._namespace.append(disguised)
+ elif not ns_compound:
+ self._namespace.append(compound)
+ elif isinstance(ns_compound, (ast.Record, ast.Union)) and len(ns_compound.fields) == 0:
+ ns_compound.fields = compound.fields
+ self._typedefs_ns = None
def set_include_paths(self, paths):
self._includepaths = list(paths)
def register_include(self, include):
- if include in self._includes:
+ if include in self._include_names:
return
filename = self._find_include(include)
self._parse_include(filename)
- self._includes.add(include)
+ self._include_names.add(include)
+
+ def lookup_giname(self, name):
+ """Given a name of the form Foo or Bar.Foo,
+return the corresponding ast.Node, or None if none
+available. Will throw KeyError however for unknown
+namespaces."""
+ if '.' not in name:
+ return self._namespace.get(name)
+ else:
+ (ns, name) = name.split('.', 1)
+ if ns == self._namespace.name:
+ return self._namespace.get(name)
+ include = self._includes[ns]
+ return include.get(name)
+
+ def lookup_typenode(self, typeobj):
+ """Given a Type object, if it points to a giname,
+calls lookup_giname() on the name. Otherwise return
+None."""
+ if typeobj.target_giname:
+ return self.lookup_giname(typeobj.target_giname)
+ return None
# Private
- def log_warning(self, text, file_positions=None, prefix=None):
+ def log_warning(self, text, file_positions=None, prefix=None,
+ fatal=False):
"""Log a warning, using optional file positioning information.
-If the warning is related to a Node type, see log_node_warning()."""
- if not self._enable_warnings:
+If the warning is related to a ast.Node type, see log_node_warning()."""
+ if not fatal and not self._enable_warnings:
return
+ self._warned = True
+
if file_positions is None or len(file_positions) == 0:
target_file_positions = [('<unknown>', -1, -1)]
else:
target_file_positions = file_positions
+ position_strings = []
for (filename, line, column) in target_file_positions:
if filename.startswith(self._cwd):
filename = filename[len(self._cwd):]
@@ -145,41 +176,58 @@ If the warning is related to a Node type, see log_node_warning()."""
position = '%s:%d' % (filename, line, )
else:
position = '%s:' % (filename, )
+ position_strings.append(position)
+ for position in position_strings[:-1]:
+ print >>sys.stderr, "%s:" % (position, )
+ last_position = position_strings[-1]
+ error_type = 'error' if fatal else 'warning'
if prefix:
print >>sys.stderr, \
-'''%s: warning: %s: %s: %s''' % (position, self._namespace.name,
+'''%s: %s: %s: %s: %s''' % (last_position, error_type, self._namespace.name,
prefix, text)
else:
print >>sys.stderr, \
-'''%s: warning: %s: %s''' % (position, self._namespace.name, text)
+'''%s: %s: %s: %s''' % (last_position, error_type, self._namespace.name, text)
+ if fatal:
+ sys.exit(1)
- def log_symbol_warning(self, symbol, text):
+ def log_symbol_warning(self, symbol, text, **kwargs):
"""Log a warning in the context of the given symbol."""
- file_positions = [(symbol.source_filename, symbol.line, -1)]
+ if symbol.source_filename:
+ file_positions = [(symbol.source_filename, symbol.line, -1)]
+ else:
+ file_positions = None
prefix = "symbol=%r" % (symbol.ident, )
- self.log_warning(text, file_positions, prefix=prefix)
+ self.log_warning(text, file_positions, prefix=prefix, **kwargs)
- def log_node_warning(self, node, text, context=None):
+ def log_node_warning(self, node, text, context=None, fatal=False):
"""Log a warning, using information about file positions from
the given node. The optional context argument, if given, should be
-another Node type which will also be displayed. If no file position
+another ast.Node type which will also be displayed. If no file position
information is available from the node, the position data from the
context will be used."""
- if len(node.file_positions) == 0 and \
- (context is not None) and len(context.file_positions) > 0:
- file_positions = context.file_positions
+ if hasattr(node, 'file_positions'):
+ if (len(node.file_positions) == 0 and
+ (context is not None) and len(context.file_positions) > 0):
+ file_positions = context.file_positions
+ else:
+ file_positions = node.file_positions
else:
- file_positions = node.file_positions
+ file_positions = None
+ if not context:
+ text = "context=%r %s" % (node, text)
if context:
- if isinstance(context, Function):
+ if isinstance(context, ast.Function):
name = context.symbol
else:
name = context.name
text = "%s: %s" % (name, text)
+ elif len(file_positions) == 0 and hasattr(node, 'name'):
+ text = "(%s)%s: %s" % (node.__class__.__name__, node.name, text)
- self.log_warning(text, file_positions)
+ self.log_warning(text, file_positions, fatal=fatal)
def _find_include(self, include):
searchdirs = self._includepaths[:]
@@ -200,7 +248,6 @@ context will be used."""
parser = self._cachestore.load(filename)
if parser is None:
parser = GIRParser()
- parser.set_include_parsing(True)
parser.parse(filename)
self._cachestore.store(filename, parser)
@@ -210,49 +257,95 @@ context will be used."""
for pkg in parser.get_pkgconfig_packages():
self._pkg_config_packages.add(pkg)
namespace = parser.get_namespace()
- nsname = namespace.name
- for node in namespace.nodes:
- if isinstance(node, Alias):
- self._names.aliases[node.name] = (nsname, node)
- elif isinstance(node, (GLibBoxed, Interface, Class)):
- self._names.type_names[node.type_name] = (nsname, node)
- giname = '%s.%s' % (nsname, node.name)
- self._names.names[giname] = (nsname, node)
- if hasattr(node, 'ctype'):
- self._names.ctypes[node.ctype] = (nsname, node)
- elif hasattr(node, 'symbol'):
- self._names.ctypes[node.symbol] = (nsname, node)
-
- def _add_node(self, node):
- if node is None:
- return
- if node.name.startswith('_'):
- return
- self._namespace.nodes.append(node)
- self._names.names[node.name] = (None, node)
-
- def _strip_namespace_func(self, name):
- prefix = self._namespace.name.lower() + '_'
- if name.lower().startswith(prefix):
- name = name[len(prefix):]
- else:
- prefix = to_underscores(self._namespace.name).lower() + '_'
- if name.lower().startswith(prefix):
- name = name[len(prefix):]
- return self.remove_prefix(name, isfunction=True)
-
- def remove_prefix(self, name, isfunction=False):
- # when --strip-prefix=g:
- # GHashTable -> HashTable
- # g_hash_table_new -> hash_table_new
- prefix = self._strip_prefix.lower()
- if isfunction:
- prefix += '_'
- if len(name) > len(prefix) and name.lower().startswith(prefix):
- name = name[len(prefix):]
-
- while name.startswith('_'):
- name = name[1:]
+ self._includes[namespace.name] = namespace
+
+ def _iter_namespaces(self):
+ """Return an iterator over all included namespaces; the
+currently-scanned namespace is first."""
+ yield self._namespace
+ for ns in self._includes.itervalues():
+ yield ns
+
+ def _sort_matches(self, x, y):
+ if x[0] is self._namespace:
+ return 1
+ elif y[0] is self._namespace:
+ return -1
+ return cmp(x[2], y[2])
+
+ def split_ctype_namespaces(self, ident):
+ """Given a StudlyCaps string identifier like FooBar, return a
+list of (namespace, stripped_identifier) sorted by namespace length,
+or raise ValueError. As a special case, if the current namespace matches,
+it is always biggest (i.e. last)."""
+ matches = []
+ for ns in self._iter_namespaces():
+ for prefix in ns.identifier_prefixes:
+ if ident.startswith(prefix):
+ matches.append((ns, ident[len(prefix):], len(prefix)))
+ break
+ if matches:
+ matches.sort(self._sort_matches)
+ return map(lambda x: (x[0], x[1]), matches)
+ raise ValueError("Unknown namespace for identifier %r" % (ident, ))
+
+ def split_csymbol(self, symbol):
+ """Given a C symbol like foo_bar_do_baz, return a pair of
+(namespace, stripped_symbol) or raise ValueError."""
+ matches = []
+ for ns in self._iter_namespaces():
+ for prefix in ns.symbol_prefixes:
+ if not prefix.endswith('_'):
+ prefix = prefix + '_'
+ if symbol.startswith(prefix):
+ matches.append((ns, symbol[len(prefix):], len(prefix)))
+ break
+ if matches:
+ matches.sort(self._sort_matches)
+ return (matches[-1][0], matches[-1][1])
+ raise ValueError("Unknown namespace for symbol %r" % (symbol, ))
+
+ def strip_identifier_or_warn(self, ident, fatal=False):
+ hidden = ident.startswith('_')
+ if hidden:
+ ident = ident[1:]
+ try:
+ matches = self.split_ctype_namespaces(ident)
+ except ValueError, e:
+ self.log_warning(str(e), fatal=fatal)
+ return None
+ for ns, name in matches:
+ if ns is self._namespace:
+ if hidden:
+ return '_' + name
+ return name
+ (ns, name) = matches[-1]
+ self.log_warning("Skipping foreign identifier %r from namespace %s" % (ident, ns.name, ),
+ fatal=fatal)
+ return None
+
+ def _strip_symbol_or_warn(self, symbol, is_constant=False, fatal=False):
+ ident = symbol.ident
+ if is_constant:
+ # Temporarily lowercase
+ ident = ident.lower()
+ hidden = ident.startswith('_')
+ if hidden:
+ ident = ident[1:]
+ try:
+ (ns, name) = self.split_csymbol(ident)
+ except ValueError, e:
+ self.log_symbol_warning(symbol, "Unknown namespace", fatal=fatal)
+ return None
+ if ns != self._namespace:
+ self.log_symbol_warning(symbol,
+"Skipping foreign symbol from namespace %s" % (ns.name, ),
+ fatal=fatal)
+ return None
+ if is_constant:
+ name = name.upper()
+ if hidden:
+ return '_' + name
return name
def _traverse_one(self, symbol, stype=None):
@@ -268,17 +361,17 @@ context will be used."""
return self._create_struct(symbol)
elif stype == CSYMBOL_TYPE_ENUM:
return self._create_enum(symbol)
- elif stype == CSYMBOL_TYPE_OBJECT:
- return self._create_object(symbol)
elif stype == CSYMBOL_TYPE_MEMBER:
return self._create_member(symbol)
elif stype == CSYMBOL_TYPE_UNION:
return self._create_union(symbol)
elif stype == CSYMBOL_TYPE_CONST:
return self._create_const(symbol)
+ # Ignore variable declarations in the header
+ elif stype == CSYMBOL_TYPE_OBJECT:
+ pass
else:
- raise NotImplementedError(
- 'Transformer: unhandled symbol: %r' % (symbol, ))
+ print 'transformer: unhandled symbol: %r' % (symbol, )
def _enum_common_prefix(self, symbol):
def common_prefix(a, b):
@@ -316,86 +409,31 @@ context will be used."""
# Ok, the enum members don't have a consistent prefix
# among them, so let's just remove the global namespace
# prefix.
- name = self.remove_prefix(child.ident)
- members.append(Member(name.lower(),
+ name = self._strip_symbol_or_warn(child, is_constant=True)
+ if name is None:
+ return None
+ members.append(ast.Member(name.lower(),
child.const_int,
child.ident))
- enum_name = self.remove_prefix(symbol.ident)
+ enum_name = self.strip_identifier_or_warn(symbol.ident)
+ if not enum_name:
+ return None
if symbol.base_type.is_bitfield:
- klass = Bitfield
+ klass = ast.Bitfield
else:
- klass = Enum
+ klass = ast.Enum
node = klass(enum_name, symbol.ident, members)
node.add_symbol_reference(symbol)
- self._names.type_names[symbol.ident] = (None, node)
- return node
-
- def _create_object(self, symbol):
- node = Member(symbol.ident, symbol.base_type.name,
- symbol.ident)
- node.add_symbol_reference(symbol)
return node
- def _type_is_callback(self, type):
- if isinstance(type, Callback):
- return True
- node = self._names.names.get(type.name)
- if node and isinstance(node[1], Callback):
- return True
- return False
-
- def _handle_closure(self, param, closure_idx, closure_param):
- if (closure_param.type.name == TYPE_ANY and
- closure_param.name.endswith('data')):
- param.closure_name = closure_param.name
- param.closure_index = closure_idx
- return True
- return False
-
- def _handle_destroy(self, param, destroy_idx, destroy_param):
- if (destroy_param.type.name == 'GLib.DestroyNotify' or
- destroy_param.type.ctype == 'GDestroyNotify'):
- param.destroy_name = destroy_param.name
- param.destroy_index = destroy_idx
- return True
- return False
-
- def _augment_callback_params(self, params):
- for i, param in enumerate(params):
- if not self._type_is_callback(param.type):
- continue
-
- # set a default scope
- if param.scope is None:
- param.scope = 'call'
-
- # j is the index where we look for closure/destroy to
- # group with the callback param
- j = i + 1
- if j == len(params):
- continue # no more args -> nothing to group
- # look at the param directly following for either a
- # closure or a destroy; only one of these will fire
- had_closure = self._handle_closure(param, j, params[j])
- had_destroy = self._handle_destroy(param, j, params[j])
- j += 1
- # are we out of params, or did we find neither?
- if j == len(params) or (not had_closure and not had_destroy):
- continue
- # we found either a closure or a destroy; check the
- # parameter following for the other
- if not had_closure:
- self._handle_closure(param, j, params[j])
- if not had_destroy:
- self._handle_destroy(param, j, params[j])
-
def _create_function(self, symbol):
parameters = list(self._create_parameters(symbol.base_type))
return_ = self._create_return(symbol.base_type.base_type)
- self._augment_callback_params(parameters)
- name = self._strip_namespace_func(symbol.ident)
- func = Function(name, return_, parameters, symbol.ident)
+ name = self._strip_symbol_or_warn(symbol)
+ if not name:
+ return None
+ func = ast.Function(name, return_, parameters, False, symbol.ident)
func.add_symbol_reference(symbol)
return func
@@ -413,11 +451,10 @@ context will be used."""
elif source_type.type == CTYPE_POINTER:
value = self._create_source_type(source_type.base_type) + '*'
else:
- value = TYPE_ANY
+ value = 'gpointer'
return value
def _create_parameters(self, base_type):
-
# warn if we see annotations for unknown parameters
param_names = set(child.ident for child in base_type.child_list)
for child in base_type.child_list:
@@ -427,7 +464,7 @@ context will be used."""
source_type = symbol.base_type
if (source_type.type == CTYPE_POINTER and
symbol.base_type.base_type.type == CTYPE_FUNCTION):
- node = self._create_callback(symbol)
+ node = self._create_callback(symbol, member=True)
elif source_type.type == CTYPE_STRUCT and source_type.name is None:
node = self._create_struct(symbol, anonymous=True)
elif source_type.type == CTYPE_UNION and source_type.name is None:
@@ -442,21 +479,18 @@ context will be used."""
derefed_name = canonical_ctype[:-1]
else:
derefed_name = canonical_ctype
- derefed_name = self.resolve_param_type(derefed_name)
- ftype = Array(None, ctype, self.parse_ctype(derefed_name))
+ ftype = ast.Array(None, self.create_type_from_ctype_string(ctype),
+ ctype=derefed_name)
child_list = list(symbol.base_type.child_list)
ftype.zeroterminated = False
if child_list:
- ftype.size = '%d' % (child_list[0].const_int, )
+ ftype.size = child_list[0].const_int
else:
- ftype = self._create_type(symbol.base_type,
- is_param=False, is_retval=False)
- ftype = self.resolve_param_type(ftype)
- # Fields are assumed to be read-write
+ ftype = self._create_type_from_base(symbol.base_type)
+ # ast.Fields are assumed to be read-write
# (except for Objects, see also glibtransformer.py)
- node = Field(symbol.ident, ftype, ftype.name,
+ node = ast.Field(symbol.ident, ftype,
readable=True, writable=True, bits=symbol.const_int)
- node.add_symbol_reference(symbol)
return node
def _create_typedef(self, symbol):
@@ -477,14 +511,16 @@ context will be used."""
CTYPE_POINTER,
CTYPE_BASIC_TYPE,
CTYPE_VOID):
- name = self.remove_prefix(symbol.ident)
+ name = self.strip_identifier_or_warn(symbol.ident)
+ if not name:
+ return None
if symbol.base_type.name:
- target = self.remove_prefix(symbol.base_type.name)
+ target = self.create_type_from_ctype_string(symbol.base_type.name)
else:
- target = 'none'
- if name in type_names:
+ target = ast.TYPE_ANY
+ if name in ast.type_names:
return None
- return Alias(name, target, ctype=symbol.ident)
+ return ast.Alias(name, target, ctype=symbol.ident)
else:
raise NotImplementedError(
"symbol %r of type %s" % (symbol.ident, ctype_name(ctype)))
@@ -494,22 +530,20 @@ context will be used."""
# First look up the ctype including any pointers;
# a few type names like 'char*' have their own aliases
# and we need pointer information for those.
- firstpass = type_name_from_ctype(ctype)
+ firstpass = ast.type_names.get(ctype)
# If we have a particular alias for this, skip deep
# canonicalization to prevent changing
# e.g. char* -> int8*
- if firstpass != ctype:
- return firstpass
+ if firstpass:
+ return firstpass.target_fundamental
- # We're also done if the type is already a fundamental
- # known type, or there are no pointers.
- if ctype in type_names or not firstpass.endswith('*'):
- return firstpass
+ if not ctype.endswith('*'):
+ return ctype
# We have a pointer type.
# Strip the end pointer, canonicalize our base type
- base = firstpass[:-1]
+ base = ctype[:-1]
canonical_base = self._canonicalize_ctype(base)
# Append the pointer again
@@ -527,52 +561,71 @@ context will be used."""
# Preserve "pointerness" of struct/union members
if (is_member and canonical.endswith('*') and
- derefed_typename in BASIC_GIR_TYPES):
- return TYPE_ANY
+ derefed_typename in ast.basic_type_names):
+ return 'gpointer'
else:
return derefed_typename
- def _create_type(self, source_type, is_param, is_retval):
+ def _create_type_from_base(self, source_type, is_parameter=False, is_return=False):
ctype = self._create_source_type(source_type)
- if ctype.startswith('va_list'):
- raise SkipError()
- # FIXME: FILE* should not be skipped, it should be handled
- # properly instead
- elif ctype == 'FILE*':
- raise SkipError
-
- is_member = not (is_param or is_retval)
- # Here we handle basic type parsing; most of the heavy lifting
- # and inference comes in annotationparser.py when we merge
- # in annotation data.
- derefed_name = self.parse_ctype(ctype, is_member)
- rettype = Type(derefed_name, ctype)
- rettype.canonical = self._canonicalize_ctype(ctype)
- derefed_ctype = ctype.replace('*', '')
- rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
-
- canontype = type_name_from_ctype(ctype)
- # Is it a const char * or a const gpointer?
- if ((canontype == TYPE_STRING or source_type.type == CTYPE_POINTER) and
- (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST)):
- rettype.is_const = True
- return rettype
+ const = ((source_type.type == CTYPE_POINTER) and
+ (source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST))
+ return self.create_type_from_ctype_string(ctype, is_const=const,
+ is_parameter=is_parameter, is_return=is_return)
+
+ def _create_bare_container_type(self, base, ctype=None,
+ is_const=False):
+ if base in ('GList', 'GSList', 'GLib.List', 'GLib.SList'):
+ if base in ('GList', 'GSList'):
+ name = 'GLib.' + base[1:]
+ else:
+ name = base
+ return ast.List(name, ast.TYPE_ANY, ctype=ctype,
+ is_const=is_const)
+ elif base in ('GArray', 'GPtrArray', 'GByteArray',
+ 'GLib.Array', 'GLib.PtrArray', 'GLib.ByteArray'):
+ if base in ('GArray', 'GPtrArray', 'GByteArray'):
+ name = 'GLib.' + base[1:]
+ else:
+ name = base
+ return ast.Array(name, ast.TYPE_ANY, ctype=ctype,
+ is_const=is_const)
+ elif base in ('GHashTable', 'GLib.HashTable'):
+ return ast.Map(ast.TYPE_ANY, ast.TYPE_ANY, ctype=ctype, is_const=is_const)
+ return None
+
+ def create_type_from_ctype_string(self, ctype, is_const=False,
+ is_parameter=False, is_return=False):
+ canonical = self._canonicalize_ctype(ctype)
+ base = canonical.replace('*', '')
+
+ # Special default: char ** -> ast.Array, same for GStrv
+ if (is_return and canonical == 'utf8*') or base == 'GStrv':
+ bare_utf8 = ast.TYPE_STRING.clone()
+ bare_utf8.ctype = None
+ return ast.Array(None, bare_utf8, ctype=ctype,
+ is_const=is_const)
+
+ fundamental = ast.type_names.get(base)
+ if fundamental is not None:
+ return ast.Type(target_fundamental=fundamental.target_fundamental,
+ ctype=ctype,
+ is_const=is_const)
+ container = self._create_bare_container_type(base, ctype=ctype, is_const=is_const)
+ if container:
+ return container
+ return ast.Type(ctype=ctype, is_const=is_const)
def _create_parameter(self, symbol):
if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
- ptype = Varargs()
+ ptype = ast.Varargs()
else:
- ptype = self._create_type(symbol.base_type,
- is_param=True, is_retval=False)
- ptype = self.resolve_param_type(ptype)
- return Parameter(symbol.ident, ptype)
+ ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
+ return ast.Parameter(symbol.ident, ptype)
def _create_return(self, source_type):
- rtype = self._create_type(source_type,
- is_param=False, is_retval=True)
- rtype = self.resolve_param_type(rtype)
- return_ = Return(rtype)
- return return_
+ typeval = self._create_type_from_base(source_type, is_return=True)
+ return ast.Return(typeval)
def _create_const(self, symbol):
# Don't create constants for non-public things
@@ -580,44 +633,67 @@ context will be used."""
if (symbol.source_filename is None or
not symbol.source_filename.endswith('.h')):
return None
- name = self._strip_namespace_func(symbol.ident)
+ # ignore non-uppercase defines
+ if not self.UCASE_CONSTANT_RE.match(symbol.ident):
+ return None
+ name = self._strip_symbol_or_warn(symbol, is_constant=True)
+ if not name:
+ return None
if symbol.const_string is not None:
- type_name = 'utf8'
+ typeval = ast.TYPE_STRING
value = symbol.const_string
elif symbol.const_int is not None:
- type_name = 'gint'
- value = symbol.const_int
+ typeval = ast.TYPE_INT
+ value = '%d' % (symbol.const_int, )
elif symbol.const_double is not None:
- type_name = 'gdouble'
- value = symbol.const_double
+ typeval = ast.TYPE_DOUBLE
+ value = '%f' % (symbol.const_double, )
else:
raise AssertionError()
- const = Constant(name, type_name, value)
+ const = ast.Constant(name, typeval, value)
const.add_symbol_reference(symbol)
return const
def _create_typedef_struct(self, symbol, disguised=False):
- name = self.remove_prefix(symbol.ident)
- struct = Struct(name, symbol.ident, disguised)
+ name = self.strip_identifier_or_warn(symbol.ident)
+ if not name:
+ return None
+ struct = ast.Record(name, symbol.ident, disguised)
+ self._parse_fields(symbol, struct)
struct.add_symbol_reference(symbol)
self._typedefs_ns[symbol.ident] = struct
- self._create_struct(symbol)
- return struct
+ return None
def _create_typedef_union(self, symbol):
- name = self.remove_prefix(symbol.ident)
- union = Union(name, symbol.ident)
+ name = self.strip_identifier_or_warn(symbol.ident)
+ if not name:
+ return None
+ union = ast.Union(name, symbol.ident)
+ self._parse_fields(symbol, union)
union.add_symbol_reference(symbol)
self._typedefs_ns[symbol.ident] = union
- self._create_union(symbol)
- return union
+ return None
def _create_typedef_callback(self, symbol):
callback = self._create_callback(symbol)
+ if not callback:
+ return None
self._typedefs_ns[callback.name] = callback
return callback
+ def _parse_fields(self, symbol, compound):
+ for child in symbol.base_type.child_list:
+ child_node = self._traverse_one(child)
+ if not child_node:
+ continue
+ if isinstance(child_node, ast.Field):
+ field = child_node
+ else:
+ field = ast.Field(child.ident, None, True, False,
+ anonymous_node=child_node)
+ compound.fields.append(field)
+
def _create_compound(self, klass, symbol, anonymous):
if symbol.ident is None:
# the compound is an anonymous member of another union or a struct
@@ -631,100 +707,95 @@ context will be used."""
# to resolve through the typedefs to find the real
# name
if symbol.ident.startswith('_'):
- name = symbol.ident[1:]
- compound = self._typedefs_ns.get(name, None)
- else:
- name = symbol.ident
+ compound = self._typedefs_ns.get(symbol.ident[1:], None)
if compound is None:
- name = self.remove_prefix(name)
+ if anonymous:
+ name = symbol.ident
+ else:
+ name = self.strip_identifier_or_warn(symbol.ident)
+ if not name:
+ return None
compound = klass(name, symbol.ident)
- for child in symbol.base_type.child_list:
- field = self._traverse_one(child)
- if field:
- compound.fields.append(field)
-
+ self._parse_fields(symbol, compound)
compound.add_symbol_reference(symbol)
return compound
def _create_struct(self, symbol, anonymous=False):
- return self._create_compound(Struct, symbol, anonymous)
+ return self._create_compound(ast.Record, symbol, anonymous)
def _create_union(self, symbol, anonymous=False):
- return self._create_compound(Union, symbol, anonymous)
+ return self._create_compound(ast.Union, symbol, anonymous)
- def _create_callback(self, symbol):
+ def _create_callback(self, symbol, member=False):
parameters = list(self._create_parameters(symbol.base_type.base_type))
retval = self._create_return(symbol.base_type.base_type.base_type)
# Mark the 'user_data' arguments
for i, param in enumerate(parameters):
- if (param.type.name == TYPE_ANY and
- param.name == 'user_data'):
- param.closure_index = i
-
- if symbol.ident.find('_') > 0:
- name = self.remove_prefix(symbol.ident, True)
+ if (param.type.target_fundamental == 'gpointer' and
+ param.argname == 'user_data'):
+ param.closure_name = param.argname
+
+ if member:
+ name = symbol.ident
+ elif symbol.ident.find('_') > 0:
+ name = self._strip_symbol_or_warn(symbol)
+ if not name:
+ return None
else:
- name = self.remove_prefix(symbol.ident)
- callback = Callback(name, retval, parameters, symbol.ident)
+ name = self.strip_identifier_or_warn(symbol.ident)
+ if not name:
+ return None
+ callback = ast.Callback(name, retval, parameters, False)
callback.add_symbol_reference(symbol)
return callback
+ def create_type_from_user_string(self, typestr):
+ """Parse a C type string (as might be given from an
+ annotation) and resolve it. For compatibility, we can consume
+both GI type string (utf8, Foo.Bar) style, as well as C (char *, FooBar) style."""
+ if '.' in typestr:
+ container = self._create_bare_container_type(typestr)
+ if container:
+ return container
+ return self._namespace.type_from_name(typestr)
+ typeval = self.create_type_from_ctype_string(typestr)
+ self.resolve_type(typeval)
+ # Explicitly clear out the c_type; there isn't one in this case.
+ typeval.ctype = None
+ return typeval
+
+ def resolve_type(self, typeval):
+ if isinstance(typeval, (ast.Array, ast.List)):
+ self.resolve_type(typeval.element_type)
+ return
+ elif isinstance(typeval, ast.Map):
+ self.resolve_type(typeval.key_type)
+ self.resolve_type(typeval.value_type)
+ return
+ elif not typeval.resolved and typeval.ctype:
+ pointer_stripped = typeval.ctype.replace('*', '')
+ try:
+ matches = self.split_ctype_namespaces(pointer_stripped)
+ except ValueError, e:
+ raise TypeResolutionException(e)
+ target_giname=None
+ for namespace, name in matches:
+ target = namespace.get(name)
+ if not target:
+ target = namespace.get_by_ctype(pointer_stripped)
+ if target:
+ typeval.target_giname='%s.%s' % (namespace.name, target.name)
+ return
+
def _typepair_to_str(self, item):
nsname, item = item
if nsname is None:
return item.name
return '%s.%s' % (nsname, item.name)
- def _resolve_type_name_1(self, type_name, ctype, names):
- # First look using the built-in names
- if ctype:
- try:
- return type_names[ctype]
- except KeyError, e:
- pass
- try:
- return type_names[type_name]
- except KeyError, e:
- pass
-
- if ctype:
- ctype = ctype.replace('*', '')
- resolved = names.ctypes.get(ctype)
- if resolved:
- return self._typepair_to_str(resolved)
- type_name = self.remove_prefix(type_name)
- resolved = names.aliases.get(type_name)
- if resolved:
- return self._typepair_to_str(resolved)
- resolved = names.names.get(type_name)
- if resolved:
- return self._typepair_to_str(resolved)
- resolved = names.type_names.get(type_name)
- if resolved:
- return self._typepair_to_str(resolved)
- raise KeyError("failed to find %r" % (type_name, ))
-
- def resolve_type_name_full(self, type_name, ctype,
- names, allow_invalid=True):
- try:
- return self._resolve_type_name_1(type_name, ctype, names)
- except KeyError, e:
- try:
- return self._resolve_type_name_1(type_name, ctype, self._names)
- except KeyError, e:
- if not allow_invalid:
- raise
- return type_name
-
- def resolve_type_name(self, type_name, ctype=None):
- try:
- return self.resolve_type_name_full(type_name, ctype, self._names)
- except KeyError, e:
- return type_name
-
def gtypename_to_giname(self, gtname, names):
resolved = names.type_names.get(gtname)
if resolved:
@@ -742,23 +813,6 @@ context will be used."""
else:
return None
- def resolve_param_type_full(self, ptype, names, **kwargs):
- if isinstance(ptype, Node):
- ptype.name = self.resolve_type_name_full(ptype.name,
- self.ctype_of(ptype),
- names, **kwargs)
- elif isinstance(ptype, basestring):
- return self.resolve_type_name_full(ptype, ptype, names, **kwargs)
- else:
- raise AssertionError("Unhandled param: %r" % (ptype, ))
- return ptype
-
- def resolve_param_type(self, ptype):
- try:
- return self.resolve_param_type_full(ptype, self._names)
- except KeyError, e:
- return ptype
-
def follow_aliases(self, type_name, names):
while True:
resolved = names.aliases.get(type_name)
@@ -768,8 +822,3 @@ context will be used."""
else:
break
return type_name
-
- def iter_enums(self):
- for node in self._namespace.nodes:
- if isinstance(node, Enum):
- yield node
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 1bd23fc0..dba958ef 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -102,3 +102,16 @@ def get_libtool_command(options):
return None
return ['libtool']
+
+
+def files_are_identical(path1, path2):
+ f1 = open(path1)
+ f2 = open(path2)
+ buf1 = f1.read(8192)
+ buf2 = f2.read(8192)
+ while buf1 == buf2 and buf1 != '':
+ buf1 = f1.read(8192)
+ buf2 = f2.read(8192)
+ f1.close()
+ f2.close()
+ return buf1 == buf2
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a86a750f..49833f10 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -14,7 +14,7 @@ BUILT_SOURCES += everything.c everything.h
CLEANFILES += $(BUILT_SOURCES) everything-stamp.h
everything-stamp.h: Makefile
- $(AM_V_GEN) $(top_builddir)/tools/g-ir-scanner --test-codegen=Everything,everything.h,everything.c
+ $(AM_V_GEN) $(top_builddir)/tools/g-ir-scanner --generate-typelib-tests=Everything,everything.h,everything.c
touch $@
everything.c: everything-stamp.h
diff --git a/tests/scanner/Annotation-1.0-expected.gir b/tests/scanner/Annotation-1.0-expected.gir
index 1d73c250..086f0639 100644
--- a/tests/scanner/Annotation-1.0-expected.gir
+++ b/tests/scanner/Annotation-1.0-expected.gir
@@ -2,7 +2,7 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
@@ -13,7 +13,8 @@ and/or use gtk-doc annotations. -->
<namespace name="Annotation"
version="1.0"
shared-library="libannotation.so"
- c:prefix="Annotation">
+ c:identifier-prefixes="Annotation"
+ c:symbol-prefixes="annotation">
<callback name="Callback" c:type="AnnotationCallback">
<doc xml:whitespace="preserve">This is a callback.</doc>
<return-value transfer-ownership="none">
@@ -74,6 +75,7 @@ and/or use gtk-doc annotations. -->
</parameters>
</callback>
<class name="Object"
+ c:symbol-prefix="object"
c:type="AnnotationObject"
parent="GObject.Object"
glib:type-name="AnnotationObject"
@@ -154,7 +156,7 @@ and/or use gtk-doc annotations. -->
<parameters>
<parameter name="nums" transfer-ownership="none">
<doc xml:whitespace="preserve">Sequence of numbers that are zero-terminated</doc>
- <array zero-terminated="0" length="2" c:type="int*">
+ <array length="1" zero-terminated="0" c:type="int*">
<type name="gint"/>
</array>
</parameter>
@@ -173,7 +175,7 @@ and/or use gtk-doc annotations. -->
<parameters>
<parameter name="nums" transfer-ownership="none">
<doc xml:whitespace="preserve">Sequence of numbers that are zero-terminated</doc>
- <array length="2" c:type="int*">
+ <array length="1" c:type="int*">
<type name="gint"/>
</array>
</parameter>
@@ -215,7 +217,7 @@ and/or use gtk-doc annotations. -->
<parameter name="func"
transfer-ownership="none"
scope="call"
- closure="2">
+ closure="1">
<doc xml:whitespace="preserve">Callback to invoke</doc>
<type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
</parameter>
@@ -365,7 +367,7 @@ each string needs to be freed.</doc>
caller-allocates="0"
transfer-ownership="full">
<doc xml:whitespace="preserve">Argument vector</doc>
- <array length="1" c:type="char***">
+ <array length="0" c:type="char***">
<type name="utf8"/>
</array>
</parameter>
@@ -379,7 +381,7 @@ each string needs to be freed.</doc>
<parameters>
<parameter name="data" transfer-ownership="none">
<doc xml:whitespace="preserve">The data</doc>
- <array length="2" c:type="guchar*">
+ <array length="1" c:type="guchar*">
<type name="guint8"/>
</array>
</parameter>
@@ -397,7 +399,7 @@ each string needs to be freed.</doc>
<parameters>
<parameter name="data" transfer-ownership="none">
<doc xml:whitespace="preserve">The data</doc>
- <array length="2" c:type="gchar*">
+ <array length="1" c:type="gchar*">
<type name="gint8"/>
</array>
</parameter>
@@ -416,7 +418,7 @@ type.</doc>
<parameters>
<parameter name="data" transfer-ownership="none">
<doc xml:whitespace="preserve">The data</doc>
- <array length="2" c:type="gpointer">
+ <array length="1" c:type="gpointer">
<type name="guint8"/>
</array>
</parameter>
@@ -448,13 +450,33 @@ type.</doc>
</return-value>
<parameters>
<parameter name="bytes" transfer-ownership="none">
- <array c:type="guchar*">
- <type name="guint8"/>
- </array>
+ <type name="guint8" c:type="guchar*"/>
</parameter>
</parameters>
</method>
- <method name="watch" c:identifier="annotation_object_watch_full">
+ <method name="watch"
+ c:identifier="annotation_object_watch"
+ shadows="annotation_object_watch_full"
+ introspectable="0">
+ <doc xml:whitespace="preserve">This is here just for the sake of being overriden by its
+annotation_object_watch_full().</doc>
+ <return-value transfer-ownership="none">
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="func" transfer-ownership="none" closure="1">
+ <doc xml:whitespace="preserve">The callback</doc>
+ <type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
+ </parameter>
+ <parameter name="user_data" transfer-ownership="none">
+ <doc xml:whitespace="preserve">The callback data</doc>
+ <type name="gpointer" c:type="gpointer"/>
+ </parameter>
+ </parameters>
+ </method>
+ <method name="watch_full"
+ c:identifier="annotation_object_watch_full"
+ shadowed-by="annotation_object_watch">
<doc xml:whitespace="preserve">Test overriding via the "Rename To" annotation.</doc>
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
@@ -463,8 +485,8 @@ type.</doc>
<parameter name="func"
transfer-ownership="none"
scope="notified"
- closure="2"
- destroy="3">
+ closure="1"
+ destroy="2">
<doc xml:whitespace="preserve">The callback</doc>
<type name="ForeachFunc" c:type="AnnotationForeachFunc"/>
</parameter>
@@ -472,7 +494,7 @@ type.</doc>
<doc xml:whitespace="preserve">The callback data</doc>
<type name="gpointer" c:type="gpointer"/>
</parameter>
- <parameter name="destroy" transfer-ownership="none" scope="call">
+ <parameter name="destroy" transfer-ownership="none" scope="async">
<doc xml:whitespace="preserve">Destroy notification</doc>
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</parameter>
@@ -492,7 +514,7 @@ type.</doc>
writable="1"
construct="1"
transfer-ownership="none">
- <type name="Callback" c:type="gpointer"/>
+ <type name="Callback"/>
</property>
<property name="string-property"
version="1.0"
@@ -502,7 +524,7 @@ type.</doc>
construct="1"
transfer-ownership="none">
<doc xml:whitespace="preserve">This is a property which is a string</doc>
- <type name="utf8" c:type="gchararray"/>
+ <type name="utf8"/>
</property>
<field name="parent_instance">
<type name="GObject.Object" c:type="GObject"/>
@@ -512,42 +534,42 @@ type.</doc>
<return-value transfer-ownership="full">
<attribute name="some.annotation.foo3" value="val3"/>
<doc xml:whitespace="preserve">the return value</doc>
- <type name="utf8" c:type="gchararray"/>
+ <type name="utf8"/>
</return-value>
<parameters>
- <parameter name="arg1" transfer-ownership="none">
+ <parameter name="object" transfer-ownership="none">
<attribute name="some.annotation.foo1" value="val1"/>
<doc xml:whitespace="preserve">a value</doc>
- <type name="utf8" c:type="gchararray"/>
+ <type name="utf8"/>
</parameter>
- <parameter name="arg2" transfer-ownership="none">
+ <parameter name="p0" transfer-ownership="none">
<attribute name="some.annotation.foo2" value="val2"/>
<doc xml:whitespace="preserve">another value</doc>
- <type name="utf8" c:type="gchararray"/>
+ <type name="utf8"/>
</parameter>
</parameters>
</glib:signal>
<glib:signal name="doc-empty-arg-parsing">
<doc xml:whitespace="preserve">This signal tests an empty document argument (@arg1)</doc>
- <return-value transfer-ownership="full">
- <type name="none" c:type="void"/>
+ <return-value transfer-ownership="none">
+ <type name="none"/>
</return-value>
<parameters>
<parameter name="object" transfer-ownership="none">
- <type name="gpointer" c:type="gpointer"/>
+ <type name="gpointer"/>
</parameter>
</parameters>
</glib:signal>
<glib:signal name="list-signal">
<doc xml:whitespace="preserve">This is a signal which takes a list of strings, but it's not
known by GObject as it's only marked as G_TYPE_POINTER</doc>
- <return-value transfer-ownership="full">
- <type name="none" c:type="void"/>
+ <return-value transfer-ownership="none">
+ <type name="none"/>
</return-value>
<parameters>
- <parameter name="list" transfer-ownership="container">
+ <parameter name="object" transfer-ownership="container">
<doc xml:whitespace="preserve">a list of strings</doc>
- <type name="GLib.List" c:type="gpointer">
+ <type name="GLib.List">
<type name="utf8"/>
</type>
</parameter>
@@ -559,13 +581,13 @@ known by GObject as it's only marked as G_TYPE_POINTER</doc>
deprecated-version="1.2">
<doc xml:whitespace="preserve">This is a signal which has a broken signal handler,
it says it's pointer but it's actually a string.</doc>
- <return-value transfer-ownership="full">
- <type name="none" c:type="void"/>
+ <return-value transfer-ownership="none">
+ <type name="none"/>
</return-value>
<parameters>
- <parameter name="string" transfer-ownership="none">
+ <parameter name="object" transfer-ownership="none">
<doc xml:whitespace="preserve">a string</doc>
- <type name="utf8" c:type="gpointer"/>
+ <type name="utf8"/>
</parameter>
</parameters>
</glib:signal>
@@ -580,8 +602,8 @@ it says it's pointer but it's actually a string.</doc>
<record name="Struct" c:type="AnnotationStruct">
<doc xml:whitespace="preserve">This is a test of an array of object in an field of a struct.</doc>
<field name="objects" writable="1">
- <array zero-terminated="0" c:type="AnnotationObject*" fixed-size="10">
- <type name="Object"/>
+ <array zero-terminated="0" c:type="AnnotationObject" fixed-size="10">
+ <type name="Object" c:type="AnnotationObject*"/>
</array>
</field>
</record>
@@ -614,13 +636,16 @@ detection, and fixing it via annotations.</doc>
<parameters>
<parameter name="callback"
transfer-ownership="none"
- scope="call"
+ scope="notified"
closure="2"
destroy="1">
<doc xml:whitespace="preserve">Destroy notification</doc>
<type name="Callback" c:type="AnnotationCallback"/>
</parameter>
- <parameter name="destroy" transfer-ownership="none" scope="call">
+ <parameter name="destroy"
+ transfer-ownership="none"
+ scope="notified"
+ closure="2">
<type name="NotifyFunc" c:type="AnnotationNotifyFunc"/>
</parameter>
<parameter name="data" transfer-ownership="none">
@@ -631,7 +656,7 @@ detection, and fixing it via annotations.</doc>
<function name="get_source_file" c:identifier="annotation_get_source_file">
<return-value transfer-ownership="full">
<doc xml:whitespace="preserve">Source file</doc>
- <type name="filename" c:type="char*"/>
+ <type name="filename"/>
</return-value>
</function>
<function name="init" c:identifier="annotation_init">
@@ -669,7 +694,9 @@ detection, and fixing it via annotations.</doc>
</parameter>
</parameters>
</function>
- <function name="ptr_array" c:identifier="annotation_ptr_array">
+ <function name="ptr_array"
+ c:identifier="annotation_ptr_array"
+ introspectable="0">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
@@ -706,7 +733,7 @@ detection, and fixing it via annotations.</doc>
<parameters>
<parameter name="fname" transfer-ownership="none">
<doc xml:whitespace="preserve">Source file</doc>
- <type name="filename" c:type="char*"/>
+ <type name="filename"/>
</parameter>
</parameters>
</function>
diff --git a/tests/scanner/Bar-1.0-expected.gir b/tests/scanner/Bar-1.0-expected.gir
index dee28b37..4d919fd9 100644
--- a/tests/scanner/Bar-1.0-expected.gir
+++ b/tests/scanner/Bar-1.0-expected.gir
@@ -2,15 +2,20 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
<include name="GLib" version="2.0"/>
<include name="GObject" version="2.0"/>
<package name="gobject-2.0"/>
- <namespace name="Bar" version="1.0" shared-library="" c:prefix="Bar">
+ <namespace name="Bar"
+ version="1.0"
+ shared-library=""
+ c:identifier-prefixes="Bar"
+ c:symbol-prefixes="bar">
<class name="Baz"
+ c:symbol-prefix="baz"
c:type="BarBaz"
parent="GObject.Object"
glib:type-name="BarBaz"
diff --git a/tests/scanner/Foo-1.0-expected.gir b/tests/scanner/Foo-1.0-expected.gir
index 003485bc..c52a9230 100644
--- a/tests/scanner/Foo-1.0-expected.gir
+++ b/tests/scanner/Foo-1.0-expected.gir
@@ -2,7 +2,7 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
@@ -15,12 +15,13 @@ and/or use gtk-doc annotations. -->
<namespace name="Foo"
version="1.0"
shared-library="libfoo.so"
- c:prefix="Foo">
+ c:identifier-prefixes="Foo"
+ c:symbol-prefixes="foo">
<alias name="ObjectCookie" c:type="FooObjectCookie">
- <type name="gpointer"/>
+ <type name="gpointer" c:type="gpointer"/>
</alias>
<alias name="XEvent" c:type="FooXEvent">
- <type name="none"/>
+ <type name="gpointer" c:type="gpointer"/>
</alias>
<enumeration name="ASingle" c:type="FooASingle">
<member name="some_single_enum"
@@ -35,7 +36,8 @@ and/or use gtk-doc annotations. -->
<record name="BRect"
c:type="FooBRect"
glib:type-name="FooBRect"
- glib:get-type="foo_brect_get_type">
+ glib:get-type="foo_brect_get_type"
+ c:symbol-prefix="brect">
<field name="x" writable="1">
<type name="gdouble" c:type="double"/>
</field>
@@ -69,7 +71,8 @@ and/or use gtk-doc annotations. -->
<union name="BUnion"
c:type="FooBUnion"
glib:type-name="FooBUnion"
- glib:get-type="foo_bunion_get_type">
+ glib:get-type="foo_bunion_get_type"
+ c:symbol-prefix="bunion">
<field name="type" writable="1">
<type name="gint" c:type="int"/>
</field>
@@ -94,7 +97,8 @@ and/or use gtk-doc annotations. -->
<record name="Boxed"
c:type="FooBoxed"
glib:type-name="FooBoxed"
- glib:get-type="foo_boxed_get_type">
+ glib:get-type="foo_boxed_get_type"
+ c:symbol-prefix="boxed">
<constructor name="new" c:identifier="foo_boxed_new">
<return-value transfer-ownership="full">
<type name="Boxed" c:type="FooBoxed*"/>
@@ -107,6 +111,7 @@ and/or use gtk-doc annotations. -->
</method>
</record>
<class name="Buffer"
+ c:symbol-prefix="buffer"
c:type="FooBuffer"
parent="Object"
glib:type-name="FooBuffer"
@@ -121,6 +126,7 @@ and/or use gtk-doc annotations. -->
</class>
<record name="BufferClass"
c:type="FooBufferClass"
+ disguised="1"
glib:is-gtype-struct-for="Buffer">
</record>
<callback name="Callback" c:type="FooCallback">
@@ -142,7 +148,8 @@ and/or use gtk-doc annotations. -->
<record name="DBusData"
c:type="FooDBusData"
glib:type-name="FooDBusData"
- glib:get-type="foo_dbus_data_get_type">
+ glib:get-type="foo_dbus_data_get_type"
+ c:symbol-prefix="dbus_data">
<method name="method" c:identifier="foo_dbus_data_method">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
@@ -150,7 +157,7 @@ and/or use gtk-doc annotations. -->
</method>
</record>
<constant name="DEFINE_SHOULD_BE_EXPOSED" value="should be exposed">
- <type name="utf8"/>
+ <type name="utf8" c:type="gchar*"/>
</constant>
<enumeration name="EnumFullname" c:type="FooEnumFullname">
<member name="one" value="1" c:identifier="FOO_ENUM_FULLNAME_ONE"/>
@@ -250,6 +257,7 @@ and/or use gtk-doc annotations. -->
</field>
</record>
<interface name="Interface"
+ c:symbol-prefix="interface"
c:type="FooInterface"
glib:type-name="FooInterface"
glib:get-type="foo_interface_get_type"
@@ -282,7 +290,7 @@ and/or use gtk-doc annotations. -->
<type name="GObject.TypeInterface" c:type="GTypeInterface"/>
</field>
<field name="do_foo">
- <callback name="do_foo" c:type="do_foo">
+ <callback name="do_foo">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
@@ -298,6 +306,7 @@ and/or use gtk-doc annotations. -->
</field>
</record>
<class name="Object"
+ c:symbol-prefix="object"
c:type="FooObject"
parent="GObject.Object"
glib:type-name="FooObject"
@@ -314,7 +323,7 @@ and/or use gtk-doc annotations. -->
uses a C sugar return type.</doc>
<return-value transfer-ownership="none">
<doc xml:whitespace="preserve">The global #FooSubobject</doc>
- <type name="Subobject" c:type="FooObject*"/>
+ <type name="Subobject"/>
</return-value>
</function>
<function name="static_meth" c:identifier="foo_object_static_meth">
@@ -388,7 +397,7 @@ uses a C sugar return type.</doc>
c:identifier="foo_object_new_cookie"
introspectable="0">
<doc xml:whitespace="preserve">Not sure why this test is here...</doc>
- <return-value transfer-ownership="full">
+ <return-value>
<type name="ObjectCookie" c:type="FooObjectCookie"/>
</return-value>
<parameters>
@@ -460,11 +469,18 @@ uses a C sugar return type.</doc>
</parameter>
</parameters>
</method>
+ <property name="hidden"
+ introspectable="0"
+ writable="1"
+ construct-only="1"
+ transfer-ownership="none">
+ <type/>
+ </property>
<property name="string"
writable="1"
construct="1"
transfer-ownership="none">
- <type name="utf8" c:type="gchararray"/>
+ <type name="utf8"/>
</property>
<field name="parent_instance">
<type name="GObject.Object" c:type="GObject"/>
@@ -474,14 +490,14 @@ uses a C sugar return type.</doc>
</field>
<glib:signal name="signal">
<return-value transfer-ownership="full">
- <type name="utf8" c:type="gchararray"/>
+ <type name="utf8"/>
</return-value>
<parameters>
<parameter name="object" transfer-ownership="none">
- <type name="GObject.Object" c:type="GObject"/>
+ <type name="GObject.Object"/>
</parameter>
<parameter name="p0" transfer-ownership="none">
- <type name="gpointer" c:type="gpointer"/>
+ <type name="gpointer"/>
</parameter>
</parameters>
</glib:signal>
@@ -493,7 +509,7 @@ uses a C sugar return type.</doc>
<type name="GObject.ObjectClass" c:type="GObjectClass"/>
</field>
<field name="virtual_method">
- <callback name="virtual_method" c:type="virtual_method">
+ <callback name="virtual_method">
<return-value transfer-ownership="none">
<type name="gboolean" c:type="gboolean"/>
</return-value>
@@ -508,7 +524,7 @@ uses a C sugar return type.</doc>
</callback>
</field>
<field name="read_fn">
- <callback name="read_fn" c:type="read_fn">
+ <callback name="read_fn">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
@@ -529,12 +545,12 @@ uses a C sugar return type.</doc>
</field>
<field name="_reserved">
<array zero-terminated="0" c:type="GCallback" fixed-size="4">
- <type name="GObject.Callback"/>
+ <type name="GObject.Callback" c:type="GCallback"/>
</array>
</field>
</record>
- <constant name="PIE_IS_TASTY" value="3.14159">
- <type name="gdouble"/>
+ <constant name="PIE_IS_TASTY" value="3.141590">
+ <type name="gdouble" c:type="gdouble"/>
</constant>
<record name="Rectangle" c:type="FooRectangle">
<field name="x" writable="1">
@@ -581,7 +597,7 @@ uses a C sugar return type.</doc>
</method>
</record>
<constant name="SUCCESS_INT" value="4408">
- <type name="gint"/>
+ <type name="gint" c:type="gint"/>
</constant>
<enumeration name="Skippable" introspectable="0" c:type="FooSkippable">
<doc xml:whitespace="preserve">Some type that is only interesting from C and should not be
@@ -612,9 +628,10 @@ exposed to language bindings.</doc>
<type name="gint" c:type="int"/>
</field>
</record>
- <record name="StructPrivate" c:type="FooStructPrivate">
+ <record name="StructPrivate" c:type="FooStructPrivate" disguised="1">
</record>
<interface name="SubInterface"
+ c:symbol-prefix="sub_interface"
c:type="FooSubInterface"
glib:type-name="FooSubInterface"
glib:get-type="foo_sub_interface_get_type"
@@ -631,8 +648,8 @@ exposed to language bindings.</doc>
</return-value>
</method>
<glib:signal name="destroy-event">
- <return-value transfer-ownership="full">
- <type name="none" c:type="void"/>
+ <return-value transfer-ownership="none">
+ <type name="none"/>
</return-value>
</glib:signal>
</interface>
@@ -643,7 +660,7 @@ exposed to language bindings.</doc>
<type name="GObject.TypeInterface" c:type="GTypeInterface"/>
</field>
<field name="destroy_event">
- <callback name="destroy_event" c:type="destroy_event">
+ <callback name="destroy_event">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
@@ -655,7 +672,7 @@ exposed to language bindings.</doc>
</callback>
</field>
<field name="do_bar">
- <callback name="do_bar" c:type="do_bar">
+ <callback name="do_bar">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
@@ -668,6 +685,7 @@ exposed to language bindings.</doc>
</field>
</record>
<class name="Subobject"
+ c:symbol-prefix="subobject"
c:type="FooSubobject"
parent="Object"
abstract="1"
@@ -699,12 +717,12 @@ exposed to language bindings.</doc>
<type name="gint" c:type="int"/>
</field>
<field name="lines" writable="1">
- <array zero-terminated="0" c:type="char" fixed-size="80">
- <type name="gchar"/>
+ <array zero-terminated="0" c:type="gchar" fixed-size="80">
+ <type name="gchar" c:type="char"/>
</array>
</field>
<field name="data" writable="1">
- <type name="gpointer" c:type="guchar*"/>
+ <type name="guint8" c:type="guchar*"/>
</field>
</record>
<union name="Union" c:type="FooUnion">
@@ -769,7 +787,7 @@ exposed to language bindings.</doc>
<parameter name="data" transfer-ownership="none">
<type name="gpointer" c:type="gpointer"/>
</parameter>
- <parameter name="destroy" transfer-ownership="none" scope="call">
+ <parameter name="destroy" transfer-ownership="none" scope="async">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</parameter>
</parameters>
@@ -785,7 +803,7 @@ exposed to language bindings.</doc>
</parameters>
</function>
<function name="enum_type_returnv" c:identifier="foo_enum_type_returnv">
- <return-value transfer-ownership="full">
+ <return-value transfer-ownership="none">
<type name="EnumType" c:type="FooEnumType"/>
</return-value>
<parameters>
@@ -794,6 +812,11 @@ exposed to language bindings.</doc>
</parameter>
</parameters>
</function>
+ <function name="error_quark" c:identifier="foo_error_quark">
+ <return-value transfer-ownership="none">
+ <type name="GLib.Quark" c:type="GQuark"/>
+ </return-value>
+ </function>
<function name="init" c:identifier="foo_init">
<return-value transfer-ownership="none">
<type name="gint" c:type="gint"/>
@@ -832,6 +855,36 @@ exposed to language bindings.</doc>
</parameter>
</parameters>
</function>
+ <function name="some_variant"
+ c:identifier="foo_some_variant"
+ introspectable="0">
+ <return-value transfer-ownership="none">
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="x" transfer-ownership="none">
+ <type name="guint" c:type="guint"/>
+ </parameter>
+ <parameter name="args" transfer-ownership="none">
+ <type name="va_list" c:type="va_list"/>
+ </parameter>
+ </parameters>
+ </function>
+ <function name="some_variant_ptr"
+ c:identifier="foo_some_variant_ptr"
+ introspectable="0">
+ <return-value transfer-ownership="none">
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="x" transfer-ownership="none">
+ <type name="guint" c:type="guint"/>
+ </parameter>
+ <parameter name="args" transfer-ownership="none">
+ <type name="va_list" c:type="va_list*"/>
+ </parameter>
+ </parameters>
+ </function>
<function name="test_array" c:identifier="foo_test_array">
<return-value transfer-ownership="container">
<array name="GLib.Array" c:type="GArray*">
@@ -929,7 +982,7 @@ exposed to language bindings.</doc>
<parameter name="i" transfer-ownership="none">
<type name="gint" c:type="gint"/>
</parameter>
- <parameter name="callback" transfer-ownership="none" scope="call">
+ <parameter name="callback" transfer-ownership="none">
<type name="VarargsCallback" c:type="FooVarargsCallback"/>
</parameter>
</parameters>
@@ -941,7 +994,7 @@ exposed to language bindings.</doc>
<type name="none" c:type="void"/>
</return-value>
<parameters>
- <parameter name="callback" transfer-ownership="none" scope="call">
+ <parameter name="callback" transfer-ownership="none">
<type name="VarargsCallback" c:type="FooVarargsCallback"/>
</parameter>
</parameters>
@@ -953,10 +1006,10 @@ exposed to language bindings.</doc>
<type name="none" c:type="void"/>
</return-value>
<parameters>
- <parameter name="callback" transfer-ownership="none" scope="call">
+ <parameter name="callback" transfer-ownership="none">
<type name="VarargsCallback" c:type="FooVarargsCallback"/>
</parameter>
- <parameter name="callback2" transfer-ownership="none" scope="call">
+ <parameter name="callback2" transfer-ownership="none">
<type name="VarargsCallback" c:type="FooVarargsCallback"/>
</parameter>
</parameters>
diff --git a/tests/scanner/GtkFrob-1.0-expected.gir b/tests/scanner/GtkFrob-1.0-expected.gir
index a890e95d..c8758472 100644
--- a/tests/scanner/GtkFrob-1.0-expected.gir
+++ b/tests/scanner/GtkFrob-1.0-expected.gir
@@ -2,7 +2,7 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
@@ -12,7 +12,8 @@ and/or use gtk-doc annotations. -->
<namespace name="GtkFrob"
version="1.0"
shared-library="libgtkfrob.so"
- c:prefix="Gtk">
+ c:identifier-prefixes="Gtk"
+ c:symbol-prefixes="gtk_frob">
<function name="language_manager_get_default"
c:identifier="gtk_frob_language_manager_get_default">
<return-value transfer-ownership="none">
diff --git a/tests/scanner/Makefile.am b/tests/scanner/Makefile.am
index 1b5dc7f5..a30336a0 100644
--- a/tests/scanner/Makefile.am
+++ b/tests/scanner/Makefile.am
@@ -91,7 +91,7 @@ GtkFrob_1_0_gir_PACKAGES = gobject-2.0
GtkFrob_1_0_gir_LIBS = libgtkfrob.la
GtkFrob_1_0_gir_INCLUDES = GObject-2.0
GtkFrob_1_0_gir_FILES = $(libgtkfrob_la_SOURCES)
-GtkFrob_1_0_gir_SCANNERFLAGS = --strip-prefix=Gtk
+GtkFrob_1_0_gir_SCANNERFLAGS = --identifier-prefix=Gtk --symbol-prefix=gtk_frob
GIRS += GtkFrob-1.0.gir
noinst_PROGRAMS = barapp
diff --git a/tests/scanner/Regress-1.0-expected.gir b/tests/scanner/Regress-1.0-expected.gir
index e909a2fe..d540a953 100644
--- a/tests/scanner/Regress-1.0-expected.gir
+++ b/tests/scanner/Regress-1.0-expected.gir
@@ -2,7 +2,7 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
@@ -13,11 +13,13 @@ and/or use gtk-doc annotations. -->
<namespace name="Regress"
version="1.0"
shared-library="libregress.so"
- c:prefix="Regress">
+ c:identifier-prefixes="Regress"
+ c:symbol-prefixes="regress">
<record name="TestBoxed"
c:type="RegressTestBoxed"
glib:type-name="RegressTestBoxed"
- glib:get-type="regress_test_boxed_get_type">
+ glib:get-type="regress_test_boxed_get_type"
+ c:symbol-prefix="test_boxed">
<field name="some_int8" writable="1">
<type name="gint8" c:type="gint8"/>
</field>
@@ -63,7 +65,7 @@ and/or use gtk-doc annotations. -->
<type name="TestBoxed" c:type="RegressTestBoxed*"/>
</return-value>
<parameters>
- <parameter name="s" transfer-ownership="full">
+ <parameter name="s" transfer-ownership="none">
<type name="utf8" c:type="char*"/>
</parameter>
</parameters>
@@ -84,7 +86,9 @@ and/or use gtk-doc annotations. -->
</parameters>
</method>
</record>
- <record name="TestBoxedPrivate" c:type="RegressTestBoxedPrivate">
+ <record name="TestBoxedPrivate"
+ c:type="RegressTestBoxedPrivate"
+ disguised="1">
</record>
<callback name="TestCallback" c:type="RegressTestCallback">
<return-value transfer-ownership="none">
@@ -136,13 +140,14 @@ and/or use gtk-doc annotations. -->
glib:nick="flag3"/>
</bitfield>
<class name="TestFloating"
+ c:symbol-prefix="test_floating"
c:type="RegressTestFloating"
parent="GObject.InitiallyUnowned"
glib:type-name="RegressTestFloating"
glib:get-type="regress_test_floating_get_type"
glib:type-struct="TestFloatingClass">
<constructor name="new" c:identifier="regress_test_floating_new">
- <return-value transfer-ownership="full">
+ <return-value transfer-ownership="none">
<doc xml:whitespace="preserve">A new floating #RegressTestFloating</doc>
<type name="TestFloating" c:type="RegressTestFloating*"/>
</return-value>
@@ -160,6 +165,7 @@ and/or use gtk-doc annotations. -->
</field>
</record>
<class name="TestFundamentalObject"
+ c:symbol-prefix="test_fundamental_object"
c:type="RegressTestFundamentalObject"
abstract="1"
glib:type-name="RegressTestFundamentalObject"
@@ -234,6 +240,7 @@ and/or use gtk-doc annotations. -->
</parameters>
</callback>
<class name="TestFundamentalSubObject"
+ c:symbol-prefix="test_fundamental_sub_object"
c:type="RegressTestFundamentalSubObject"
parent="TestFundamentalObject"
glib:type-name="RegressTestFundamentalSubObject"
@@ -269,6 +276,7 @@ and/or use gtk-doc annotations. -->
</field>
</record>
<interface name="TestInterface"
+ c:symbol-prefix="test_interface"
c:type="RegressTestInterface"
glib:type-name="RegressTestInterface"
glib:get-type="regress_test_interface_get_type"
@@ -282,6 +290,7 @@ and/or use gtk-doc annotations. -->
</field>
</record>
<class name="TestObj"
+ c:symbol-prefix="test_obj"
c:type="RegressTestObj"
parent="GObject.Object"
glib:type-name="RegressTestObj"
@@ -304,7 +313,7 @@ and/or use gtk-doc annotations. -->
<parameter name="user_data" transfer-ownership="none">
<type name="gpointer" c:type="gpointer"/>
</parameter>
- <parameter name="notify" transfer-ownership="none" scope="call">
+ <parameter name="notify" transfer-ownership="none" scope="async">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</parameter>
</parameters>
@@ -321,6 +330,21 @@ and/or use gtk-doc annotations. -->
</parameter>
</parameters>
</constructor>
+ <function name="null_out" c:identifier="regress_test_obj_null_out">
+ <return-value transfer-ownership="none">
+ <type name="none" c:type="void"/>
+ </return-value>
+ <parameters>
+ <parameter name="obj"
+ direction="out"
+ caller-allocates="0"
+ transfer-ownership="full"
+ allow-none="1">
+ <doc xml:whitespace="preserve">A #RegressTestObj</doc>
+ <type name="TestObj" c:type="RegressTestObj**"/>
+ </parameter>
+ </parameters>
+ </function>
<function name="static_method"
c:identifier="regress_test_obj_static_method">
<return-value transfer-ownership="none">
@@ -399,11 +423,6 @@ case.</doc>
<type name="none" c:type="void"/>
</return-value>
</method>
- <method name="null_out" c:identifier="regress_test_obj_null_out">
- <return-value transfer-ownership="none">
- <type name="none" c:type="void"/>
- </return-value>
- </method>
<method name="set_bare" c:identifier="regress_test_obj_set_bare">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
@@ -487,10 +506,10 @@ case.</doc>
</parameters>
</method>
<property name="bare" writable="1" transfer-ownership="none">
- <type name="GObject.Object" c:type="GObject"/>
+ <type name="GObject.Object"/>
</property>
<property name="boxed" writable="1" transfer-ownership="none">
- <type name="TestBoxed" c:type="RegressTestBoxed"/>
+ <type name="TestBoxed"/>
</property>
<property name="double" writable="1" transfer-ownership="none">
<type name="gdouble"/>
@@ -499,7 +518,7 @@ case.</doc>
<type name="gfloat"/>
</property>
<property name="hash-table" writable="1" transfer-ownership="container">
- <type name="GLib.HashTable" c:type="GHashTable">
+ <type name="GLib.HashTable">
<type name="utf8"/>
<type name="gint8"/>
</type>
@@ -508,7 +527,7 @@ case.</doc>
<type name="gint"/>
</property>
<property name="list" writable="1" transfer-ownership="none">
- <type name="GLib.List" c:type="gpointer">
+ <type name="GLib.List">
<type name="utf8"/>
</type>
</property>
@@ -525,7 +544,10 @@ case.</doc>
<type name="TestBoxed" c:type="RegressTestBoxed*"/>
</field>
<field name="hash_table">
- <type name="GLib.HashTable" c:type="GHashTable*"/>
+ <type name="GLib.HashTable" c:type="GHashTable*">
+ <type name="gpointer" c:type="gpointer"/>
+ <type name="gpointer" c:type="gpointer"/>
+ </type>
</field>
<field name="list">
<type name="GLib.List" c:type="GList*">
@@ -545,17 +567,17 @@ case.</doc>
<type name="utf8" c:type="char*"/>
</field>
<glib:signal name="test">
- <return-value transfer-ownership="full">
- <type name="none" c:type="void"/>
+ <return-value transfer-ownership="none">
+ <type name="none"/>
</return-value>
</glib:signal>
<glib:signal name="test-with-static-scope-arg">
- <return-value transfer-ownership="full">
- <type name="none" c:type="void"/>
+ <return-value transfer-ownership="none">
+ <type name="none"/>
</return-value>
<parameters>
<parameter name="object" transfer-ownership="none">
- <type name="TestSimpleBoxedA" c:type="RegressTestSimpleBoxedA"/>
+ <type name="TestSimpleBoxedA"/>
</parameter>
</parameters>
</glib:signal>
@@ -567,7 +589,7 @@ case.</doc>
<type name="GObject.ObjectClass" c:type="GObjectClass"/>
</field>
<field name="matrix">
- <callback name="matrix" c:type="matrix">
+ <callback name="matrix">
<return-value transfer-ownership="none">
<type name="gint" c:type="int"/>
</return-value>
@@ -592,7 +614,8 @@ case.</doc>
<record name="TestSimpleBoxedA"
c:type="RegressTestSimpleBoxedA"
glib:type-name="RegressTestSimpleBoxedA"
- glib:get-type="regress_test_simple_boxed_a_get_type">
+ glib:get-type="regress_test_simple_boxed_a_get_type"
+ c:symbol-prefix="test_simple_boxed_a">
<field name="some_int" writable="1">
<type name="gint" c:type="gint"/>
</field>
@@ -620,11 +643,18 @@ case.</doc>
</parameter>
</parameters>
</method>
+ <function name="const_return"
+ c:identifier="regress_test_simple_boxed_a_const_return">
+ <return-value transfer-ownership="none">
+ <type name="TestSimpleBoxedA" c:type="RegressTestSimpleBoxedA*"/>
+ </return-value>
+ </function>
</record>
<record name="TestSimpleBoxedB"
c:type="RegressTestSimpleBoxedB"
glib:type-name="RegressTestSimpleBoxedB"
- glib:get-type="regress_test_simple_boxed_b_get_type">
+ glib:get-type="regress_test_simple_boxed_b_get_type"
+ c:symbol-prefix="test_simple_boxed_b">
<field name="some_int8" writable="1">
<type name="gint8" c:type="gint8"/>
</field>
@@ -694,15 +724,8 @@ case.</doc>
</parameters>
</method>
</record>
- <record name="TestStructC" c:type="_RegressTestStructC">
- <field name="another_int" writable="1">
- <type name="gint" c:type="gint"/>
- </field>
- <field name="obj" writable="1">
- <type name="GObject.Object" c:type="GObject*"/>
- </field>
- </record>
<class name="TestSubObj"
+ c:symbol-prefix="test_sub_obj"
c:type="RegressTestSubObj"
parent="TestObj"
glib:type-name="RegressTestSubObj"
@@ -736,6 +759,7 @@ case.</doc>
</field>
</record>
<class name="TestWi8021x"
+ c:symbol-prefix="test_wi_802_1x"
c:type="RegressTestWi8021x"
parent="GObject.Object"
glib:type-name="RegressTestWi8021x"
@@ -746,36 +770,44 @@ case.</doc>
<type name="TestWi8021x" c:type="RegressTestWi8021x*"/>
</return-value>
</constructor>
- <function name="static_method"
- c:identifier="regress_test_wi_802_1x_static_method">
+ <function name="get_testbool"
+ c:identifier="regress_test_wi_802_1x_get_testbool">
<return-value transfer-ownership="none">
- <type name="gint" c:type="int"/>
+ <type name="gboolean" c:type="gboolean"/>
</return-value>
<parameters>
- <parameter name="x" transfer-ownership="none">
- <type name="gint" c:type="int"/>
+ <parameter name="obj" transfer-ownership="none">
+ <type name="TestWi8021x" c:type="RegressTestWi8021x*"/>
</parameter>
</parameters>
</function>
- <method name="get_testbool"
- c:identifier="regress_test_wi_802_1x_get_testbool">
- <return-value transfer-ownership="none">
- <type name="gboolean" c:type="gboolean"/>
- </return-value>
- </method>
- <method name="set_testbool"
- c:identifier="regress_test_wi_802_1x_set_testbool">
+ <function name="set_testbool"
+ c:identifier="regress_test_wi_802_1x_set_testbool">
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
<parameters>
+ <parameter name="obj" transfer-ownership="none">
+ <type name="TestWi8021x" c:type="RegressTestWi8021x*"/>
+ </parameter>
<parameter name="v" transfer-ownership="none">
<type name="gboolean" c:type="gboolean"/>
</parameter>
</parameters>
- </method>
+ </function>
+ <function name="static_method"
+ c:identifier="regress_test_wi_802_1x_static_method">
+ <return-value transfer-ownership="none">
+ <type name="gint" c:type="int"/>
+ </return-value>
+ <parameters>
+ <parameter name="x" transfer-ownership="none">
+ <type name="gint" c:type="int"/>
+ </parameter>
+ </parameters>
+ </function>
<property name="testbool" writable="1" transfer-ownership="none">
- <type name="gboolean" c:type="gboolean"/>
+ <type name="gboolean"/>
</property>
<field name="parent_instance">
<type name="GObject.Object" c:type="GObject"/>
@@ -791,6 +823,14 @@ case.</doc>
<type name="GObject.ObjectClass" c:type="GObjectClass"/>
</field>
</record>
+ <record name="_TestStructC" c:type="_RegressTestStructC">
+ <field name="another_int" writable="1">
+ <type name="gint" c:type="gint"/>
+ </field>
+ <field name="obj" writable="1">
+ <type name="GObject.Object" c:type="GObject*"/>
+ </field>
+ </record>
<function name="set_abort_on_error"
c:identifier="regress_set_abort_on_error">
<return-value transfer-ownership="none">
@@ -1235,7 +1275,7 @@ is invoked.</doc>
<parameter name="user_data" transfer-ownership="none">
<type name="gpointer" c:type="gpointer"/>
</parameter>
- <parameter name="notify" transfer-ownership="none" scope="call">
+ <parameter name="notify" transfer-ownership="none" scope="async">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</parameter>
</parameters>
@@ -1408,7 +1448,7 @@ call and can be released on return.</doc>
c:identifier="regress_test_ghash_nested_everything_return">
<doc xml:whitespace="preserve">Specify nested parameterized types directly with the (type ) annotation.</doc>
<return-value transfer-ownership="full">
- <type name="GLib.HashTable" c:type="GHashTable*">
+ <type name="GLib.HashTable">
<type name="utf8"/>
<type name="GLib.HashTable">
<type name="utf8"/>
@@ -1914,12 +1954,6 @@ call and can be released on return.</doc>
</parameter>
</parameters>
</function>
- <function name="test_simple_boxed_a_const_return"
- c:identifier="regress_test_simple_boxed_a_const_return">
- <return-value transfer-ownership="none">
- <type name="TestSimpleBoxedA" c:type="RegressTestSimpleBoxedA*"/>
- </return-value>
- </function>
<function name="test_simple_callback"
c:identifier="regress_test_simple_callback">
<return-value transfer-ownership="none">
@@ -2124,7 +2158,7 @@ call and can be released on return.</doc>
<parameter name="user_data" transfer-ownership="none">
<type name="gpointer" c:type="gpointer"/>
</parameter>
- <parameter name="notify" transfer-ownership="none" scope="call">
+ <parameter name="notify" transfer-ownership="none" scope="async">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</parameter>
<parameter name="y"
@@ -2279,7 +2313,7 @@ call and can be released on return.</doc>
<type name="none" c:type="void"/>
</return-value>
<parameters>
- <parameter name="in" transfer-ownership="full" allow-none="1">
+ <parameter name="in" transfer-ownership="none" allow-none="1">
<type name="utf8" c:type="char*"/>
</parameter>
</parameters>
@@ -2353,7 +2387,7 @@ call and can be released on return.</doc>
<function name="test_value_get_fundamental_object"
c:identifier="regress_test_value_get_fundamental_object"
introspectable="0">
- <return-value transfer-ownership="full">
+ <return-value>
<type name="TestFundamentalObject"
c:type="RegressTestFundamentalObject*"/>
</return-value>
diff --git a/tests/scanner/TestInherit-1.0-expected.gir b/tests/scanner/TestInherit-1.0-expected.gir
index 4ed155ea..84654543 100644
--- a/tests/scanner/TestInherit-1.0-expected.gir
+++ b/tests/scanner/TestInherit-1.0-expected.gir
@@ -2,7 +2,7 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
@@ -13,8 +13,10 @@ and/or use gtk-doc annotations. -->
<namespace name="TestInherit"
version="1.0"
shared-library="libtestinherit.so"
- c:prefix="TestInherit">
+ c:identifier-prefixes="TestInherit"
+ c:symbol-prefixes="test_inherit">
<class name="Drawable"
+ c:symbol-prefix="drawable"
c:type="TestInheritDrawable"
parent="GObject.Object"
abstract="1"
diff --git a/tests/scanner/Utility-1.0-expected.gir b/tests/scanner/Utility-1.0-expected.gir
index f4c69092..c09c056b 100644
--- a/tests/scanner/Utility-1.0-expected.gir
+++ b/tests/scanner/Utility-1.0-expected.gir
@@ -2,7 +2,7 @@
<!-- This file was automatically generated from C sources - DO NOT EDIT!
To affect the contents of this file, edit the original C definitions,
and/or use gtk-doc annotations. -->
-<repository version="1.1"
+<repository version="1.2"
xmlns="http://www.gtk.org/introspection/core/1.0"
xmlns:c="http://www.gtk.org/introspection/c/1.0"
xmlns:glib="http://www.gtk.org/introspection/glib/1.0">
@@ -12,13 +12,14 @@ and/or use gtk-doc annotations. -->
<namespace name="Utility"
version="1.0"
shared-library="libutility.so"
- c:prefix="Utility">
+ c:identifier-prefixes="Utility"
+ c:symbol-prefixes="utility">
<alias name="Glyph" c:type="UtilityGlyph">
- <type name="guint32"/>
+ <type name="guint32" c:type="guint32"/>
</alias>
<record name="Buffer" c:type="UtilityBuffer">
<field name="data" writable="1">
- <type name="gpointer" c:type="char*"/>
+ <type name="gpointer"/>
</field>
<field name="length" writable="1">
<type name="gulong" c:type="gsize"/>
@@ -61,6 +62,7 @@ and/or use gtk-doc annotations. -->
<member name="c" value="4" c:identifier="UTILITY_FLAG_C"/>
</bitfield>
<class name="Object"
+ c:symbol-prefix="object"
c:type="UtilityObject"
parent="GObject.Object"
glib:type-name="UtilityObject"
@@ -77,14 +79,14 @@ and/or use gtk-doc annotations. -->
<parameter name="func"
transfer-ownership="none"
scope="notified"
- closure="3"
- destroy="4">
+ closure="2"
+ destroy="3">
<type name="FileFunc" c:type="UtilityFileFunc"/>
</parameter>
<parameter name="user_data" transfer-ownership="none">
<type name="gpointer" c:type="gpointer"/>
</parameter>
- <parameter name="destroy" transfer-ownership="none" scope="call">
+ <parameter name="destroy" transfer-ownership="none" scope="async">
<type name="GLib.DestroyNotify" c:type="GDestroyNotify"/>
</parameter>
</parameters>
@@ -112,7 +114,7 @@ and/or use gtk-doc annotations. -->
</field>
<field name="data" writable="1">
<array zero-terminated="0" c:type="guint8" fixed-size="16">
- <type name="guint8"/>
+ <type name="guint8" c:type="guint8"/>
</array>
</field>
</record>