glib/girepository/girparser.c
Simon McVittie 3f6de0706e girparser: Don't rely on gi-compile-repository exporting debug level
It seems cleaner to store this in the parser, rather than having the
compiler export a global variable that the parser must read.

Signed-off-by: Simon McVittie <smcv@collabora.com>
2024-02-14 10:36:34 +00:00

3865 lines
114 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
* GObject introspection: A parser for the XML GIR format
*
* Copyright (C) 2005 Matthias Clasen
* Copyright (C) 2008 Philip Van Hoof
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* 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 "config.h"
#include "girparser-private.h"
#include "girnode-private.h"
#include "gitypelib-internal.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
/* This is a "major" version in the sense that it's only bumped
* for incompatible changes.
*/
#define SUPPORTED_GIR_VERSION "1.2"
#ifdef G_OS_WIN32
#include <windows.h>
#ifdef GIR_DIR
#undef GIR_DIR
#endif
/* GIR_DIR is used only in code called just once,
* so no problem leaking this
*/
#define GIR_DIR \
g_build_filename (g_win32_get_package_installation_directory_of_module(NULL), \
"share", \
GIR_SUFFIX, \
NULL)
#endif
struct _GIIrParser
{
char **includes;
char **gi_gir_path;
GList *parsed_modules; /* All previously parsed modules */
GLogLevelFlags logged_levels;
};
typedef enum
{
STATE_NONE = 0,
STATE_START,
STATE_END,
STATE_REPOSITORY,
STATE_INCLUDE,
STATE_C_INCLUDE, /* 5 */
STATE_PACKAGE,
STATE_NAMESPACE,
STATE_ENUM,
STATE_BITFIELD,
STATE_FUNCTION, /* 10 */
STATE_FUNCTION_RETURN,
STATE_FUNCTION_PARAMETERS,
STATE_FUNCTION_PARAMETER,
STATE_CLASS,
STATE_CLASS_FIELD, /* 15 */
STATE_CLASS_PROPERTY,
STATE_INTERFACE,
STATE_INTERFACE_PROPERTY,
STATE_INTERFACE_FIELD,
STATE_IMPLEMENTS, /* 20 */
STATE_PREREQUISITE,
STATE_BOXED,
STATE_BOXED_FIELD,
STATE_STRUCT,
STATE_STRUCT_FIELD, /* 25 */
STATE_UNION,
STATE_UNION_FIELD,
STATE_NAMESPACE_CONSTANT,
STATE_CLASS_CONSTANT,
STATE_INTERFACE_CONSTANT, /* 30 */
STATE_ALIAS,
STATE_TYPE,
STATE_ATTRIBUTE,
STATE_PASSTHROUGH
} ParseState;
typedef struct _ParseContext ParseContext;
struct _ParseContext
{
GIIrParser *parser;
ParseState state;
int unknown_depth;
ParseState prev_state;
GList *modules;
GList *include_modules;
GList *dependencies;
GHashTable *aliases;
GHashTable *disguised_structures;
GHashTable *pointer_structures;
const char *file_path;
const char *namespace;
const char *c_prefix;
GIIrModule *current_module;
GSList *node_stack;
char *current_alias;
GIIrNode *current_typed;
GList *type_stack;
GList *type_parameters;
int type_depth;
ParseState in_embedded_state;
};
#define CURRENT_NODE(ctx) ((GIIrNode *)((ctx)->node_stack->data))
static void start_element_handler (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
void *user_data,
GError **error);
static void end_element_handler (GMarkupParseContext *context,
const char *element_name,
void *user_data,
GError **error);
static void text_handler (GMarkupParseContext *context,
const char *text,
gsize text_len,
void *user_data,
GError **error);
static void cleanup (GMarkupParseContext *context,
GError *error,
void *user_data);
static void state_switch (ParseContext *ctx, ParseState newstate);
static GMarkupParser markup_parser =
{
start_element_handler,
end_element_handler,
text_handler,
NULL,
cleanup
};
static gboolean
start_alias (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error);
static gboolean
start_type (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error);
static const char *find_attribute (const char *name,
const char **attribute_names,
const char **attribute_values);
GIIrParser *
gi_ir_parser_new (void)
{
GIIrParser *parser = g_slice_new0 (GIIrParser);
const char *gi_gir_path = g_getenv ("GI_GIR_PATH");
if (gi_gir_path != NULL)
parser->gi_gir_path = g_strsplit (gi_gir_path, G_SEARCHPATH_SEPARATOR_S, 0);
parser->logged_levels = G_LOG_LEVEL_MASK & ~(G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_DEBUG);
return parser;
}
void
gi_ir_parser_set_debug (GIIrParser *parser,
GLogLevelFlags logged_levels)
{
parser->logged_levels = logged_levels;
}
void
gi_ir_parser_free (GIIrParser *parser)
{
GList *l;
g_strfreev (parser->includes);
g_strfreev (parser->gi_gir_path);
for (l = parser->parsed_modules; l; l = l->next)
gi_ir_module_free (l->data);
g_slice_free (GIIrParser, parser);
}
void
gi_ir_parser_set_includes (GIIrParser *parser,
const char *const *includes)
{
g_strfreev (parser->includes);
parser->includes = g_strdupv ((char **)includes);
}
static void
firstpass_start_element_handler (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
void *user_data,
GError **error)
{
ParseContext *ctx = user_data;
if (strcmp (element_name, "alias") == 0)
{
start_alias (context, element_name, attribute_names, attribute_values,
ctx, error);
}
else if (ctx->state == STATE_ALIAS && strcmp (element_name, "type") == 0)
{
start_type (context, element_name, attribute_names, attribute_values,
ctx, error);
}
else if (strcmp (element_name, "record") == 0)
{
const char *name;
const char *disguised;
const char *pointer;
name = find_attribute ("name", attribute_names, attribute_values);
disguised = find_attribute ("disguised", attribute_names, attribute_values);
pointer = find_attribute ("pointer", attribute_names, attribute_values);
if (g_strcmp0 (pointer, "1") == 0)
{
char *key;
key = g_strdup_printf ("%s.%s", ctx->namespace, name);
g_hash_table_replace (ctx->pointer_structures, key, GINT_TO_POINTER (1));
}
else if (g_strcmp0 (disguised, "1") == 0)
{
char *key;
key = g_strdup_printf ("%s.%s", ctx->namespace, name);
g_hash_table_replace (ctx->disguised_structures, key, GINT_TO_POINTER (1));
}
}
}
static void
firstpass_end_element_handler (GMarkupParseContext *context,
const char *element_name,
gpointer user_data,
GError **error)
{
ParseContext *ctx = user_data;
if (strcmp (element_name, "alias") == 0)
{
state_switch (ctx, STATE_NAMESPACE);
g_free (ctx->current_alias);
ctx->current_alias = NULL;
}
else if (strcmp (element_name, "type") == 0 && ctx->state == STATE_TYPE)
state_switch (ctx, ctx->prev_state);
}
static GMarkupParser firstpass_parser =
{
firstpass_start_element_handler,
firstpass_end_element_handler,
NULL,
NULL,
NULL,
};
static char *
locate_gir (GIIrParser *parser,
const char *girname)
{
const char *const *datadirs;
const char *const *dir;
char *path = NULL;
g_debug ("Looking for %s", girname);
datadirs = g_get_system_data_dirs ();
if (parser->includes != NULL)
{
for (dir = (const char *const *)parser->includes; *dir; dir++)
{
path = g_build_filename (*dir, girname, NULL);
g_debug ("Trying %s from includes", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
}
}
if (parser->gi_gir_path != NULL)
{
for (dir = (const char *const *) parser->gi_gir_path; *dir; dir++)
{
if (**dir == '\0')
continue;
path = g_build_filename (*dir, girname, NULL);
g_debug ("Trying %s from GI_GIR_PATH", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
}
}
path = g_build_filename (g_get_user_data_dir (), GIR_SUFFIX, girname, NULL);
g_debug ("Trying %s from user data dir", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
for (dir = datadirs; *dir; dir++)
{
path = g_build_filename (*dir, GIR_SUFFIX, girname, NULL);
g_debug ("Trying %s from system data dirs", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
}
path = g_build_filename (GIR_DIR, girname, NULL);
g_debug ("Trying %s from GIR_DIR", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
path = g_build_filename (GOBJECT_INTROSPECTION_DATADIR, GIR_SUFFIX, girname, NULL);
g_debug ("Trying %s from DATADIR", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
#ifdef G_OS_UNIX
path = g_build_filename ("/usr/share", GIR_SUFFIX, girname, NULL);
g_debug ("Trying %s", path);
if (g_file_test (path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_REGULAR))
return g_steal_pointer (&path);
g_clear_pointer (&path, g_free);
#endif
g_debug ("Did not find %s", girname);
return NULL;
}
#define MISSING_ATTRIBUTE(context,error,element,attribute) \
do { \
int line_number, char_number; \
g_markup_parse_context_get_position (context, &line_number, &char_number); \
g_set_error (error, \
G_MARKUP_ERROR, \
G_MARKUP_ERROR_INVALID_CONTENT, \
"Line %d, character %d: The attribute '%s' on the element '%s' must be specified", \
line_number, char_number, attribute, element); \
} while (0)
static const char *
find_attribute (const char *name,
const char **attribute_names,
const char **attribute_values)
{
size_t i;
for (i = 0; attribute_names[i] != NULL; i++)
if (strcmp (attribute_names[i], name) == 0)
return attribute_values[i];
return 0;
}
static void
state_switch (ParseContext *ctx, ParseState newstate)
{
g_assert (ctx->state != newstate);
ctx->prev_state = ctx->state;
ctx->state = newstate;
if (ctx->state == STATE_PASSTHROUGH)
ctx->unknown_depth = 1;
}
static GIIrNode *
pop_node (ParseContext *ctx)
{
GSList *top;
GIIrNode *node;
g_assert (ctx->node_stack != 0);
top = ctx->node_stack;
node = top->data;
g_debug ("popping node %d %s", node->type, node->name);
ctx->node_stack = top->next;
g_slist_free_1 (top);
return node;
}
static void
push_node (ParseContext *ctx, GIIrNode *node)
{
g_assert (node != NULL);
g_debug ("pushing node %d %s", node->type, node->name);
ctx->node_stack = g_slist_prepend (ctx->node_stack, node);
}
static GIIrNodeType * parse_type_internal (GIIrModule *module,
const char *str,
char **next,
gboolean in_glib,
gboolean in_gobject);
typedef struct {
const char *str;
size_t size;
unsigned int is_signed : 1;
} IntegerAliasInfo;
static IntegerAliasInfo integer_aliases[] = {
{ "gchar", SIZEOF_CHAR, 1 },
{ "guchar", SIZEOF_CHAR, 0 },
{ "gshort", SIZEOF_SHORT, 1 },
{ "gushort", SIZEOF_SHORT, 0 },
{ "gint", SIZEOF_INT, 1 },
{ "guint", SIZEOF_INT, 0 },
{ "glong", SIZEOF_LONG, 1 },
{ "gulong", SIZEOF_LONG, 0 },
{ "gssize", GLIB_SIZEOF_SIZE_T, 1 },
{ "gsize", GLIB_SIZEOF_SIZE_T, 0 },
{ "gintptr", GLIB_SIZEOF_SIZE_T, 1 },
{ "guintptr", GLIB_SIZEOF_SIZE_T, 0 },
};
typedef struct {
const char *str;
int tag;
gboolean pointer;
} BasicTypeInfo;
#define BASIC_TYPE_FIXED_OFFSET 3
static BasicTypeInfo basic_types[] = {
{ "none", GI_TYPE_TAG_VOID, 0 },
{ "gpointer", GI_TYPE_TAG_VOID, 1 },
{ "gboolean", GI_TYPE_TAG_BOOLEAN, 0 },
{ "gint8", GI_TYPE_TAG_INT8, 0 }, /* Start of BASIC_TYPE_FIXED_OFFSET */
{ "guint8", GI_TYPE_TAG_UINT8, 0 },
{ "gint16", GI_TYPE_TAG_INT16, 0 },
{ "guint16", GI_TYPE_TAG_UINT16, 0 },
{ "gint32", GI_TYPE_TAG_INT32, 0 },
{ "guint32", GI_TYPE_TAG_UINT32, 0 },
{ "gint64", GI_TYPE_TAG_INT64, 0 },
{ "guint64", GI_TYPE_TAG_UINT64, 0 },
{ "gfloat", GI_TYPE_TAG_FLOAT, 0 },
{ "gdouble", GI_TYPE_TAG_DOUBLE, 0 },
{ "GType", GI_TYPE_TAG_GTYPE, 0 },
{ "utf8", GI_TYPE_TAG_UTF8, 1 },
{ "filename", GI_TYPE_TAG_FILENAME,1 },
{ "gunichar", GI_TYPE_TAG_UNICHAR, 0 },
};
static const BasicTypeInfo *
parse_basic (const char *str)
{
size_t i;
size_t n_basic = G_N_ELEMENTS (basic_types);
for (i = 0; i < n_basic; i++)
{
if (strcmp (str, basic_types[i].str) == 0)
return &(basic_types[i]);
}
for (i = 0; i < G_N_ELEMENTS (integer_aliases); i++)
{
if (strcmp (str, integer_aliases[i].str) == 0)
{
switch (integer_aliases[i].size)
{
case sizeof (uint8_t):
if (integer_aliases[i].is_signed)
return &basic_types[BASIC_TYPE_FIXED_OFFSET];
else
return &basic_types[BASIC_TYPE_FIXED_OFFSET+1];
break;
case sizeof (uint16_t):
if (integer_aliases[i].is_signed)
return &basic_types[BASIC_TYPE_FIXED_OFFSET+2];
else
return &basic_types[BASIC_TYPE_FIXED_OFFSET+3];
break;
case sizeof (uint32_t):
if (integer_aliases[i].is_signed)
return &basic_types[BASIC_TYPE_FIXED_OFFSET+4];
else
return &basic_types[BASIC_TYPE_FIXED_OFFSET+5];
break;
case sizeof (uint64_t):
if (integer_aliases[i].is_signed)
return &basic_types[BASIC_TYPE_FIXED_OFFSET+6];
else
return &basic_types[BASIC_TYPE_FIXED_OFFSET+7];
break;
default:
g_assert_not_reached ();
}
}
}
return NULL;
}
static GIIrNodeType *
parse_type_internal (GIIrModule *module,
const char *str,
char **next,
gboolean in_glib,
gboolean in_gobject)
{
const BasicTypeInfo *basic;
GIIrNodeType *type;
char *temporary_type = NULL;
type = (GIIrNodeType *)gi_ir_node_new (GI_IR_NODE_TYPE, module);
type->unparsed = g_strdup (str);
/* See comment below on GLib.List handling */
if (in_gobject && strcmp (str, "Type") == 0)
{
temporary_type = g_strdup ("GLib.Type");
str = temporary_type;
}
basic = parse_basic (str);
if (basic != NULL)
{
type->is_basic = TRUE;
type->tag = basic->tag;
type->is_pointer = basic->pointer;
str += strlen(basic->str);
}
else if (in_glib)
{
/* If we're inside GLib, handle "List" etc. by prefixing with
* "GLib." so the parsing code below doesn't have to get more
* special.
*/
if (g_str_has_prefix (str, "List<") ||
strcmp (str, "List") == 0)
{
temporary_type = g_strdup_printf ("GLib.List%s", str + 4);
str = temporary_type;
}
else if (g_str_has_prefix (str, "SList<") ||
strcmp (str, "SList") == 0)
{
temporary_type = g_strdup_printf ("GLib.SList%s", str + 5);
str = temporary_type;
}
else if (g_str_has_prefix (str, "HashTable<") ||
strcmp (str, "HashTable") == 0)
{
temporary_type = g_strdup_printf ("GLib.HashTable%s", str + 9);
str = temporary_type;
}
else if (g_str_has_prefix (str, "Error<") ||
strcmp (str, "Error") == 0)
{
temporary_type = g_strdup_printf ("GLib.Error%s", str + 5);
str = temporary_type;
}
}
if (basic != NULL)
/* found a basic type */;
else if (g_str_has_prefix (str, "GLib.List") ||
g_str_has_prefix (str, "GLib.SList"))
{
str += strlen ("GLib.");
if (g_str_has_prefix (str, "List"))
{
type->tag = GI_TYPE_TAG_GLIST;
type->is_glist = TRUE;
type->is_pointer = TRUE;
str += strlen ("List");
}
else
{
type->tag = GI_TYPE_TAG_GSLIST;
type->is_gslist = TRUE;
type->is_pointer = TRUE;
str += strlen ("SList");
}
}
else if (g_str_has_prefix (str, "GLib.HashTable"))
{
str += strlen ("GLib.");
type->tag = GI_TYPE_TAG_GHASH;
type->is_ghashtable = TRUE;
type->is_pointer = TRUE;
str += strlen ("HashTable");
}
else if (g_str_has_prefix (str, "GLib.Error"))
{
str += strlen ("GLib.");
type->tag = GI_TYPE_TAG_ERROR;
type->is_error = TRUE;
type->is_pointer = TRUE;
str += strlen ("Error");
if (*str == '<')
{
char *tmp, *end;
(str)++;
end = strchr (str, '>');
tmp = g_strndup (str, end - str);
type->errors = g_strsplit (tmp, ",", 0);
g_free (tmp);
str = end;
}
}
else
{
const char *start;
type->tag = GI_TYPE_TAG_INTERFACE;
type->is_interface = TRUE;
start = str;
/* must be an interface type */
while (g_ascii_isalnum (*str) ||
*str == '.' ||
*str == '-' ||
*str == '_' ||
*str == ':')
(str)++;
type->giinterface = g_strndup (start, str - start);
}
if (next)
*next = (char*)str;
g_assert (type->tag >= 0 && type->tag < GI_TYPE_TAG_N_TYPES);
g_free (temporary_type);
return type;
/* error: */
gi_ir_node_free ((GIIrNode *)type);
g_free (temporary_type);
return NULL;
}
static const char *
resolve_aliases (ParseContext *ctx, const char *type)
{
void *orig;
void *value;
GSList *seen_values = NULL;
const char *lookup;
char *prefixed;
if (strchr (type, '.') == NULL)
{
prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type);
lookup = prefixed;
}
else
{
lookup = type;
prefixed = NULL;
}
seen_values = g_slist_prepend (seen_values, (char*)lookup);
while (g_hash_table_lookup_extended (ctx->current_module->aliases, lookup, &orig, &value))
{
g_debug ("Resolved: %s => %s", lookup, (char*)value);
lookup = value;
if (g_slist_find_custom (seen_values, lookup,
(GCompareFunc)strcmp) != NULL)
break;
seen_values = g_slist_prepend (seen_values, (char*) lookup);
}
g_slist_free (seen_values);
if (lookup == prefixed)
lookup = type;
g_free (prefixed);
return lookup;
}
static void
is_pointer_or_disguised_structure (ParseContext *ctx,
const char *type,
gboolean *is_pointer,
gboolean *is_disguised)
{
const char *lookup;
char *prefixed;
if (strchr (type, '.') == NULL)
{
prefixed = g_strdup_printf ("%s.%s", ctx->namespace, type);
lookup = prefixed;
}
else
{
lookup = type;
prefixed = NULL;
}
if (is_pointer != NULL)
*is_pointer = g_hash_table_lookup (ctx->current_module->pointer_structures, lookup) != NULL;
if (is_disguised != NULL)
*is_disguised = g_hash_table_lookup (ctx->current_module->disguised_structures, lookup) != NULL;
g_free (prefixed);
}
static GIIrNodeType *
parse_type (ParseContext *ctx, const char *type)
{
GIIrNodeType *node;
const BasicTypeInfo *basic;
gboolean in_glib, in_gobject;
in_glib = strcmp (ctx->namespace, "GLib") == 0;
in_gobject = strcmp (ctx->namespace, "GObject") == 0;
/* Do not search aliases for basic types */
basic = parse_basic (type);
if (basic == NULL)
type = resolve_aliases (ctx, type);
node = parse_type_internal (ctx->current_module, type, NULL, in_glib, in_gobject);
if (node)
g_debug ("Parsed type: %s => %d", type, node->tag);
else
g_critical ("Failed to parse type: '%s'", type);
return node;
}
static gboolean
introspectable_prelude (GMarkupParseContext *context,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
ParseState new_state)
{
const char *introspectable_arg;
const char *shadowed_by;
gboolean introspectable;
g_assert (ctx->state != STATE_PASSTHROUGH);
introspectable_arg = find_attribute ("introspectable", attribute_names, attribute_values);
shadowed_by = find_attribute ("shadowed-by", attribute_names, attribute_values);
introspectable = !(introspectable_arg && atoi (introspectable_arg) == 0) && shadowed_by == NULL;
if (introspectable)
state_switch (ctx, new_state);
else
state_switch (ctx, STATE_PASSTHROUGH);
return introspectable;
}
static gboolean
start_glib_boxed (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *typename;
const char *typeinit;
const char *deprecated;
GIIrNodeBoxed *boxed;
if (!(strcmp (element_name, "glib:boxed") == 0 &&
ctx->state == STATE_NAMESPACE))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_BOXED))
return TRUE;
name = find_attribute ("glib:name", attribute_names, attribute_values);
typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:name");
return FALSE;
}
else if (typename == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
return FALSE;
}
else if (typeinit == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
return FALSE;
}
boxed = (GIIrNodeBoxed *) gi_ir_node_new (GI_IR_NODE_BOXED,
ctx->current_module);
((GIIrNode *)boxed)->name = g_strdup (name);
boxed->gtype_name = g_strdup (typename);
boxed->gtype_init = g_strdup (typeinit);
if (deprecated)
boxed->deprecated = TRUE;
else
boxed->deprecated = FALSE;
push_node (ctx, (GIIrNode *)boxed);
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, boxed);
return TRUE;
}
static gboolean
start_function (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *shadows;
const char *symbol;
const char *deprecated;
const char *throws;
const char *set_property;
const char *get_property;
GIIrNodeFunction *function;
gboolean found = FALSE;
ParseState in_embedded_state = STATE_NONE;
switch (ctx->state)
{
case STATE_NAMESPACE:
found = (strcmp (element_name, "function") == 0 ||
strcmp (element_name, "callback") == 0);
break;
case STATE_CLASS:
case STATE_BOXED:
case STATE_STRUCT:
case STATE_UNION:
found = strcmp (element_name, "constructor") == 0;
/* fallthrough */
G_GNUC_FALLTHROUGH;
case STATE_INTERFACE:
found = (found ||
strcmp (element_name, "function") == 0 ||
strcmp (element_name, "method") == 0 ||
strcmp (element_name, "callback") == 0);
break;
case STATE_ENUM:
found = strcmp (element_name, "function") == 0;
break;
case STATE_CLASS_FIELD:
case STATE_STRUCT_FIELD:
found = (found || strcmp (element_name, "callback") == 0);
in_embedded_state = ctx->state;
break;
default:
break;
}
if (!found)
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION))
return TRUE;
ctx->in_embedded_state = in_embedded_state;
name = find_attribute ("name", attribute_names, attribute_values);
shadows = find_attribute ("shadows", attribute_names, attribute_values);
symbol = find_attribute ("c:identifier", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
throws = find_attribute ("throws", attribute_names, attribute_values);
set_property = find_attribute ("glib:set-property", attribute_names, attribute_values);
get_property = find_attribute ("glib:get-property", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
else if (strcmp (element_name, "callback") != 0 && symbol == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "c:identifier");
return FALSE;
}
if (shadows)
name = shadows;
function = (GIIrNodeFunction *) gi_ir_node_new (GI_IR_NODE_FUNCTION,
ctx->current_module);
((GIIrNode *)function)->name = g_strdup (name);
function->symbol = g_strdup (symbol);
function->parameters = NULL;
if (deprecated)
function->deprecated = TRUE;
else
function->deprecated = FALSE;
if (strcmp (element_name, "method") == 0 ||
strcmp (element_name, "constructor") == 0)
{
function->is_method = TRUE;
if (strcmp (element_name, "constructor") == 0)
function->is_constructor = TRUE;
else
function->is_constructor = FALSE;
if (set_property != NULL)
{
function->is_setter = TRUE;
function->is_getter = FALSE;
function->property = g_strdup (set_property);
}
else if (get_property != NULL)
{
function->is_setter = FALSE;
function->is_getter = TRUE;
function->property = g_strdup (get_property);
}
else
{
function->is_setter = FALSE;
function->is_getter = FALSE;
function->property = NULL;
}
}
else
{
function->is_method = FALSE;
function->is_setter = FALSE;
function->is_getter = FALSE;
function->is_constructor = FALSE;
if (strcmp (element_name, "callback") == 0)
((GIIrNode *)function)->type = GI_IR_NODE_CALLBACK;
}
if (throws && strcmp (throws, "1") == 0)
function->throws = TRUE;
else
function->throws = FALSE;
if (ctx->node_stack == NULL)
{
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, function);
}
else if (ctx->current_typed)
{
GIIrNodeField *field;
field = (GIIrNodeField *)ctx->current_typed;
field->callback = function;
}
else
switch (CURRENT_NODE (ctx)->type)
{
case GI_IR_NODE_INTERFACE:
case GI_IR_NODE_OBJECT:
{
GIIrNodeInterface *iface;
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, function);
}
break;
case GI_IR_NODE_BOXED:
{
GIIrNodeBoxed *boxed;
boxed = (GIIrNodeBoxed *)CURRENT_NODE (ctx);
boxed->members = g_list_append (boxed->members, function);
}
break;
case GI_IR_NODE_STRUCT:
{
GIIrNodeStruct *struct_;
struct_ = (GIIrNodeStruct *)CURRENT_NODE (ctx);
struct_->members = g_list_append (struct_->members, function); }
break;
case GI_IR_NODE_UNION:
{
GIIrNodeUnion *union_;
union_ = (GIIrNodeUnion *)CURRENT_NODE (ctx);
union_->members = g_list_append (union_->members, function);
}
break;
case GI_IR_NODE_ENUM:
case GI_IR_NODE_FLAGS:
{
GIIrNodeEnum *enum_;
enum_ = (GIIrNodeEnum *)CURRENT_NODE (ctx);
enum_->methods = g_list_append (enum_->methods, function);
}
break;
default:
g_assert_not_reached ();
}
push_node(ctx, (GIIrNode *)function);
return TRUE;
}
static void
parse_property_transfer (GIIrNodeProperty *property,
const char *transfer,
ParseContext *ctx)
{
if (transfer == NULL)
{
#if 0
GIIrNodeInterface *iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
g_debug ("required attribute 'transfer-ownership' is missing from "
"property '%s' in type '%s.%s'. Assuming 'none'",
property->node.name, ctx->namespace, iface->node.name);
#endif
transfer = "none";
}
if (strcmp (transfer, "none") == 0)
{
property->transfer = FALSE;
property->shallow_transfer = FALSE;
}
else if (strcmp (transfer, "container") == 0)
{
property->transfer = FALSE;
property->shallow_transfer = TRUE;
}
else if (strcmp (transfer, "full") == 0)
{
property->transfer = TRUE;
property->shallow_transfer = FALSE;
}
else
{
GIIrNodeInterface *iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
g_warning ("Unknown transfer-ownership value: '%s' for property '%s' in "
"type '%s.%s'", transfer, property->node.name, ctx->namespace,
iface->node.name);
}
}
static gboolean
parse_param_transfer (GIIrNodeParam *param, const char *transfer, const char *name,
GError **error)
{
if (transfer == NULL)
{
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)
{
param->transfer = FALSE;
param->shallow_transfer = FALSE;
}
else if (strcmp (transfer, "container") == 0)
{
param->transfer = FALSE;
param->shallow_transfer = TRUE;
}
else if (strcmp (transfer, "full") == 0)
{
param->transfer = TRUE;
param->shallow_transfer = FALSE;
}
else
{
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
start_instance_parameter (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *transfer;
gboolean transfer_full;
if (!(strcmp (element_name, "instance-parameter") == 0 &&
ctx->state == STATE_FUNCTION_PARAMETERS))
return FALSE;
transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
state_switch (ctx, STATE_PASSTHROUGH);
if (g_strcmp0 (transfer, "full") == 0)
transfer_full = TRUE;
else if (g_strcmp0 (transfer, "none") == 0)
transfer_full = FALSE;
else
{
g_set_error (error, G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"invalid value for 'transfer-ownership' for instance parameter: %s", transfer);
return FALSE;
}
switch (CURRENT_NODE (ctx)->type)
{
case GI_IR_NODE_FUNCTION:
case GI_IR_NODE_CALLBACK:
{
GIIrNodeFunction *func;
func = (GIIrNodeFunction *)CURRENT_NODE (ctx);
func->instance_transfer_full = transfer_full;
}
break;
case GI_IR_NODE_SIGNAL:
{
GIIrNodeSignal *signal;
signal = (GIIrNodeSignal *)CURRENT_NODE (ctx);
signal->instance_transfer_full = transfer_full;
}
break;
case GI_IR_NODE_VFUNC:
{
GIIrNodeVFunc *vfunc;
vfunc = (GIIrNodeVFunc *)CURRENT_NODE (ctx);
vfunc->instance_transfer_full = transfer_full;
}
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
static gboolean
start_parameter (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *direction;
const char *retval;
const char *optional;
const char *caller_allocates;
const char *allow_none;
const char *transfer;
const char *scope;
const char *closure;
const char *destroy;
const char *skip;
const char *nullable;
GIIrNodeParam *param;
if (!(strcmp (element_name, "parameter") == 0 &&
ctx->state == STATE_FUNCTION_PARAMETERS))
return FALSE;
name = find_attribute ("name", attribute_names, attribute_values);
direction = find_attribute ("direction", attribute_names, attribute_values);
retval = find_attribute ("retval", attribute_names, attribute_values);
optional = find_attribute ("optional", attribute_names, attribute_values);
allow_none = find_attribute ("allow-none", attribute_names, attribute_values);
caller_allocates = find_attribute ("caller-allocates", attribute_names, attribute_values);
transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
scope = find_attribute ("scope", attribute_names, attribute_values);
closure = find_attribute ("closure", attribute_names, attribute_values);
destroy = find_attribute ("destroy", attribute_names, attribute_values);
skip = find_attribute ("skip", attribute_names, attribute_values);
nullable = find_attribute ("nullable", attribute_names, attribute_values);
if (name == NULL)
name = "unknown";
param = (GIIrNodeParam *)gi_ir_node_new (GI_IR_NODE_PARAM,
ctx->current_module);
ctx->current_typed = (GIIrNode*) param;
ctx->current_typed->name = g_strdup (name);
state_switch (ctx, STATE_FUNCTION_PARAMETER);
if (direction && strcmp (direction, "out") == 0)
{
param->in = FALSE;
param->out = TRUE;
if (caller_allocates == NULL)
param->caller_allocates = FALSE;
else
param->caller_allocates = strcmp (caller_allocates, "1") == 0;
}
else if (direction && strcmp (direction, "inout") == 0)
{
param->in = TRUE;
param->out = TRUE;
param->caller_allocates = FALSE;
}
else
{
param->in = TRUE;
param->out = FALSE;
param->caller_allocates = FALSE;
}
if (retval && strcmp (retval, "1") == 0)
param->retval = TRUE;
else
param->retval = FALSE;
if (optional && strcmp (optional, "1") == 0)
param->optional = TRUE;
else
param->optional = FALSE;
if (nullable && strcmp (nullable, "1") == 0)
param->nullable = TRUE;
else
param->nullable = FALSE;
if (allow_none && strcmp (allow_none, "1") == 0)
{
if (param->out)
param->optional = TRUE;
else
param->nullable = TRUE;
}
if (skip && strcmp (skip, "1") == 0)
param->skip = TRUE;
else
param->skip = FALSE;
if (!parse_param_transfer (param, transfer, name, error))
return FALSE;
if (scope && strcmp (scope, "call") == 0)
param->scope = GI_SCOPE_TYPE_CALL;
else if (scope && strcmp (scope, "async") == 0)
param->scope = GI_SCOPE_TYPE_ASYNC;
else if (scope && strcmp (scope, "notified") == 0)
param->scope = GI_SCOPE_TYPE_NOTIFIED;
else if (scope && strcmp (scope, "forever") == 0)
param->scope = GI_SCOPE_TYPE_FOREVER;
else
param->scope = GI_SCOPE_TYPE_INVALID;
param->closure = closure ? atoi (closure) : -1;
param->destroy = destroy ? atoi (destroy) : -1;
((GIIrNode *)param)->name = g_strdup (name);
switch (CURRENT_NODE (ctx)->type)
{
case GI_IR_NODE_FUNCTION:
case GI_IR_NODE_CALLBACK:
{
GIIrNodeFunction *func;
func = (GIIrNodeFunction *)CURRENT_NODE (ctx);
func->parameters = g_list_append (func->parameters, param);
}
break;
case GI_IR_NODE_SIGNAL:
{
GIIrNodeSignal *signal;
signal = (GIIrNodeSignal *)CURRENT_NODE (ctx);
signal->parameters = g_list_append (signal->parameters, param);
}
break;
case GI_IR_NODE_VFUNC:
{
GIIrNodeVFunc *vfunc;
vfunc = (GIIrNodeVFunc *)CURRENT_NODE (ctx);
vfunc->parameters = g_list_append (vfunc->parameters, param);
}
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
static gboolean
start_field (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *readable;
const char *writable;
const char *bits;
const char *branch;
GIIrNodeField *field;
ParseState target_state;
gboolean introspectable;
guint64 parsed_bits;
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;
}
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);
bits = find_attribute ("bits", attribute_names, attribute_values);
branch = find_attribute ("branch", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
field = (GIIrNodeField *)gi_ir_node_new (GI_IR_NODE_FIELD,
ctx->current_module);
if (introspectable)
{
ctx->current_typed = (GIIrNode*) field;
}
else
{
field->type = parse_type (ctx, "gpointer");
}
((GIIrNode *)field)->name = g_strdup (name);
/* Fields are assumed to be read-only.
* (see also girwriter.py and generate.c)
*/
field->readable = readable == NULL || strcmp (readable, "0") == 0;
field->writable = writable != NULL && strcmp (writable, "1") == 0;
if (bits == NULL)
field->bits = 0;
else if (g_ascii_string_to_unsigned (bits, 10, 0, G_MAXUINT, &parsed_bits, error))
field->bits = parsed_bits;
else
{
gi_ir_node_free ((GIIrNode *) field);
return FALSE;
}
switch (CURRENT_NODE (ctx)->type)
{
case GI_IR_NODE_OBJECT:
{
GIIrNodeInterface *iface;
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, field);
}
break;
case GI_IR_NODE_INTERFACE:
{
GIIrNodeInterface *iface;
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, field);
}
break;
case GI_IR_NODE_BOXED:
{
GIIrNodeBoxed *boxed;
boxed = (GIIrNodeBoxed *)CURRENT_NODE (ctx);
boxed->members = g_list_append (boxed->members, field);
}
break;
case GI_IR_NODE_STRUCT:
{
GIIrNodeStruct *struct_;
struct_ = (GIIrNodeStruct *)CURRENT_NODE (ctx);
struct_->members = g_list_append (struct_->members, field);
}
break;
case GI_IR_NODE_UNION:
{
GIIrNodeUnion *union_;
union_ = (GIIrNodeUnion *)CURRENT_NODE (ctx);
union_->members = g_list_append (union_->members, field);
if (branch)
{
GIIrNodeConstant *constant;
constant = (GIIrNodeConstant *) gi_ir_node_new (GI_IR_NODE_CONSTANT,
ctx->current_module);
((GIIrNode *)constant)->name = g_strdup (name);
constant->value = g_strdup (branch);
constant->type = union_->discriminator_type;
constant->deprecated = FALSE;
union_->discriminators = g_list_append (union_->discriminators, constant);
}
}
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
static gboolean
start_alias (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
name = find_attribute ("name", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
ctx->current_alias = g_strdup (name);
state_switch (ctx, STATE_ALIAS);
return TRUE;
}
static gboolean
start_enum (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *typename;
const char *typeinit;
const char *deprecated;
const char *error_domain;
GIIrNodeEnum *enum_;
if (!((strcmp (element_name, "enumeration") == 0 && ctx->state == STATE_NAMESPACE) ||
(strcmp (element_name, "bitfield") == 0 && ctx->state == STATE_NAMESPACE)))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_ENUM))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
error_domain = find_attribute ("glib:error-domain", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
if (strcmp (element_name, "enumeration") == 0)
enum_ = (GIIrNodeEnum *) gi_ir_node_new (GI_IR_NODE_ENUM,
ctx->current_module);
else
enum_ = (GIIrNodeEnum *) gi_ir_node_new (GI_IR_NODE_FLAGS,
ctx->current_module);
((GIIrNode *)enum_)->name = g_strdup (name);
enum_->gtype_name = g_strdup (typename);
enum_->gtype_init = g_strdup (typeinit);
enum_->error_domain = g_strdup (error_domain);
if (deprecated)
enum_->deprecated = TRUE;
else
enum_->deprecated = FALSE;
push_node (ctx, (GIIrNode *) enum_);
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, enum_);
return TRUE;
}
static gboolean
start_property (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
ParseState target_state;
const char *name;
const char *readable;
const char *writable;
const char *construct;
const char *construct_only;
const char *transfer;
const char *setter;
const char *getter;
GIIrNodeProperty *property;
GIIrNodeInterface *iface;
if (!(strcmp (element_name, "property") == 0 &&
(ctx->state == STATE_CLASS ||
ctx->state == STATE_INTERFACE)))
return FALSE;
if (ctx->state == STATE_CLASS)
target_state = STATE_CLASS_PROPERTY;
else if (ctx->state == STATE_INTERFACE)
target_state = STATE_INTERFACE_PROPERTY;
else
g_assert_not_reached ();
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
readable = find_attribute ("readable", attribute_names, attribute_values);
writable = find_attribute ("writable", attribute_names, attribute_values);
construct = find_attribute ("construct", attribute_names, attribute_values);
construct_only = find_attribute ("construct-only", attribute_names, attribute_values);
transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
setter = find_attribute ("setter", attribute_names, attribute_values);
getter = find_attribute ("getter", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
property = (GIIrNodeProperty *) gi_ir_node_new (GI_IR_NODE_PROPERTY,
ctx->current_module);
ctx->current_typed = (GIIrNode*) property;
((GIIrNode *)property)->name = g_strdup (name);
/* Assume properties are readable */
if (readable == NULL || strcmp (readable, "1") == 0)
property->readable = TRUE;
else
property->readable = FALSE;
if (writable && strcmp (writable, "1") == 0)
property->writable = TRUE;
else
property->writable = FALSE;
if (construct && strcmp (construct, "1") == 0)
property->construct = TRUE;
else
property->construct = FALSE;
if (construct_only && strcmp (construct_only, "1") == 0)
property->construct_only = TRUE;
else
property->construct_only = FALSE;
property->setter = g_strdup (setter);
property->getter = g_strdup (getter);
parse_property_transfer (property, transfer, ctx);
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, property);
return TRUE;
}
static int64_t
parse_value (const char *str)
{
char *shift_op;
/* FIXME just a quick hack */
shift_op = strstr (str, "<<");
if (shift_op)
{
int64_t base, shift;
base = g_ascii_strtoll (str, NULL, 10);
shift = g_ascii_strtoll (shift_op + 3, NULL, 10);
return base << shift;
}
else
return g_ascii_strtoll (str, NULL, 10);
return 0;
}
static gboolean
start_member (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *value;
const char *deprecated;
const char *c_identifier;
GIIrNodeEnum *enum_;
GIIrNodeValue *value_;
if (!(strcmp (element_name, "member") == 0 &&
ctx->state == STATE_ENUM))
return FALSE;
name = find_attribute ("name", attribute_names, attribute_values);
value = find_attribute ("value", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
c_identifier = find_attribute ("c:identifier", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
value_ = (GIIrNodeValue *) gi_ir_node_new (GI_IR_NODE_VALUE,
ctx->current_module);
((GIIrNode *)value_)->name = g_strdup (name);
value_->value = parse_value (value);
if (deprecated)
value_->deprecated = TRUE;
else
value_->deprecated = FALSE;
g_hash_table_insert (((GIIrNode *)value_)->attributes,
g_strdup ("c:identifier"),
g_strdup (c_identifier));
enum_ = (GIIrNodeEnum *)CURRENT_NODE (ctx);
enum_->values = g_list_append (enum_->values, value_);
return TRUE;
}
static gboolean
start_constant (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
ParseState prev_state;
ParseState target_state;
const char *name;
const char *value;
const char *deprecated;
GIIrNodeConstant *constant;
if (!(strcmp (element_name, "constant") == 0 &&
(ctx->state == STATE_NAMESPACE ||
ctx->state == STATE_CLASS ||
ctx->state == STATE_INTERFACE)))
return FALSE;
switch (ctx->state)
{
case STATE_NAMESPACE:
target_state = STATE_NAMESPACE_CONSTANT;
break;
case STATE_CLASS:
target_state = STATE_CLASS_CONSTANT;
break;
case STATE_INTERFACE:
target_state = STATE_INTERFACE_CONSTANT;
break;
default:
g_assert_not_reached ();
}
prev_state = ctx->state;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, target_state))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
value = find_attribute ("value", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
else if (value == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "value");
return FALSE;
}
constant = (GIIrNodeConstant *) gi_ir_node_new (GI_IR_NODE_CONSTANT,
ctx->current_module);
((GIIrNode *)constant)->name = g_strdup (name);
constant->value = g_strdup (value);
ctx->current_typed = (GIIrNode*) constant;
if (deprecated)
constant->deprecated = TRUE;
else
constant->deprecated = FALSE;
if (prev_state == STATE_NAMESPACE)
{
push_node (ctx, (GIIrNode *) constant);
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, constant);
}
else
{
GIIrNodeInterface *iface;
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, constant);
}
return TRUE;
}
static gboolean
start_interface (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *typename;
const char *typeinit;
const char *deprecated;
const char *glib_type_struct;
GIIrNodeInterface *iface;
if (!(strcmp (element_name, "interface") == 0 &&
ctx->state == STATE_NAMESPACE))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_INTERFACE))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
else if (typename == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
return FALSE;
}
else if (typeinit == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
return FALSE;
}
iface = (GIIrNodeInterface *) gi_ir_node_new (GI_IR_NODE_INTERFACE,
ctx->current_module);
((GIIrNode *)iface)->name = g_strdup (name);
iface->gtype_name = g_strdup (typename);
iface->gtype_init = g_strdup (typeinit);
iface->glib_type_struct = g_strdup (glib_type_struct);
if (deprecated)
iface->deprecated = TRUE;
else
iface->deprecated = FALSE;
push_node (ctx, (GIIrNode *) iface);
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, iface);
return TRUE;
}
static gboolean
start_class (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *parent;
const char *glib_type_struct;
const char *typename;
const char *typeinit;
const char *deprecated;
const char *abstract;
const char *fundamental;
const char *final;
const char *ref_func;
const char *unref_func;
const char *set_value_func;
const char *get_value_func;
GIIrNodeInterface *iface;
if (!(strcmp (element_name, "class") == 0 &&
ctx->state == STATE_NAMESPACE))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_CLASS))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
parent = find_attribute ("parent", attribute_names, attribute_values);
glib_type_struct = find_attribute ("glib:type-struct", attribute_names, attribute_values);
typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
abstract = find_attribute ("abstract", attribute_names, attribute_values);
final = find_attribute ("final", attribute_names, attribute_values);
fundamental = find_attribute ("glib:fundamental", attribute_names, attribute_values);
ref_func = find_attribute ("glib:ref-func", attribute_names, attribute_values);
unref_func = find_attribute ("glib:unref-func", attribute_names, attribute_values);
set_value_func = find_attribute ("glib:set-value-func", attribute_names, attribute_values);
get_value_func = find_attribute ("glib:get-value-func", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
else if (typename == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
return FALSE;
}
else if (typeinit == NULL && strcmp (typename, "GObject"))
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
return FALSE;
}
iface = (GIIrNodeInterface *) gi_ir_node_new (GI_IR_NODE_OBJECT,
ctx->current_module);
((GIIrNode *)iface)->name = g_strdup (name);
iface->gtype_name = g_strdup (typename);
iface->gtype_init = g_strdup (typeinit);
iface->parent = g_strdup (parent);
iface->glib_type_struct = g_strdup (glib_type_struct);
if (deprecated)
iface->deprecated = TRUE;
else
iface->deprecated = FALSE;
iface->abstract = abstract && strcmp (abstract, "1") == 0;
iface->final_ = final && strcmp (final, "1") == 0;
if (fundamental)
iface->fundamental = TRUE;
if (ref_func)
iface->ref_func = g_strdup (ref_func);
if (unref_func)
iface->unref_func = g_strdup (unref_func);
if (set_value_func)
iface->set_value_func = g_strdup (set_value_func);
if (get_value_func)
iface->get_value_func = g_strdup (get_value_func);
push_node (ctx, (GIIrNode *) iface);
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, iface);
return TRUE;
}
static gboolean
start_type (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *ctype;
gboolean in_alias = FALSE;
gboolean is_array;
gboolean is_varargs;
GIIrNodeType *typenode;
is_array = strcmp (element_name, "array") == 0;
is_varargs = strcmp (element_name, "varargs") == 0;
if (!(is_array || is_varargs || (strcmp (element_name, "type") == 0)))
return FALSE;
if (ctx->state == STATE_TYPE)
{
ctx->type_depth++;
ctx->type_stack = g_list_prepend (ctx->type_stack, ctx->type_parameters);
ctx->type_parameters = NULL;
}
else if (ctx->state == STATE_FUNCTION_PARAMETER ||
ctx->state == STATE_FUNCTION_RETURN ||
ctx->state == STATE_STRUCT_FIELD ||
ctx->state == STATE_UNION_FIELD ||
ctx->state == STATE_CLASS_PROPERTY ||
ctx->state == STATE_CLASS_FIELD ||
ctx->state == STATE_INTERFACE_FIELD ||
ctx->state == STATE_INTERFACE_PROPERTY ||
ctx->state == STATE_BOXED_FIELD ||
ctx->state == STATE_NAMESPACE_CONSTANT ||
ctx->state == STATE_CLASS_CONSTANT ||
ctx->state == STATE_INTERFACE_CONSTANT ||
ctx->state == STATE_ALIAS
)
{
if (ctx->state == STATE_ALIAS)
in_alias = TRUE;
state_switch (ctx, STATE_TYPE);
ctx->type_depth = 1;
ctx->type_stack = NULL;
ctx->type_parameters = NULL;
}
name = find_attribute ("name", attribute_names, attribute_values);
if (in_alias && ctx->current_alias)
{
char *key;
char *value;
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
key = g_strdup_printf ("%s.%s", ctx->namespace, ctx->current_alias);
if (!strchr (name, '.'))
{
const BasicTypeInfo *basic = parse_basic (name);
if (!basic)
{
/* For non-basic types, re-qualify the interface */
value = g_strdup_printf ("%s.%s", ctx->namespace, name);
}
else
{
value = g_strdup (name);
}
}
else
value = g_strdup (name);
g_hash_table_replace (ctx->aliases, key, value);
return TRUE;
}
else if (!ctx->current_module || in_alias)
return TRUE;
if (!ctx->current_typed)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"The element <type> is invalid here");
return FALSE;
}
if (is_varargs)
return TRUE;
if (is_array)
{
const char *zero;
const char *len;
const char *size;
typenode = (GIIrNodeType *)gi_ir_node_new (GI_IR_NODE_TYPE,
ctx->current_module);
typenode->tag = GI_TYPE_TAG_ARRAY;
typenode->is_pointer = TRUE;
typenode->is_array = TRUE;
if (name && strcmp (name, "GLib.Array") == 0) {
typenode->array_type = GI_ARRAY_TYPE_ARRAY;
} else if (name && strcmp (name, "GLib.ByteArray") == 0) {
typenode->array_type = GI_ARRAY_TYPE_BYTE_ARRAY;
} else if (name && strcmp (name, "GLib.PtrArray") == 0) {
typenode->array_type = GI_ARRAY_TYPE_PTR_ARRAY;
} else {
typenode->array_type = GI_ARRAY_TYPE_C;
}
if (typenode->array_type == GI_ARRAY_TYPE_C) {
guint64 parsed_uint;
zero = find_attribute ("zero-terminated", attribute_names, attribute_values);
len = find_attribute ("length", attribute_names, attribute_values);
size = find_attribute ("fixed-size", attribute_names, attribute_values);
typenode->has_length = len != NULL;
if (!typenode->has_length)
typenode->length = -1;
else if (g_ascii_string_to_unsigned (len, 10, 0, G_MAXUINT, &parsed_uint, error))
typenode->length = parsed_uint;
else
{
gi_ir_node_free ((GIIrNode *) typenode);
return FALSE;
}
typenode->has_size = size != NULL;
if (!typenode->has_size)
typenode->size = -1;
else if (g_ascii_string_to_unsigned (size, 10, 0, G_MAXSIZE, &parsed_uint, error))
typenode->size = parsed_uint;
else
{
gi_ir_node_free ((GIIrNode *) typenode);
return FALSE;
}
if (zero)
typenode->zero_terminated = strcmp(zero, "1") == 0;
else
/* If neither zero-terminated nor length nor fixed-size is given, assume zero-terminated. */
typenode->zero_terminated = !(typenode->has_length || typenode->has_size);
if (typenode->has_size && ctx->current_typed->type == GI_IR_NODE_FIELD)
typenode->is_pointer = FALSE;
} else {
typenode->zero_terminated = FALSE;
typenode->has_length = FALSE;
typenode->length = -1;
typenode->has_size = FALSE;
typenode->size = -1;
}
}
else
{
int pointer_depth;
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
pointer_depth = 0;
ctype = find_attribute ("c:type", attribute_names, attribute_values);
if (ctype != NULL)
{
const char *cp = ctype + strlen(ctype) - 1;
while (cp > ctype && *cp-- == '*')
pointer_depth++;
if (g_str_has_prefix (ctype, "gpointer")
|| g_str_has_prefix (ctype, "gconstpointer"))
pointer_depth++;
}
if (ctx->current_typed->type == GI_IR_NODE_PARAM &&
((GIIrNodeParam *)ctx->current_typed)->out &&
pointer_depth > 0)
pointer_depth--;
typenode = parse_type (ctx, name);
/* A "pointer" structure is one where the c:type is a typedef that
* to a pointer to a structure; we used to call them "disguised"
* structures as well.
*/
if (typenode->tag == GI_TYPE_TAG_INTERFACE)
{
gboolean is_pointer = FALSE;
gboolean is_disguised = FALSE;
is_pointer_or_disguised_structure (ctx, typenode->giinterface,
&is_pointer,
&is_disguised);
if (is_pointer || is_disguised)
pointer_depth++;
}
if (pointer_depth > 0)
typenode->is_pointer = TRUE;
}
ctx->type_parameters = g_list_append (ctx->type_parameters, typenode);
return TRUE;
}
static void
end_type_top (ParseContext *ctx)
{
GIIrNodeType *typenode;
if (!ctx->type_parameters)
goto out;
typenode = (GIIrNodeType*)ctx->type_parameters->data;
/* Default to pointer for unspecified containers */
if (typenode->tag == GI_TYPE_TAG_ARRAY ||
typenode->tag == GI_TYPE_TAG_GLIST ||
typenode->tag == GI_TYPE_TAG_GSLIST)
{
if (typenode->parameter_type1 == NULL)
typenode->parameter_type1 = parse_type (ctx, "gpointer");
}
else if (typenode->tag == GI_TYPE_TAG_GHASH)
{
if (typenode->parameter_type1 == NULL)
{
typenode->parameter_type1 = parse_type (ctx, "gpointer");
typenode->parameter_type2 = parse_type (ctx, "gpointer");
}
}
switch (ctx->current_typed->type)
{
case GI_IR_NODE_PARAM:
{
GIIrNodeParam *param = (GIIrNodeParam *)ctx->current_typed;
param->type = typenode;
}
break;
case GI_IR_NODE_FIELD:
{
GIIrNodeField *field = (GIIrNodeField *)ctx->current_typed;
field->type = typenode;
}
break;
case GI_IR_NODE_PROPERTY:
{
GIIrNodeProperty *property = (GIIrNodeProperty *) ctx->current_typed;
property->type = typenode;
}
break;
case GI_IR_NODE_CONSTANT:
{
GIIrNodeConstant *constant = (GIIrNodeConstant *)ctx->current_typed;
constant->type = typenode;
}
break;
default:
g_printerr("current node is %d\n", CURRENT_NODE (ctx)->type);
g_assert_not_reached ();
}
g_list_free (ctx->type_parameters);
out:
ctx->type_depth = 0;
ctx->type_parameters = NULL;
ctx->current_typed = NULL;
}
static void
end_type_recurse (ParseContext *ctx)
{
GIIrNodeType *parent;
GIIrNodeType *param = NULL;
parent = (GIIrNodeType *) ((GList*)ctx->type_stack->data)->data;
if (ctx->type_parameters)
param = (GIIrNodeType *) ctx->type_parameters->data;
if (parent->tag == GI_TYPE_TAG_ARRAY ||
parent->tag == GI_TYPE_TAG_GLIST ||
parent->tag == GI_TYPE_TAG_GSLIST)
{
g_assert (param != NULL);
if (parent->parameter_type1 == NULL)
parent->parameter_type1 = param;
else
g_assert_not_reached ();
}
else if (parent->tag == GI_TYPE_TAG_GHASH)
{
g_assert (param != NULL);
if (parent->parameter_type1 == NULL)
parent->parameter_type1 = param;
else if (parent->parameter_type2 == NULL)
parent->parameter_type2 = param;
else
g_assert_not_reached ();
}
g_list_free (ctx->type_parameters);
ctx->type_parameters = (GList *)ctx->type_stack->data;
ctx->type_stack = g_list_delete_link (ctx->type_stack, ctx->type_stack);
}
static void
end_type (ParseContext *ctx)
{
if (ctx->type_depth == 1)
{
end_type_top (ctx);
state_switch (ctx, ctx->prev_state);
}
else
{
end_type_recurse (ctx);
ctx->type_depth--;
}
}
static gboolean
start_attribute (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *value;
GIIrNode *curnode;
if (strcmp (element_name, "attribute") != 0 || ctx->node_stack == NULL)
return FALSE;
name = find_attribute ("name", attribute_names, attribute_values);
value = find_attribute ("value", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
if (value == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "value");
return FALSE;
}
state_switch (ctx, STATE_ATTRIBUTE);
curnode = CURRENT_NODE (ctx);
if (ctx->current_typed && ctx->current_typed->type == GI_IR_NODE_PARAM)
{
g_hash_table_insert (ctx->current_typed->attributes, g_strdup (name), g_strdup (value));
}
else
{
g_hash_table_insert (curnode->attributes, g_strdup (name), g_strdup (value));
}
return TRUE;
}
static gboolean
start_return_value (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
GIIrNodeParam *param;
const char *transfer;
const char *skip;
const char *nullable;
if (!(strcmp (element_name, "return-value") == 0 &&
ctx->state == STATE_FUNCTION))
return FALSE;
param = (GIIrNodeParam *)gi_ir_node_new (GI_IR_NODE_PARAM,
ctx->current_module);
param->in = FALSE;
param->out = FALSE;
param->retval = TRUE;
ctx->current_typed = (GIIrNode*) param;
state_switch (ctx, STATE_FUNCTION_RETURN);
skip = find_attribute ("skip", attribute_names, attribute_values);
if (skip && strcmp (skip, "1") == 0)
param->skip = TRUE;
else
param->skip = FALSE;
transfer = find_attribute ("transfer-ownership", attribute_names, attribute_values);
if (!parse_param_transfer (param, transfer, NULL, error))
return FALSE;
nullable = find_attribute ("nullable", attribute_names, attribute_values);
if (nullable && g_str_equal (nullable, "1"))
param->nullable = TRUE;
switch (CURRENT_NODE (ctx)->type)
{
case GI_IR_NODE_FUNCTION:
case GI_IR_NODE_CALLBACK:
{
GIIrNodeFunction *func = (GIIrNodeFunction *)CURRENT_NODE (ctx);
func->result = param;
}
break;
case GI_IR_NODE_SIGNAL:
{
GIIrNodeSignal *signal = (GIIrNodeSignal *)CURRENT_NODE (ctx);
signal->result = param;
}
break;
case GI_IR_NODE_VFUNC:
{
GIIrNodeVFunc *vfunc = (GIIrNodeVFunc *)CURRENT_NODE (ctx);
vfunc->result = param;
}
break;
default:
g_assert_not_reached ();
}
return TRUE;
}
static gboolean
start_implements (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
GIIrNodeInterface *iface;
const char *name;
if (strcmp (element_name, "implements") != 0 ||
!(ctx->state == STATE_CLASS))
return FALSE;
state_switch (ctx, STATE_IMPLEMENTS);
name = find_attribute ("name", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->interfaces = g_list_append (iface->interfaces, g_strdup (name));
return TRUE;
}
static gboolean
start_glib_signal (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *when;
const char *no_recurse;
const char *detailed;
const char *action;
const char *no_hooks;
const char *has_class_closure;
GIIrNodeInterface *iface;
GIIrNodeSignal *signal;
if (!(strcmp (element_name, "glib:signal") == 0 &&
(ctx->state == STATE_CLASS ||
ctx->state == STATE_INTERFACE)))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
when = find_attribute ("when", attribute_names, attribute_values);
no_recurse = find_attribute ("no-recurse", attribute_names, attribute_values);
detailed = find_attribute ("detailed", attribute_names, attribute_values);
action = find_attribute ("action", attribute_names, attribute_values);
no_hooks = find_attribute ("no-hooks", attribute_names, attribute_values);
has_class_closure = find_attribute ("has-class-closure", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
signal = (GIIrNodeSignal *)gi_ir_node_new (GI_IR_NODE_SIGNAL,
ctx->current_module);
((GIIrNode *)signal)->name = g_strdup (name);
signal->run_first = FALSE;
signal->run_last = FALSE;
signal->run_cleanup = FALSE;
if (when == NULL || g_ascii_strcasecmp (when, "LAST") == 0)
signal->run_last = TRUE;
else if (g_ascii_strcasecmp (when, "FIRST") == 0)
signal->run_first = TRUE;
else
signal->run_cleanup = TRUE;
if (no_recurse && strcmp (no_recurse, "1") == 0)
signal->no_recurse = TRUE;
else
signal->no_recurse = FALSE;
if (detailed && strcmp (detailed, "1") == 0)
signal->detailed = TRUE;
else
signal->detailed = FALSE;
if (action && strcmp (action, "1") == 0)
signal->action = TRUE;
else
signal->action = FALSE;
if (no_hooks && strcmp (no_hooks, "1") == 0)
signal->no_hooks = TRUE;
else
signal->no_hooks = FALSE;
if (has_class_closure && strcmp (has_class_closure, "1") == 0)
signal->has_class_closure = TRUE;
else
signal->has_class_closure = FALSE;
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, signal);
push_node (ctx, (GIIrNode *)signal);
return TRUE;
}
static gboolean
start_vfunc (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *must_chain_up;
const char *override;
const char *is_class_closure;
const char *offset;
const char *invoker;
const char *throws;
GIIrNodeInterface *iface;
GIIrNodeVFunc *vfunc;
guint64 parsed_offset;
if (!(strcmp (element_name, "virtual-method") == 0 &&
(ctx->state == STATE_CLASS ||
ctx->state == STATE_INTERFACE)))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_FUNCTION))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
must_chain_up = find_attribute ("must-chain-up", attribute_names, attribute_values);
override = find_attribute ("override", attribute_names, attribute_values);
is_class_closure = find_attribute ("is-class-closure", attribute_names, attribute_values);
offset = find_attribute ("offset", attribute_names, attribute_values);
invoker = find_attribute ("invoker", attribute_names, attribute_values);
throws = find_attribute ("throws", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
vfunc = (GIIrNodeVFunc *)gi_ir_node_new (GI_IR_NODE_VFUNC,
ctx->current_module);
((GIIrNode *)vfunc)->name = g_strdup (name);
if (must_chain_up && strcmp (must_chain_up, "1") == 0)
vfunc->must_chain_up = TRUE;
else
vfunc->must_chain_up = FALSE;
if (override && strcmp (override, "always") == 0)
{
vfunc->must_be_implemented = TRUE;
vfunc->must_not_be_implemented = FALSE;
}
else if (override && strcmp (override, "never") == 0)
{
vfunc->must_be_implemented = FALSE;
vfunc->must_not_be_implemented = TRUE;
}
else
{
vfunc->must_be_implemented = FALSE;
vfunc->must_not_be_implemented = FALSE;
}
if (is_class_closure && strcmp (is_class_closure, "1") == 0)
vfunc->is_class_closure = TRUE;
else
vfunc->is_class_closure = FALSE;
if (throws && strcmp (throws, "1") == 0)
vfunc->throws = TRUE;
else
vfunc->throws = FALSE;
if (offset == NULL)
vfunc->offset = 0xFFFF;
else if (g_ascii_string_to_unsigned (offset, 10, 0, G_MAXSIZE, &parsed_offset, error))
vfunc->offset = parsed_offset;
else
{
gi_ir_node_free ((GIIrNode *) vfunc);
return FALSE;
}
vfunc->invoker = g_strdup (invoker);
iface = (GIIrNodeInterface *)CURRENT_NODE (ctx);
iface->members = g_list_append (iface->members, vfunc);
push_node (ctx, (GIIrNode *)vfunc);
return TRUE;
}
static gboolean
start_struct (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *deprecated;
const char *disguised;
const char *opaque;
const char *pointer;
const char *gtype_name;
const char *gtype_init;
const char *gtype_struct;
const char *foreign;
const char *copy_func;
const char *free_func;
GIIrNodeStruct *struct_;
if (!(strcmp (element_name, "record") == 0 &&
(ctx->state == STATE_NAMESPACE ||
ctx->state == STATE_UNION ||
ctx->state == STATE_STRUCT ||
ctx->state == STATE_CLASS)))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_STRUCT))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
disguised = find_attribute ("disguised", attribute_names, attribute_values);
pointer = find_attribute ("pointer", attribute_names, attribute_values);
opaque = find_attribute ("opaque", attribute_names, attribute_values);
gtype_name = find_attribute ("glib:type-name", attribute_names, attribute_values);
gtype_init = find_attribute ("glib:get-type", attribute_names, attribute_values);
gtype_struct = find_attribute ("glib:is-gtype-struct-for", attribute_names, attribute_values);
foreign = find_attribute ("foreign", attribute_names, attribute_values);
copy_func = find_attribute ("copy-function", attribute_names, attribute_values);
free_func = find_attribute ("free-function", attribute_names, attribute_values);
if (name == NULL && ctx->node_stack == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
if ((gtype_name == NULL && gtype_init != NULL))
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:type-name");
return FALSE;
}
if ((gtype_name != NULL && gtype_init == NULL))
{
MISSING_ATTRIBUTE (context, error, element_name, "glib:get-type");
return FALSE;
}
struct_ = (GIIrNodeStruct *) gi_ir_node_new (GI_IR_NODE_STRUCT,
ctx->current_module);
((GIIrNode *)struct_)->name = g_strdup (name ? name : "");
if (deprecated)
struct_->deprecated = TRUE;
else
struct_->deprecated = FALSE;
if (g_strcmp0 (disguised, "1") == 0)
struct_->disguised = TRUE;
if (g_strcmp0 (pointer, "1") == 0)
struct_->pointer = TRUE;
if (g_strcmp0 (opaque, "1") == 0)
struct_->opaque = TRUE;
struct_->is_gtype_struct = gtype_struct != NULL;
struct_->gtype_name = g_strdup (gtype_name);
struct_->gtype_init = g_strdup (gtype_init);
struct_->foreign = (g_strcmp0 (foreign, "1") == 0);
struct_->copy_func = g_strdup (copy_func);
struct_->free_func = g_strdup (free_func);
if (ctx->node_stack == NULL)
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, struct_);
push_node (ctx, (GIIrNode *)struct_);
return TRUE;
}
static gboolean
start_union (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *name;
const char *deprecated;
const char *typename;
const char *typeinit;
const char *copy_func;
const char *free_func;
GIIrNodeUnion *union_;
if (!(strcmp (element_name, "union") == 0 &&
(ctx->state == STATE_NAMESPACE ||
ctx->state == STATE_UNION ||
ctx->state == STATE_STRUCT ||
ctx->state == STATE_CLASS)))
return FALSE;
if (!introspectable_prelude (context, attribute_names, attribute_values, ctx, STATE_UNION))
return TRUE;
name = find_attribute ("name", attribute_names, attribute_values);
deprecated = find_attribute ("deprecated", attribute_names, attribute_values);
typename = find_attribute ("glib:type-name", attribute_names, attribute_values);
typeinit = find_attribute ("glib:get-type", attribute_names, attribute_values);
copy_func = find_attribute ("copy-function", attribute_names, attribute_values);
free_func = find_attribute ("free-function", attribute_names, attribute_values);
if (name == NULL && ctx->node_stack == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
return FALSE;
}
union_ = (GIIrNodeUnion *) gi_ir_node_new (GI_IR_NODE_UNION,
ctx->current_module);
((GIIrNode *)union_)->name = g_strdup (name ? name : "");
union_->gtype_name = g_strdup (typename);
union_->gtype_init = g_strdup (typeinit);
union_->copy_func = g_strdup (copy_func);
union_->free_func = g_strdup (free_func);
if (deprecated)
union_->deprecated = TRUE;
else
union_->deprecated = FALSE;
if (ctx->node_stack == NULL)
ctx->current_module->entries =
g_list_append (ctx->current_module->entries, union_);
push_node (ctx, (GIIrNode *)union_);
return TRUE;
}
static gboolean
start_discriminator (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
ParseContext *ctx,
GError **error)
{
const char *type;
const char *offset;
guint64 parsed_offset;
if (!(strcmp (element_name, "discriminator") == 0 &&
ctx->state == STATE_UNION))
return FALSE;
type = find_attribute ("type", attribute_names, attribute_values);
offset = find_attribute ("offset", attribute_names, attribute_values);
if (type == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "type");
return FALSE;
}
else if (offset == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "offset");
return FALSE;
}
((GIIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_type
= parse_type (ctx, type);
if (g_ascii_string_to_unsigned (offset, 10, 0, G_MAXSIZE, &parsed_offset, error))
((GIIrNodeUnion *)CURRENT_NODE (ctx))->discriminator_offset = parsed_offset;
else
return FALSE;
return TRUE;
}
static gboolean
parse_include (GMarkupParseContext *context,
ParseContext *ctx,
const char *name,
const char *version)
{
GError *error = NULL;
char *buffer;
gsize length;
char *girpath, *girname;
GIIrModule *module;
GList *l;
for (l = ctx->parser->parsed_modules; l; l = l->next)
{
GIIrModule *m = l->data;
if (strcmp (m->name, name) == 0)
{
if (strcmp (m->version, version) == 0)
{
ctx->include_modules = g_list_prepend (ctx->include_modules, m);
return TRUE;
}
else
{
g_printerr ("Module '%s' imported with conflicting versions '%s' and '%s'\n",
name, m->version, version);
return FALSE;
}
}
}
girname = g_strdup_printf ("%s-%s.gir", name, version);
girpath = locate_gir (ctx->parser, girname);
if (girpath == NULL)
{
g_printerr ("Could not find GIR file '%s'; check XDG_DATA_DIRS or use --includedir\n",
girname);
g_free (girname);
return FALSE;
}
g_free (girname);
g_debug ("Parsing include %s", girpath);
if (!g_file_get_contents (girpath, &buffer, &length, &error))
{
g_printerr ("%s: %s\n", girpath, error->message);
g_clear_error (&error);
g_free (girpath);
return FALSE;
}
module = gi_ir_parser_parse_string (ctx->parser, name, girpath, buffer, length, &error);
g_free (buffer);
if (error != NULL)
{
int line_number, char_number;
g_markup_parse_context_get_position (context, &line_number, &char_number);
g_printerr ("%s:%d:%d: error: %s\n", girpath, line_number, char_number, error->message);
g_clear_error (&error);
g_free (girpath);
return FALSE;
}
g_free (girpath);
ctx->include_modules = g_list_append (ctx->include_modules,
module);
return TRUE;
}
static void
start_element_handler (GMarkupParseContext *context,
const char *element_name,
const char **attribute_names,
const char **attribute_values,
gpointer user_data,
GError **error)
{
ParseContext *ctx = user_data;
if (ctx->parser->logged_levels & G_LOG_LEVEL_DEBUG)
{
GString *tags = g_string_new ("");
int i;
for (i = 0; attribute_names[i]; i++)
g_string_append_printf (tags, "%s=\"%s\" ",
attribute_names[i],
attribute_values[i]);
if (i)
{
g_string_insert_c (tags, 0, ' ');
g_string_truncate (tags, tags->len - 1);
}
g_debug ("<%s%s>", element_name, tags->str);
g_string_free (tags, TRUE);
}
if (ctx->state == STATE_PASSTHROUGH)
{
ctx->unknown_depth += 1;
return;
}
switch (element_name[0])
{
case 'a':
if (ctx->state == STATE_NAMESPACE && strcmp (element_name, "alias") == 0)
{
state_switch (ctx, STATE_ALIAS);
goto out;
}
if (start_type (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_attribute (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'b':
if (start_enum (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'c':
if (start_function (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_constant (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_class (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'd':
if (start_discriminator (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
if (strcmp ("doc", element_name) == 0 || strcmp ("doc-deprecated", element_name) == 0 ||
strcmp ("doc-stability", element_name) == 0 || strcmp ("doc-version", element_name) == 0 ||
strcmp ("docsection", element_name) == 0)
{
state_switch (ctx, STATE_PASSTHROUGH);
goto out;
}
break;
case 'e':
if (start_enum (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'f':
if (strcmp ("function-macro", element_name) == 0)
{
state_switch (ctx, STATE_PASSTHROUGH);
goto out;
}
else if (start_function (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_field (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'g':
if (start_glib_boxed (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_glib_signal (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'i':
if (strcmp (element_name, "include") == 0 &&
ctx->state == STATE_REPOSITORY)
{
const char *name;
const char *version;
name = find_attribute ("name", attribute_names, attribute_values);
version = find_attribute ("version", attribute_names, attribute_values);
if (name == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "name");
break;
}
if (version == NULL)
{
MISSING_ATTRIBUTE (context, error, element_name, "version");
break;
}
if (!parse_include (context, ctx, name, version))
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Failed to parse included gir %s-%s",
name,
version);
return;
}
ctx->dependencies = g_list_prepend (ctx->dependencies,
g_strdup_printf ("%s-%s", name, version));
state_switch (ctx, STATE_INCLUDE);
goto out;
}
if (start_interface (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_implements (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_instance_parameter (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (strcmp (element_name, "c:include") == 0)
{
state_switch (ctx, STATE_C_INCLUDE);
goto out;
}
break;
case 'm':
if (start_function (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_member (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'n':
if (strcmp (element_name, "namespace") == 0 && ctx->state == STATE_REPOSITORY)
{
const char *name, *version, *shared_library, *cprefix;
if (ctx->current_module != NULL)
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Only one <namespace/> element is currently allowed per <repository/>");
goto out;
}
name = find_attribute ("name", attribute_names, attribute_values);
version = find_attribute ("version", attribute_names, attribute_values);
shared_library = find_attribute ("shared-library", attribute_names, attribute_values);
cprefix = find_attribute ("c:identifier-prefixes", attribute_names, attribute_values);
/* Backwards compatibility; vala currently still generates this */
if (cprefix == NULL)
cprefix = find_attribute ("c:prefix", attribute_names, attribute_values);
if (name == NULL)
MISSING_ATTRIBUTE (context, error, element_name, "name");
else if (version == NULL)
MISSING_ATTRIBUTE (context, error, element_name, "version");
else
{
GList *l;
if (strcmp (name, ctx->namespace) != 0)
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"<namespace/> name element '%s' doesn't match file name '%s'",
name, ctx->namespace);
ctx->current_module = gi_ir_module_new (name, version, shared_library, cprefix);
ctx->current_module->aliases = ctx->aliases;
ctx->aliases = NULL;
ctx->current_module->disguised_structures = ctx->disguised_structures;
ctx->current_module->pointer_structures = ctx->pointer_structures;
ctx->disguised_structures = NULL;
ctx->pointer_structures = NULL;
for (l = ctx->include_modules; l; l = l->next)
gi_ir_module_add_include_module (ctx->current_module, l->data);
g_list_free (ctx->include_modules);
ctx->include_modules = NULL;
ctx->modules = g_list_append (ctx->modules, ctx->current_module);
ctx->current_module->dependencies = ctx->dependencies;
state_switch (ctx, STATE_NAMESPACE);
goto out;
}
}
break;
case 'p':
if (start_property (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (strcmp (element_name, "parameters") == 0 &&
ctx->state == STATE_FUNCTION)
{
state_switch (ctx, STATE_FUNCTION_PARAMETERS);
goto out;
}
else if (start_parameter (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (strcmp (element_name, "prerequisite") == 0 &&
ctx->state == STATE_INTERFACE)
{
const char *name;
name = find_attribute ("name", attribute_names, attribute_values);
state_switch (ctx, STATE_PREREQUISITE);
if (name == NULL)
MISSING_ATTRIBUTE (context, error, element_name, "name");
else
{
GIIrNodeInterface *iface;
iface = (GIIrNodeInterface *)CURRENT_NODE(ctx);
iface->prerequisites = g_list_append (iface->prerequisites, g_strdup (name));
}
goto out;
}
else if (strcmp (element_name, "package") == 0 &&
ctx->state == STATE_REPOSITORY)
{
state_switch (ctx, STATE_PACKAGE);
goto out;
}
break;
case 'r':
if (strcmp (element_name, "repository") == 0 && ctx->state == STATE_START)
{
const char *version;
version = find_attribute ("version", attribute_names, attribute_values);
if (version == NULL)
MISSING_ATTRIBUTE (context, error, element_name, "version");
else if (strcmp (version, SUPPORTED_GIR_VERSION) != 0)
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Unsupported version '%s'",
version);
else
state_switch (ctx, STATE_REPOSITORY);
goto out;
}
else if (start_return_value (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
else if (start_struct (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 's':
if (strcmp ("source-position", element_name) == 0)
{
state_switch (ctx, STATE_PASSTHROUGH);
goto out;
}
break;
case 'u':
if (start_union (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 't':
if (start_type (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
case 'v':
if (start_vfunc (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
if (start_type (context, element_name,
attribute_names, attribute_values,
ctx, error))
goto out;
break;
default:
break;
}
if (*error == NULL && ctx->state != STATE_PASSTHROUGH)
{
int line_number, char_number;
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: 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);
}
out:
if (*error)
{
int line_number, char_number;
g_markup_parse_context_get_position (context, &line_number, &char_number);
g_printerr ("%s:%d:%d: error: %s\n", ctx->file_path, line_number, char_number, (*error)->message);
}
}
static gboolean
require_one_of_end_elements (GMarkupParseContext *context,
ParseContext *ctx,
const char *actual_name,
GError **error,
...)
{
va_list args;
int line_number, char_number;
const char *expected;
gboolean matched = FALSE;
va_start (args, error);
while ((expected = va_arg (args, const char*)) != NULL)
{
if (strcmp (expected, actual_name) == 0)
{
matched = TRUE;
break;
}
}
va_end (args);
if (matched)
return TRUE;
g_markup_parse_context_get_position (context, &line_number, &char_number);
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Unexpected end tag '%s' on line %d char %d; current state=%d (prev=%d)",
actual_name,
line_number, char_number, ctx->state, ctx->prev_state);
return FALSE;
}
static gboolean
state_switch_end_struct_or_union (GMarkupParseContext *context,
ParseContext *ctx,
const char *element_name,
GError **error)
{
pop_node (ctx);
if (ctx->node_stack == NULL)
{
state_switch (ctx, STATE_NAMESPACE);
}
else
{
if (CURRENT_NODE (ctx)->type == GI_IR_NODE_STRUCT)
state_switch (ctx, STATE_STRUCT);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_UNION)
state_switch (ctx, STATE_UNION);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_OBJECT)
state_switch (ctx, STATE_CLASS);
else
{
int line_number, char_number;
g_markup_parse_context_get_position (context, &line_number, &char_number);
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Unexpected end tag '%s' on line %d char %d",
element_name,
line_number, char_number);
return FALSE;
}
}
return TRUE;
}
static gboolean
require_end_element (GMarkupParseContext *context,
ParseContext *ctx,
const char *expected_name,
const char *actual_name,
GError **error)
{
return require_one_of_end_elements (context, ctx, actual_name, error, expected_name, NULL);
}
static void
end_element_handler (GMarkupParseContext *context,
const char *element_name,
gpointer user_data,
GError **error)
{
ParseContext *ctx = user_data;
g_debug ("</%s>", element_name);
switch (ctx->state)
{
case STATE_START:
case STATE_END:
/* no need to GError here, GMarkup already catches this */
break;
case STATE_REPOSITORY:
state_switch (ctx, STATE_END);
break;
case STATE_INCLUDE:
if (require_end_element (context, ctx, "include", element_name, error))
{
state_switch (ctx, STATE_REPOSITORY);
}
break;
case STATE_C_INCLUDE:
if (require_end_element (context, ctx, "c:include", element_name, error))
{
state_switch (ctx, STATE_REPOSITORY);
}
break;
case STATE_PACKAGE:
if (require_end_element (context, ctx, "package", element_name, error))
{
state_switch (ctx, STATE_REPOSITORY);
}
break;
case STATE_NAMESPACE:
if (require_end_element (context, ctx, "namespace", element_name, error))
{
ctx->current_module = NULL;
state_switch (ctx, STATE_REPOSITORY);
}
break;
case STATE_ALIAS:
if (require_end_element (context, ctx, "alias", element_name, error))
{
g_free (ctx->current_alias);
ctx->current_alias = NULL;
state_switch (ctx, STATE_NAMESPACE);
}
break;
case STATE_FUNCTION_RETURN:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "return-value", element_name, error))
{
state_switch (ctx, STATE_FUNCTION);
}
break;
case STATE_FUNCTION_PARAMETERS:
if (require_end_element (context, ctx, "parameters", element_name, error))
{
state_switch (ctx, STATE_FUNCTION);
}
break;
case STATE_FUNCTION_PARAMETER:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "parameter", element_name, error))
{
state_switch (ctx, STATE_FUNCTION_PARAMETERS);
}
break;
case STATE_FUNCTION:
{
pop_node (ctx);
if (ctx->node_stack == NULL)
{
state_switch (ctx, STATE_NAMESPACE);
}
else
{
g_debug("case STATE_FUNCTION %d", CURRENT_NODE (ctx)->type);
if (ctx->in_embedded_state != STATE_NONE)
{
state_switch (ctx, ctx->in_embedded_state);
ctx->in_embedded_state = STATE_NONE;
}
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_INTERFACE)
state_switch (ctx, STATE_INTERFACE);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_OBJECT)
state_switch (ctx, STATE_CLASS);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_BOXED)
state_switch (ctx, STATE_BOXED);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_STRUCT)
state_switch (ctx, STATE_STRUCT);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_UNION)
state_switch (ctx, STATE_UNION);
else if (CURRENT_NODE (ctx)->type == GI_IR_NODE_ENUM ||
CURRENT_NODE (ctx)->type == GI_IR_NODE_FLAGS)
state_switch (ctx, STATE_ENUM);
else
{
int line_number, char_number;
g_markup_parse_context_get_position (context, &line_number, &char_number);
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Unexpected end tag '%s' on line %d char %d",
element_name,
line_number, char_number);
}
}
}
break;
case STATE_CLASS_FIELD:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "field", element_name, error))
{
state_switch (ctx, STATE_CLASS);
}
break;
case STATE_CLASS_PROPERTY:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "property", element_name, error))
{
state_switch (ctx, STATE_CLASS);
}
break;
case STATE_CLASS:
if (require_end_element (context, ctx, "class", element_name, error))
{
pop_node (ctx);
state_switch (ctx, STATE_NAMESPACE);
}
break;
case STATE_INTERFACE_PROPERTY:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "property", element_name, error))
{
state_switch (ctx, STATE_INTERFACE);
}
break;
case STATE_INTERFACE_FIELD:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "field", element_name, error))
{
state_switch (ctx, STATE_INTERFACE);
}
break;
case STATE_INTERFACE:
if (require_end_element (context, ctx, "interface", element_name, error))
{
pop_node (ctx);
state_switch (ctx, STATE_NAMESPACE);
}
break;
case STATE_ENUM:
if (strcmp ("member", element_name) == 0)
break;
else if (strcmp ("function", element_name) == 0)
break;
else if (require_one_of_end_elements (context, ctx,
element_name, error, "enumeration",
"bitfield", NULL))
{
pop_node (ctx);
state_switch (ctx, STATE_NAMESPACE);
}
break;
case STATE_BOXED:
if (require_end_element (context, ctx, "glib:boxed", element_name, error))
{
pop_node (ctx);
state_switch (ctx, STATE_NAMESPACE);
}
break;
case STATE_BOXED_FIELD:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "field", element_name, error))
{
state_switch (ctx, STATE_BOXED);
}
break;
case STATE_STRUCT_FIELD:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "field", element_name, error))
{
state_switch (ctx, STATE_STRUCT);
}
break;
case STATE_STRUCT:
if (require_end_element (context, ctx, "record", element_name, error))
{
state_switch_end_struct_or_union (context, ctx, element_name, error);
}
break;
case STATE_UNION_FIELD:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "field", element_name, error))
{
state_switch (ctx, STATE_UNION);
}
break;
case STATE_UNION:
if (require_end_element (context, ctx, "union", element_name, error))
{
state_switch_end_struct_or_union (context, ctx, element_name, error);
}
break;
case STATE_IMPLEMENTS:
if (strcmp ("interface", element_name) == 0)
break;
if (require_end_element (context, ctx, "implements", element_name, error))
state_switch (ctx, STATE_CLASS);
break;
case STATE_PREREQUISITE:
if (require_end_element (context, ctx, "prerequisite", element_name, error))
state_switch (ctx, STATE_INTERFACE);
break;
case STATE_NAMESPACE_CONSTANT:
case STATE_CLASS_CONSTANT:
case STATE_INTERFACE_CONSTANT:
if (strcmp ("type", element_name) == 0)
break;
if (require_end_element (context, ctx, "constant", element_name, error))
{
switch (ctx->state)
{
case STATE_NAMESPACE_CONSTANT:
pop_node (ctx);
state_switch (ctx, STATE_NAMESPACE);
break;
case STATE_CLASS_CONSTANT:
state_switch (ctx, STATE_CLASS);
break;
case STATE_INTERFACE_CONSTANT:
state_switch (ctx, STATE_INTERFACE);
break;
default:
g_assert_not_reached ();
break;
}
}
break;
case STATE_TYPE:
if ((strcmp ("type", element_name) == 0) || (strcmp ("array", element_name) == 0) ||
(strcmp ("varargs", element_name) == 0))
{
end_type (ctx);
}
break;
case STATE_ATTRIBUTE:
if (strcmp ("attribute", element_name) == 0)
{
state_switch (ctx, ctx->prev_state);
}
break;
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;
default:
g_error ("Unhandled state %d in end_element_handler", ctx->state);
}
}
static void
text_handler (GMarkupParseContext *context,
const char *text,
gsize text_len,
gpointer user_data,
GError **error)
{
/* FIXME warn about non-whitespace text */
}
static void
cleanup (GMarkupParseContext *context,
GError *error,
void *user_data)
{
ParseContext *ctx = user_data;
GList *m;
for (m = ctx->modules; m; m = m->next)
gi_ir_module_free (m->data);
g_list_free (ctx->modules);
ctx->modules = NULL;
ctx->current_module = NULL;
}
/**
* gi_ir_parser_parse_string:
* @parser: a #GIIrParser
* @namespace: the namespace of the string
* @filename: (nullable) (type filename): Path to parsed file, or `NULL`
* @buffer: (array length=length): the data containing the XML
* @length: length of the data, in bytes
* @error: return location for a [type@GLib.Error], or `NULL`
*
* Parse a string that holds a complete GIR XML file, and return a list of a
* a [class@GIRepository.IrModule] for each `<namespace/>` element within the
* file.
*
* Returns: (transfer none): a new [class@GIRepository.IrModule]
* Since: 2.80
*/
GIIrModule *
gi_ir_parser_parse_string (GIIrParser *parser,
const char *namespace,
const char *filename,
const char *buffer,
gssize length,
GError **error)
{
ParseContext ctx = { 0 };
GMarkupParseContext *context;
ctx.parser = parser;
ctx.state = STATE_START;
ctx.file_path = filename;
ctx.namespace = namespace;
ctx.include_modules = NULL;
ctx.aliases = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
ctx.disguised_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
ctx.pointer_structures = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
ctx.type_depth = 0;
ctx.dependencies = NULL;
ctx.current_module = NULL;
context = g_markup_parse_context_new (&firstpass_parser, 0, &ctx, NULL);
if (!g_markup_parse_context_parse (context, buffer, length, error))
goto out;
if (!g_markup_parse_context_end_parse (context, error))
goto out;
g_markup_parse_context_free (context);
ctx.state = STATE_START;
context = g_markup_parse_context_new (&markup_parser, 0, &ctx, NULL);
if (!g_markup_parse_context_parse (context, buffer, length, error))
goto out;
if (!g_markup_parse_context_end_parse (context, error))
goto out;
parser->parsed_modules = g_list_concat (g_list_copy (ctx.modules),
parser->parsed_modules);
out:
if (ctx.modules == NULL)
{
/* An error occurred before we created a module, so we haven't
* transferred ownership of these hash tables to the module.
*/
g_clear_pointer (&ctx.aliases, g_hash_table_unref);
g_clear_pointer (&ctx.disguised_structures, g_hash_table_unref);
g_clear_pointer (&ctx.pointer_structures, g_hash_table_unref);
g_list_free (ctx.include_modules);
}
g_markup_parse_context_free (context);
if (ctx.modules)
return ctx.modules->data;
if (error && *error == NULL)
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Expected namespace element in the gir file");
return NULL;
}
/**
* gi_ir_parser_parse_file:
* @parser: a #GIIrParser
* @filename: (type filename): filename to parse
* @error: return location for a [type@GLib.Error], or `NULL`
*
* Parse the given GIR XML file, and return a list of a
* [class@GIRepository.IrModule] for each `<namespace/>` element within the
* file.
*
* Returns: (transfer container): a newly allocated list of
* [class@GIRepository.IrModule]s. The modules themselves
* are owned by the `GIIrParser` and will be freed along with the parser.
* Since: 2.80
*/
GIIrModule *
gi_ir_parser_parse_file (GIIrParser *parser,
const char *filename,
GError **error)
{
char *buffer;
gsize length;
GIIrModule *module;
char *dash;
char *namespace;
if (!g_str_has_suffix (filename, ".gir"))
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_INVALID_CONTENT,
"Expected filename to end with '.gir'");
return NULL;
}
g_debug ("[parsing] filename %s", filename);
namespace = g_path_get_basename (filename);
namespace[strlen(namespace)-4] = '\0';
/* Remove version */
dash = strstr (namespace, "-");
if (dash != NULL)
*dash = '\0';
if (!g_file_get_contents (filename, &buffer, &length, error))
{
g_free (namespace);
return NULL;
}
module = gi_ir_parser_parse_string (parser, namespace, filename, buffer, length, error);
g_free (namespace);
g_free (buffer);
return module;
}