summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@src.gnome.org>2008-11-11 05:10:36 +0000
committerOwen Taylor <otaylor@src.gnome.org>2008-11-11 05:10:36 +0000
commit7e1e368b9f09835a4c65c3e8ff7780523893180d (patch)
tree549f6f46dec594ee3429cda80d66abbbda85906b
parent570088cd5548cdc3e13b85b5e900457c3b5a94ea (diff)
downloadgobject-introspection-7e1e368b9f09835a4c65c3e8ff7780523893180d.tar.gz
Bug 560252 - Compute field offsets before writing typelib
girnode.h: Store the total size and alignment for GIrNodeStruct/Boxed/Union. giroffset.c: New file implementing computation of structure field offsets. girnode.c: Compute structure field offsets before writing types into the typelib. docs/typelib-format.txt: Document that a field offset of 0xFFFF means "unknown". Also fix description of the discriminator_offset field for unions. svn path=/trunk/; revision=876
-rw-r--r--ChangeLog17
-rw-r--r--docs/typelib-format.txt12
-rw-r--r--girepository/Makefile.am1
-rw-r--r--girepository/girnode.c7
-rw-r--r--girepository/girnode.h16
-rw-r--r--girepository/giroffsets.c413
6 files changed, 461 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index 5a69b2a4..244fff3f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,22 @@
2008-11-10 Owen Taylor <otaylor@redhat.com>
+ Bug 560252 - Compute field offsets before writing typelib
+
+ * girepository/girnode.h: Store the total size and alignment for
+ GIrNodeStruct/Boxed/Union.
+
+ * girepository/giroffset.c: New file implementing computation of
+ structure field offsets.
+
+ * girepository/girnode.c: Compute structure field offsets before
+ writing types into the typelib.
+
+ * docs/typelib-format.txt: Document that a field offset of 0xFFFF
+ means "unknown". Also fix description of the discriminator_offset
+ field for unions.
+
+2008-11-10 Owen Taylor <otaylor@redhat.com>
+
Bug 560250 - Fully parse included modules
For some things, like computing structure offsets to put into the typelib
diff --git a/docs/typelib-format.txt b/docs/typelib-format.txt
index 0c813ba8..80b29c9b 100644
--- a/docs/typelib-format.txt
+++ b/docs/typelib-format.txt
@@ -735,7 +735,8 @@ signal:
interface to which this virtual function belongs.
struct_offset:
- The offset of the function pointer in the class struct.
+ The offset of the function pointer in the class struct. The value
+ 0xFFFF indicates that the struct offset is unknown.
signature:
Offset of the SignatureBlob describing the parameter types and the
@@ -767,7 +768,8 @@ bits: If this field is part of a bitfield, the number of bits which it
uses, otherwise 0.
struct_offset:
- The offset of the field in the struct.
+ The offset of the field in the struct. The value 0xFFFF indicates
+ that the struct offset is unknown.
type: The type of the field.
@@ -1071,8 +1073,10 @@ gtype: For types which are registered with GType, contains the
n_fields: Length of the arrays
discriminator_offset:
- Offset from the beginning of the blob where the
- discriminator of a discriminated union is located
+ Offset from the beginning of the union where the
+ discriminator of a discriminated union is located.
+ The value 0xFFFF indicates that the discriminator offset
+ is unknown.
discriminator_type:
Type of the discriminator
diff --git a/girepository/Makefile.am b/girepository/Makefile.am
index 005672dc..c3694fd1 100644
--- a/girepository/Makefile.am
+++ b/girepository/Makefile.am
@@ -25,6 +25,7 @@ libgirepository_parser_la_SOURCES = \
girmodule.h \
girnode.c \
girnode.h \
+ giroffsets.c \
girparser.c \
girparser.h
libgirepository_parser_la_CFLAGS = $(GIREPO_CFLAGS)
diff --git a/girepository/girnode.c b/girepository/girnode.c
index b7f0cb27..87b418c8 100644
--- a/girepository/girnode.c
+++ b/girepository/girnode.c
@@ -1354,6 +1354,8 @@ g_ir_node_build_typelib (GIrNode *node,
node->name ? " " : "",
g_ir_node_type_to_string (node->type));
+ g_ir_node_compute_offsets (node, module, modules);
+
switch (node->type)
{
case G_IR_NODE_TYPE:
@@ -1525,7 +1527,10 @@ g_ir_node_build_typelib (GIrNode *node,
blob->writable = field->writable;
blob->reserved = 0;
blob->bits = 0;
- blob->struct_offset = field->offset;
+ if (field->offset >= 0)
+ blob->struct_offset = field->offset;
+ else
+ blob->struct_offset = 0xFFFF; /* mark as unknown */
g_ir_node_build_typelib ((GIrNode *)field->type,
module, modules, strings, types,
diff --git a/girepository/girnode.h b/girepository/girnode.h
index bc24a11c..971df6b9 100644
--- a/girepository/girnode.h
+++ b/girepository/girnode.h
@@ -271,6 +271,9 @@ struct _GIrNodeBoxed
gchar *gtype_name;
gchar *gtype_init;
+
+ gint alignment;
+ gint size;
GList *members;
};
@@ -284,6 +287,9 @@ struct _GIrNodeStruct
gchar *gtype_name;
gchar *gtype_init;
+
+ gint alignment;
+ gint size;
GList *members;
};
@@ -300,6 +306,9 @@ struct _GIrNodeUnion
gchar *gtype_name;
gchar *gtype_init;
+ gint alignment;
+ gint size;
+
gint discriminator_offset;
GIrNodeType *discriminator_type;
};
@@ -348,6 +357,13 @@ gboolean g_ir_find_node (GIrModule *module,
GIrNode **node_out,
GIrModule **module_out);
+/* In giroffsets.c */
+
+void g_ir_node_compute_offsets (GIrNode *node,
+ GIrModule *module,
+ GList *modules);
+
+
G_END_DECLS
#endif /* __G_IR_NODE_H__ */
diff --git a/girepository/giroffsets.c b/girepository/giroffsets.c
new file mode 100644
index 00000000..8489dc43
--- /dev/null
+++ b/girepository/giroffsets.c
@@ -0,0 +1,413 @@
+/* GObject introspection: Compute structure offsets
+ *
+ * Copyright (C) 2008 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "girffi.h"
+#include "girnode.h"
+
+/* The C standard specifies that an enumeration can be any char or any signed
+ * or unsigned integer type capable of resresenting all the values of the
+ * enumeration. We use test enumerations to figure out what choices the
+ * compiler makes.
+ */
+
+typedef enum {
+ ENUM_1 = 1 /* compiler could use int8, uint8, int16, uint16, int32, uint32 */
+} Enum1;
+
+typedef enum {
+ ENUM_2 = 128 /* compiler could use uint8, int16, uint16, int32, uint32 */
+} Enum2;
+
+typedef enum {
+ ENUM_3 = 257 /* compiler could use int16, uint16, int32, uint32 */
+} Enum3;
+
+typedef enum {
+ ENUM_4 = G_MAXSHORT + 1 /* compiler could use uint16, int32, uint32 */
+} Enum4;
+
+typedef enum {
+ ENUM_5 = G_MAXUSHORT + 1 /* compiler could use int32, uint32 */
+} Enum5;
+
+typedef enum {
+ ENUM_6 = ((guint)G_MAXINT) + 1 /* compiler could use uint32 */
+} Enum6;
+
+/* GIrNodeValue has guint32 values, so if it matters to the ABI whether
+ * constant values are signed, we are in trouble. And we don't handle
+ * enums with > 32 bit values. */
+
+#if 0
+typedef enum {
+ ENUM_7 = -1 /* compiler could use int8, int16, int32 */
+} Enum7;
+
+/* etc... */
+#endif
+
+static gboolean
+get_enum_size_alignment (GIrNodeEnum *enum_node,
+ gint *size,
+ gint *alignment)
+{
+ GList *l;
+ guint32 max_value = 0;
+ int width;
+ ffi_type *type_ffi;
+
+ for (l = enum_node->values; l; l = l->next)
+ {
+ GIrNodeValue *value = l->data;
+ if (value->value > max_value)
+ max_value = value->value;
+ }
+
+ if (max_value < 128)
+ width = sizeof (Enum1);
+ else if (max_value < 256)
+ width = sizeof (Enum2);
+ else if (max_value < G_MAXSHORT)
+ width = sizeof (Enum3);
+ else if (max_value < G_MAXUSHORT)
+ width = sizeof (Enum4);
+ else if (max_value < G_MAXINT)
+ width = sizeof (Enum5);
+ else
+ width = sizeof (Enum6);
+
+ if (width == 1)
+ type_ffi = &ffi_type_sint8;
+ else if (width == 2)
+ type_ffi = &ffi_type_sint16;
+ else if (width == 4)
+ type_ffi = &ffi_type_sint32;
+ else if (width == 8)
+ type_ffi = &ffi_type_sint64;
+ else
+ g_error ("Unexpected enum width %d", width);
+
+ *size = type_ffi->size;
+ *alignment = type_ffi->alignment;
+
+ return TRUE;
+}
+
+static gboolean
+get_interface_size_alignment (GIrNodeType *type,
+ GIrModule *module,
+ GList *modules,
+ gint *size,
+ gint *alignment)
+{
+ GIrNode *iface;
+ GIrModule *iface_module;
+
+ if (!g_ir_find_node (module, modules, type->interface, &iface, &iface_module))
+ {
+ g_warning ("Type for type name '%s' not found", type->interface);
+ *size = -1;
+ *alignment = -1;
+ return FALSE;
+ }
+
+ g_ir_node_compute_offsets (iface, iface_module,
+ iface_module == module ? modules : NULL);
+
+ switch (iface->type)
+ {
+ case G_IR_NODE_BOXED:
+ {
+ GIrNodeBoxed *boxed = (GIrNodeBoxed *)iface;
+ *size = boxed->size;
+ *alignment = boxed->alignment;
+ break;
+ }
+ case G_IR_NODE_STRUCT:
+ {
+ GIrNodeStruct *struct_ = (GIrNodeStruct *)iface;
+ *size = struct_->size;
+ *alignment = struct_->alignment;
+ break;
+ }
+ case G_IR_NODE_UNION:
+ {
+ GIrNodeUnion *union_ = (GIrNodeUnion *)iface;
+ *size = union_->size;
+ *alignment = union_->alignment;
+ break;
+ }
+ case G_IR_NODE_ENUM:
+ case G_IR_NODE_FLAGS:
+ {
+ return get_enum_size_alignment ((GIrNodeEnum *)iface,
+ size, alignment);
+ }
+ case G_IR_NODE_CALLBACK:
+ {
+ *size = ffi_type_pointer.size;
+ *alignment = ffi_type_pointer.alignment;
+ break;
+ }
+ default:
+ {
+ g_warning ("Unexpected non-pointer field of type %s in structure",
+ g_ir_node_type_to_string (iface->type));
+ *size = -1;
+ *alignment = -1;
+ break;
+ }
+ }
+
+ return *alignment != -1;
+}
+
+static gboolean
+get_field_size_alignment (GIrNodeField *field,
+ GIrModule *module,
+ GList *modules,
+ gint *size,
+ gint *alignment)
+{
+ GIrNodeType *type = field->type;
+ ffi_type *type_ffi;
+
+ if (type->is_pointer)
+ {
+ type_ffi = &ffi_type_pointer;
+ }
+ else
+ {
+ if (type->tag == GI_TYPE_TAG_INTERFACE)
+ {
+ return get_interface_size_alignment (type,
+ module, modules,
+ size, alignment);
+ }
+ else
+ {
+ type_ffi = g_ir_ffi_get_ffi_type (type->tag);
+
+ if (type_ffi == &ffi_type_void)
+ {
+ g_warning ("field '%s' has void type", ((GIrNode *)field)->name);
+ *size = -1;
+ *alignment = -1;
+ return FALSE;
+ }
+ else if (type_ffi == &ffi_type_pointer)
+ {
+ g_warning ("non-pointer field '%s' has unhandled type %s",
+ ((GIrNode *)field)->name,
+ g_type_tag_to_string (type->tag));
+ *size = -1;
+ *alignment = -1;
+ return FALSE;
+ }
+ }
+ }
+
+ g_assert (type_ffi);
+ *size = type_ffi->size;
+ *alignment = type_ffi->alignment;
+
+ return TRUE;
+}
+
+#define ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1))
+
+static gboolean
+compute_struct_field_offsets (GList *members,
+ GIrModule *module,
+ GList *modules,
+ gint *size_out,
+ gint *alignment_out)
+{
+ int size = 0;
+ int alignment = 1;
+ GList *l;
+ gboolean have_error = FALSE;
+
+ for (l = members; l; l = l->next)
+ {
+ GIrNode *member = (GIrNode *)l->data;
+
+ if (member->type == G_IR_NODE_FIELD)
+ {
+ GIrNodeField *field = (GIrNodeField *)member;
+
+ if (!have_error)
+ {
+ int member_size;
+ int member_alignment;
+
+ if (get_field_size_alignment (field,
+ module, modules,
+ &member_size, &member_alignment))
+ {
+ size = ALIGN (size, member_alignment);
+ alignment = MAX (alignment, member_alignment);
+ field->offset = size;
+ size += member_size;
+ }
+ else
+ have_error = TRUE;
+ }
+
+ if (have_error)
+ field->offset = -1;
+ }
+ else if (member->type == G_IR_NODE_CALLBACK)
+ {
+ size = ffi_type_pointer.size;
+ alignment = ffi_type_pointer.alignment;
+ }
+ }
+
+ /* Structs are tail-padded out to a multiple of their alignment */
+ size = ALIGN (size, alignment);
+
+ if (!have_error)
+ {
+ *size_out = size;
+ *alignment_out = alignment;
+ }
+ else
+ {
+ *size_out = -1;
+ *alignment_out = -1;
+ }
+
+ return !have_error;
+}
+
+static gboolean
+compute_union_field_offsets (GList *members,
+ GIrModule *module,
+ GList *modules,
+ gint *size_out,
+ gint *alignment_out)
+{
+ int size = 0;
+ int alignment = 1;
+ GList *l;
+ gboolean have_error = FALSE;
+
+ for (l = members; l; l = l->next)
+ {
+ GIrNode *member = (GIrNode *)l->data;
+
+ if (member->type == G_IR_NODE_FIELD)
+ {
+ GIrNodeField *field = (GIrNodeField *)member;
+
+ if (!have_error)
+ {
+ int member_size;
+ int member_alignment;
+
+ if (get_field_size_alignment (field,
+ module, modules,
+ &member_size, &member_alignment))
+ {
+ size = MAX (size, member_size);
+ alignment = MAX (alignment, member_alignment);
+ }
+ else
+ have_error = TRUE;
+ }
+ }
+ }
+
+ /* Unions are tail-padded out to a multiple of their alignment */
+ size = ALIGN (size, alignment);
+
+ if (!have_error)
+ {
+ *size_out = size;
+ *alignment_out = alignment;
+ }
+ else
+ {
+ *size_out = -1;
+ *alignment_out = -1;
+ }
+
+ return !have_error;
+}
+
+/**
+ * g_ir_node_compute_offsets:
+ * @node: a #GIrNode
+ * @module: Current module being processed
+ * @moudles: all currently loaded modules
+ *
+ * If a node is a a structure or union, makes sure that the field
+ * offsets have been computed, and also computes the overall size and
+ * alignment for the type.
+ */
+void
+g_ir_node_compute_offsets (GIrNode *node,
+ GIrModule *module,
+ GList *modules)
+{
+ switch (node->type)
+ {
+ case G_IR_NODE_BOXED:
+ {
+ GIrNodeBoxed *boxed = (GIrNodeBoxed *)node;
+
+ if (boxed->alignment != 0) /* Already done */
+ return;
+
+ compute_struct_field_offsets (boxed->members,
+ module, modules,
+ &boxed->size, &boxed->alignment);
+ break;
+ }
+ case G_IR_NODE_STRUCT:
+ {
+ GIrNodeStruct *struct_ = (GIrNodeStruct *)node;
+
+ if (struct_->alignment != 0)
+ return;
+
+ compute_struct_field_offsets (struct_->members,
+ module, modules,
+ &struct_->size, &struct_->alignment);
+ break;
+ }
+ case G_IR_NODE_UNION:
+ {
+ GIrNodeUnion *union_ = (GIrNodeUnion *)node;
+
+ if (union_->alignment != 0)
+ return;
+
+ compute_union_field_offsets (union_->members,
+ module, modules,
+ &union_->size, &union_->alignment);
+ break;
+ }
+ default:
+ /* Nothing to do */
+ return;
+ }
+}