glib/gdump.c
Johan Dahlin b6d50e2951 Add support for non-GObject fundamental objects
This patch adds support for instantiable fundamental object types,
which are not GObject based. This is mostly interesting for being
able to support GstMiniObject's which are extensivly used in GStreamer.
Includes a big test case to the Everything module (inspired by
GstMiniObject) which should be used by language bindings who wishes to
test this functionallity.

This patch increases the size of the typelib and breaks compatibility
with older typelibs.

https://bugzilla.gnome.org/show_bug.cgi?id=568913
2010-07-09 10:15:45 -03:00

467 lines
12 KiB
C

/* GObject introspection: Dump introspection data
*
* Copyright (C) 2008 Colin Walters <walters@verbum.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include <stdlib.h>
#include <glib.h>
#include <glib-object.h>
#include <gio/gio.h>
#include "girepository.h"
#include "config.h"
#include <string.h>
static void
escaped_printf (GOutputStream *out, const char *fmt, ...)
{
char *str;
va_list args;
gsize written;
GError *error = NULL;
va_start (args, fmt);
str = g_markup_vprintf_escaped (fmt, args);
if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
{
g_critical ("failed to write to iochannel: %s", error->message);
g_clear_error (&error);
}
g_free (str);
va_end (args);
}
static void
goutput_write (GOutputStream *out, const char *str)
{
gsize written;
GError *error = NULL;
if (!g_output_stream_write_all (out, str, strlen (str), &written, NULL, &error))
{
g_critical ("failed to write to iochannel: %s", error->message);
g_clear_error (&error);
}
}
typedef GType (*GetTypeFunc)(void);
static GType
invoke_get_type (GModule *self, const char *symbol, GError **error)
{
GetTypeFunc sym;
if (!g_module_symbol (self, symbol, (void**)&sym))
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Failed to find symbol '%s'", symbol);
return G_TYPE_INVALID;
}
return sym ();
}
static void
dump_properties (GType type, GOutputStream *out)
{
guint i;
guint n_properties;
GParamSpec **props;
if (G_TYPE_FUNDAMENTAL (type) == G_TYPE_OBJECT)
{
GObjectClass *klass;
klass = g_type_class_ref (type);
props = g_object_class_list_properties (klass, &n_properties);
}
else
{
void *klass;
klass = g_type_default_interface_ref (type);
props = g_object_interface_list_properties (klass, &n_properties);
}
for (i = 0; i < n_properties; i++)
{
GParamSpec *prop;
prop = props[i];
if (prop->owner_type != type)
continue;
escaped_printf (out, " <property name=\"%s\" type=\"%s\" flags=\"%d\"/>\n",
prop->name, g_type_name (prop->value_type), prop->flags);
}
g_free (props);
}
static void
dump_signals (GType type, GOutputStream *out)
{
guint i;
guint n_sigs;
guint *sig_ids;
sig_ids = g_signal_list_ids (type, &n_sigs);
for (i = 0; i < n_sigs; i++)
{
guint sigid;
GSignalQuery query;
guint j;
sigid = sig_ids[i];
g_signal_query (sigid, &query);
escaped_printf (out, " <signal name=\"%s\" return=\"%s\">\n",
query.signal_name, g_type_name (query.return_type));
for (j = 0; j < query.n_params; j++)
{
escaped_printf (out, " <param type=\"%s\"/>\n",
g_type_name (query.param_types[j]));
}
goutput_write (out, " </signal>\n");
}
}
static void
dump_object_type (GType type, const char *symbol, GOutputStream *out)
{
guint n_interfaces;
guint i;
GType *interfaces;
escaped_printf (out, " <class name=\"%s\" get-type=\"%s\"",
g_type_name (type), symbol);
if (type != G_TYPE_OBJECT)
{
GString *parent_str;
GType parent;
gboolean first = TRUE;
parent = type;
parent_str = g_string_new ("");
do
{
parent = g_type_parent (parent);
if (first)
first = FALSE;
else
g_string_append_c (parent_str, ',');
g_string_append (parent_str, g_type_name (parent));
} while (parent != G_TYPE_OBJECT && parent != G_TYPE_INVALID);
escaped_printf (out, " parents=\"%s\"", parent_str->str);
g_string_free (parent_str, TRUE);
}
if (G_TYPE_IS_ABSTRACT (type))
escaped_printf (out, " abstract=\"1\"");
goutput_write (out, ">\n");
interfaces = g_type_interfaces (type, &n_interfaces);
for (i = 0; i < n_interfaces; i++)
{
GType itype = interfaces[i];
escaped_printf (out, " <implements name=\"%s\"/>\n",
g_type_name (itype));
}
dump_properties (type, out);
dump_signals (type, out);
goutput_write (out, " </class>\n");
}
static void
dump_interface_type (GType type, const char *symbol, GOutputStream *out)
{
guint n_interfaces;
guint i;
GType *interfaces;
escaped_printf (out, " <interface name=\"%s\" get-type=\"%s\">\n",
g_type_name (type), symbol);
interfaces = g_type_interface_prerequisites (type, &n_interfaces);
for (i = 0; i < n_interfaces; i++)
{
GType itype = interfaces[i];
if (itype == G_TYPE_OBJECT)
{
/* Treat this as implicit for now; in theory GInterfaces are
* supported on things like GstMiniObject, but right now
* the introspection system only supports GObject.
* http://bugzilla.gnome.org/show_bug.cgi?id=559706
*/
continue;
}
escaped_printf (out, " <prerequisite name=\"%s\"/>\n",
g_type_name (itype));
}
dump_properties (type, out);
dump_signals (type, out);
goutput_write (out, " </interface>\n");
}
static void
dump_boxed_type (GType type, const char *symbol, GOutputStream *out)
{
escaped_printf (out, " <boxed name=\"%s\" get-type=\"%s\"/>\n",
g_type_name (type), symbol);
}
static void
dump_flags_type (GType type, const char *symbol, GOutputStream *out)
{
guint i;
GFlagsClass *klass;
klass = g_type_class_ref (type);
escaped_printf (out, " <flags name=\"%s\" get-type=\"%s\">\n",
g_type_name (type), symbol);
for (i = 0; i < klass->n_values; i++)
{
GFlagsValue *value = &(klass->values[i]);
escaped_printf (out, " <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
value->value_name, value->value_nick, value->value);
}
goutput_write (out, " </flags>\n");
}
static void
dump_enum_type (GType type, const char *symbol, GOutputStream *out)
{
guint i;
GEnumClass *klass;
klass = g_type_class_ref (type);
escaped_printf (out, " <enum name=\"%s\" get-type=\"%s\">\n",
g_type_name (type), symbol);
for (i = 0; i < klass->n_values; i++)
{
GEnumValue *value = &(klass->values[i]);
escaped_printf (out, " <member name=\"%s\" nick=\"%s\" value=\"%d\"/>\n",
value->value_name, value->value_nick, value->value);
}
goutput_write (out, " </enum>");
}
static void
dump_fundamental_type (GType type, const char *symbol, GOutputStream *out)
{
guint n_interfaces;
guint i;
GType *interfaces;
GString *parent_str;
GType parent;
gboolean first = TRUE;
escaped_printf (out, " <fundamental name=\"%s\" get-type=\"%s\"",
g_type_name (type), symbol);
if (G_TYPE_IS_ABSTRACT (type))
escaped_printf (out, " abstract=\"1\"");
if (G_TYPE_IS_INSTANTIATABLE (type))
escaped_printf (out, " instantiatable=\"1\"");
parent = type;
parent_str = g_string_new ("");
do
{
parent = g_type_parent (parent);
if (first)
first = FALSE;
else
g_string_append_c (parent_str, ',');
if (!g_type_name (parent))
break;
g_string_append (parent_str, g_type_name (parent));
} while (parent != G_TYPE_INVALID);
if (parent_str->len > 0)
escaped_printf (out, " parents=\"%s\"", parent_str->str);
g_string_free (parent_str, TRUE);
goutput_write (out, ">\n");
interfaces = g_type_interfaces (type, &n_interfaces);
for (i = 0; i < n_interfaces; i++)
{
GType itype = interfaces[i];
escaped_printf (out, " <implements name=\"%s\"/>\n",
g_type_name (itype));
}
goutput_write (out, " </fundamental>\n");
}
static void
dump_type (GType type, const char *symbol, GOutputStream *out)
{
switch (g_type_fundamental (type))
{
case G_TYPE_OBJECT:
dump_object_type (type, symbol, out);
break;
case G_TYPE_INTERFACE:
dump_interface_type (type, symbol, out);
break;
case G_TYPE_BOXED:
dump_boxed_type (type, symbol, out);
break;
case G_TYPE_FLAGS:
dump_flags_type (type, symbol, out);
break;
case G_TYPE_ENUM:
dump_enum_type (type, symbol, out);
break;
case G_TYPE_POINTER:
/* GValue, etc. Just skip them. */
break;
default:
dump_fundamental_type (type, symbol, out);
break;
}
}
/**
* g_irepository_dump:
* @arg: Comma-separated pair of input and output filenames
* @error: a %GError
*
* Argument specified is a comma-separated pair of filenames; i.e. of
* the form "input.txt,output.xml". The input file should be a
* UTF-8 Unix-line-ending text file, with each line containing the name
* of a GType _get_type function.
*
* The output file should already exist, but be empty. This function will
* overwrite its contents.
*
* Returns: %TRUE on success, %FALSE on error
*/
gboolean
g_irepository_dump (const char *arg, GError **error)
{
GHashTable *output_types;
char **args;
GFile *input_file;
GFile *output_file;
GFileInputStream *input;
GFileOutputStream *output;
GDataInputStream *in;
GModule *self;
gboolean caught_error = FALSE;
self = g_module_open (NULL, 0);
if (!self)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"failed to open self: %s",
g_module_error ());
return FALSE;
}
args = g_strsplit (arg, ",", 2);
input_file = g_file_new_for_path (args[0]);
output_file = g_file_new_for_path (args[1]);
input = g_file_read (input_file, NULL, error);
if (input == NULL)
return FALSE;
output = g_file_replace (output_file, NULL, FALSE, 0, NULL, error);
if (output == NULL)
{
g_input_stream_close (G_INPUT_STREAM (input), NULL, NULL);
return FALSE;
}
goutput_write (G_OUTPUT_STREAM (output), "<?xml version=\"1.0\"?>\n");
goutput_write (G_OUTPUT_STREAM (output), "<dump>\n");
output_types = g_hash_table_new (NULL, NULL);
in = g_data_input_stream_new (G_INPUT_STREAM (input));
g_object_unref (input);
while (TRUE)
{
gsize len;
char *line = g_data_input_stream_read_line (in, &len, NULL, NULL);
GType type;
if (line == NULL || *line == '\0')
{
g_free (line);
break;
}
g_strchomp (line);
type = invoke_get_type (self, line, error);
if (type == G_TYPE_INVALID)
{
g_printerr ("Invalid GType: '%s'\n", line);
caught_error = TRUE;
g_free (line);
break;
}
if (g_hash_table_lookup (output_types, (gpointer) type))
goto next;
g_hash_table_insert (output_types, (gpointer) type, (gpointer) type);
dump_type (type, line, G_OUTPUT_STREAM (output));
next:
g_free (line);
}
g_hash_table_destroy (output_types);
goutput_write (G_OUTPUT_STREAM (output), "</dump>\n");
{
GError **ioerror;
/* Avoid overwriting an earlier set error */
if (caught_error)
ioerror = NULL;
else
ioerror = error;
if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, ioerror))
return FALSE;
if (!g_output_stream_close (G_OUTPUT_STREAM (output), NULL, ioerror))
return FALSE;
}
return !caught_error;
}