glib/gdump.c
Dan Winship 215d83ebb0 Switch to storing string form of error quarks
Instead of storing the name of the function to call to get the
error quark, store the string form of the error quark, which
we derive from the introspection binary during scanning.

Update EnumBlob and GIEnumInfo to include the new information.

This will allow determining a back-mapping from error quark
to error domain without having to dlsym() and call all the
known error quark functions.

Based on earlier patches from Owen Taylor and Maxim Ermilov.

https://bugzilla.gnome.org/show_bug.cgi?id=602516
2011-08-13 09:42:37 -03:00

538 lines
13 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>
/* This file is both compiled into libgirepository.so, and installed
* on the filesystem. But for the dumper, we want to avoid linking
* to libgirepository; see
* https://bugzilla.gnome.org/show_bug.cgi?id=630342
*/
#ifdef G_IREPOSITORY_COMPILATION
#include "config.h"
#include "girepository.h"
#endif
#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);
typedef GQuark (*ErrorQuarkFunc)(void);
static GType
invoke_get_type (GModule *self, const char *symbol, GError **error)
{
GetTypeFunc sym;
GType ret;
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;
}
ret = sym ();
if (ret == G_TYPE_INVALID)
{
g_set_error (error,
G_IO_ERROR,
G_IO_ERROR_FAILED,
"Function '%s' returned G_TYPE_INVALID", symbol);
}
return ret;
}
static GQuark
invoke_error_quark (GModule *self, const char *symbol, GError **error)
{
ErrorQuarkFunc 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 = g_type_parent (type);
parent_str = g_string_new ("");
while (parent != G_TYPE_INVALID)
{
if (first)
first = FALSE;
else
g_string_append_c (parent_str, ',');
g_string_append (parent_str, g_type_name (parent));
parent = g_type_parent (parent);
}
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 = g_type_parent (type);
parent_str = g_string_new ("");
while (parent != G_TYPE_INVALID)
{
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));
parent = g_type_parent (parent);
}
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;
}
}
static void
dump_error_quark (GQuark quark, const char *symbol, GOutputStream *out)
{
escaped_printf (out, " <error-quark function=\"%s\" domain=\"%s\"/>\n",
symbol, g_quark_to_string (quark));
}
/**
* 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
*/
#ifndef G_IREPOSITORY_COMPILATION
static gboolean
dump_irepository (const char *arg, GError **error)
#else
gboolean
g_irepository_dump (const char *arg, GError **error)
#endif
{
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);
const char *function;
if (line == NULL || *line == '\0')
{
g_free (line);
break;
}
g_strchomp (line);
if (strncmp (line, "get-type:", strlen ("get-type:")) == 0)
{
GType type;
function = line + strlen ("get-type:");
type = invoke_get_type (self, function, error);
if (type == G_TYPE_INVALID)
{
g_printerr ("Invalid GType function: '%s'\n", function);
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, function, G_OUTPUT_STREAM (output));
}
else if (strncmp (line, "error-quark:", strlen ("error-quark:")) == 0)
{
GQuark quark;
function = line + strlen ("error-quark:");
quark = invoke_error_quark (self, function, error);
if (quark == 0)
{
g_printerr ("Invalid error quark function: '%s'\n", function);
caught_error = TRUE;
g_free (line);
break;
}
dump_error_quark (quark, function, 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;
}