Bug 560252 - Compute field offsets before writing typelib
authorOwen Taylor <otaylor@src.gnome.org>
Tue, 11 Nov 2008 05:10:36 +0000 (05:10 +0000)
committerOwen Taylor <otaylor@src.gnome.org>
Tue, 11 Nov 2008 05:10:36 +0000 (05:10 +0000)
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

ChangeLog
docs/typelib-format.txt
girepository/Makefile.am
girepository/girnode.c
girepository/girnode.h
girepository/giroffsets.c [new file with mode: 0644]

index 5a69b2a..244fff3 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+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
index 0c813ba..80b29c9 100644 (file)
@@ -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 
index 005672d..c3694fd 100644 (file)
@@ -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)
index b7f0cb2..87b418c 100644 (file)
@@ -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,
index bc24a11..971df6b 100644 (file)
@@ -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 (file)
index 0000000..8489dc4
--- /dev/null
@@ -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;
+    }
+}