diff options
author | Owen Taylor <otaylor@src.gnome.org> | 2008-11-11 05:10:36 +0000 |
---|---|---|
committer | Owen Taylor <otaylor@src.gnome.org> | 2008-11-11 05:10:36 +0000 |
commit | 7e1e368b9f09835a4c65c3e8ff7780523893180d (patch) | |
tree | 549f6f46dec594ee3429cda80d66abbbda85906b | |
parent | 570088cd5548cdc3e13b85b5e900457c3b5a94ea (diff) | |
download | gobject-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-- | ChangeLog | 17 | ||||
-rw-r--r-- | docs/typelib-format.txt | 12 | ||||
-rw-r--r-- | girepository/Makefile.am | 1 | ||||
-rw-r--r-- | girepository/girnode.c | 7 | ||||
-rw-r--r-- | girepository/girnode.h | 16 | ||||
-rw-r--r-- | girepository/giroffsets.c | 413 |
6 files changed, 461 insertions, 5 deletions
@@ -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; + } +} |