Merge branch 'master' of ssh://walters@git.gnome.org/git/gobject-introspection
[gnome.gobject-introspection] / girepository / giroffsets.c
1 /* GObject introspection: Compute structure offsets
2  *
3  * Copyright (C) 2008 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "girffi.h"
22 #include "girnode.h"
23
24 /* The C standard specifies that an enumeration can be any char or any signed
25  * or unsigned integer type capable of resresenting all the values of the
26  * enumeration. We use test enumerations to figure out what choices the
27  * compiler makes.
28  */
29
30 typedef enum {
31   ENUM_1 = 1 /* compiler could use int8, uint8, int16, uint16, int32, uint32 */
32 } Enum1;
33
34 typedef enum {
35   ENUM_2 = 128 /* compiler could use uint8, int16, uint16, int32, uint32 */
36 } Enum2;
37
38 typedef enum {
39   ENUM_3 = 257 /* compiler could use int16, uint16, int32, uint32 */
40 } Enum3;
41
42 typedef enum {
43   ENUM_4 = G_MAXSHORT + 1 /* compiler could use uint16, int32, uint32 */
44 } Enum4;
45
46 typedef enum {
47   ENUM_5 = G_MAXUSHORT + 1 /* compiler could use int32, uint32 */
48 } Enum5;
49
50 typedef enum {
51   ENUM_6 = ((guint)G_MAXINT) + 1 /* compiler could use uint32 */
52 } Enum6;
53
54 /* GIrNodeValue has guint32 values, so if it matters to the ABI whether
55  * constant values are signed, we are in trouble. And we don't handle
56  * enums with > 32 bit values. */
57
58 #if 0
59 typedef enum {
60   ENUM_7 = -1 /* compiler could use int8, int16, int32 */
61 } Enum7;
62
63 /* etc... */
64 #endif
65
66 static void
67 compute_enum_storage_type (GIrNodeEnum *enum_node)
68 {
69   GList *l;
70   guint32 max_value = 0;
71   int width;
72
73   if (enum_node->storage_type != GI_TYPE_TAG_VOID) /* already done */
74     return;
75
76   for (l = enum_node->values; l; l = l->next)
77     {
78       GIrNodeValue *value = l->data;
79       if (value->value > max_value)
80         max_value = value->value;
81     }
82
83   if (max_value < 128)
84     width = sizeof (Enum1);
85   else if (max_value < 256)
86     width = sizeof (Enum2);
87   else if (max_value < G_MAXSHORT)
88     width = sizeof (Enum3);
89   else if (max_value < G_MAXUSHORT)
90     width = sizeof (Enum4);
91   else if (max_value < G_MAXINT)
92     width = sizeof (Enum5);
93   else
94     width = sizeof (Enum6);
95
96   if (width == 1)
97     enum_node->storage_type = GI_TYPE_TAG_UINT8;
98   else if (width == 2)
99     enum_node->storage_type = GI_TYPE_TAG_UINT16;
100   else if (width == 4)
101     enum_node->storage_type = GI_TYPE_TAG_UINT32;
102   else if (width == 8)
103     enum_node->storage_type = GI_TYPE_TAG_UINT64;
104   else
105     g_error ("Unexpected enum width %d", width);
106 }
107
108 static gboolean
109 get_enum_size_alignment (GIrNodeEnum *enum_node,
110                          gint        *size,
111                          gint        *alignment)
112 {
113   ffi_type *type_ffi;
114
115   compute_enum_storage_type (enum_node);
116
117   switch (enum_node->storage_type)
118     {
119     case GI_TYPE_TAG_INT8:
120     case GI_TYPE_TAG_UINT8:
121       type_ffi = &ffi_type_uint8;
122       break;
123     case GI_TYPE_TAG_INT16:
124     case GI_TYPE_TAG_UINT16:
125       type_ffi = &ffi_type_uint16;
126       break;
127     case GI_TYPE_TAG_INT32:
128     case GI_TYPE_TAG_UINT32:
129       type_ffi = &ffi_type_uint32;
130       break;
131     case GI_TYPE_TAG_INT64:
132     case GI_TYPE_TAG_UINT64:
133       type_ffi = &ffi_type_uint64;
134       break;
135     default:
136       g_error ("Unexpected enum storage type %s",
137                g_type_tag_to_string (enum_node->storage_type));
138     }
139
140   *size = type_ffi->size;
141   *alignment = type_ffi->alignment;
142
143   return TRUE;
144 }
145
146 static gboolean
147 get_interface_size_alignment (GIrNodeType *type,
148                               GIrModule   *module,
149                               GList       *modules,
150                               gint        *size,
151                               gint        *alignment,
152                               const char  *who)
153 {
154   GIrNode *iface;
155   GIrModule *iface_module;
156
157   if (!g_ir_find_node (module, modules, type->interface, &iface, &iface_module))
158     {
159       g_warning ("Can't resolve type '%s' for %s", type->interface, who);
160       *size = -1;
161       *alignment = -1;
162       return FALSE;
163     }
164
165   g_ir_node_compute_offsets (iface, iface_module,
166                              iface_module == module ? modules : NULL);
167
168   switch (iface->type)
169     {
170     case G_IR_NODE_BOXED:
171       {
172         GIrNodeBoxed *boxed = (GIrNodeBoxed *)iface;
173         *size = boxed->size;
174         *alignment = boxed->alignment;
175         break;
176       }
177     case G_IR_NODE_STRUCT:
178       {
179         GIrNodeStruct *struct_ = (GIrNodeStruct *)iface;
180         *size = struct_->size;
181         *alignment = struct_->alignment;
182         break;
183       }
184     case G_IR_NODE_OBJECT:
185     case G_IR_NODE_INTERFACE:
186       {
187         GIrNodeInterface *interface = (GIrNodeInterface *)iface;
188         *size = interface->size;
189         *alignment = interface->alignment;
190         break;
191       }
192     case G_IR_NODE_UNION:
193       {
194         GIrNodeUnion *union_ = (GIrNodeUnion *)iface;
195         *size = union_->size;
196         *alignment = union_->alignment;
197         break;
198       }
199     case G_IR_NODE_ENUM:
200     case G_IR_NODE_FLAGS:
201       {
202         return get_enum_size_alignment ((GIrNodeEnum *)iface,
203                                         size, alignment);
204       }
205     case G_IR_NODE_CALLBACK:
206       {
207         *size = ffi_type_pointer.size;
208         *alignment = ffi_type_pointer.alignment;
209         break;
210       }
211     default:
212       {
213         g_warning ("%s has is not a pointer and is of type %s",
214                    who,
215                    g_ir_node_type_to_string (iface->type));
216         *size = -1;
217         *alignment = -1;
218         break;
219       }
220     }
221
222   return *alignment > 0;
223 }
224
225 static gboolean
226 get_type_size_alignment (GIrNodeType *type,
227                          GIrModule   *module,
228                          GList       *modules,
229                          gint        *size,
230                          gint        *alignment,
231                          const char  *who)
232 {
233   ffi_type *type_ffi;
234
235   if (type->tag == GI_TYPE_TAG_ARRAY)
236     {
237       gint elt_size, elt_alignment;
238           
239       if (!type->has_size
240           || !get_type_size_alignment(type->parameter_type1, module, modules,
241                                       &elt_size, &elt_alignment, who))
242         {
243           *size = -1;
244           *alignment = -1;
245           return FALSE;
246         }
247           
248       *size = type->size * elt_size;
249       *alignment = elt_alignment;
250           
251       return TRUE;
252     }
253   else if (type->is_pointer)
254     {
255       type_ffi = &ffi_type_pointer;
256     }
257   else
258     {
259       if (type->tag == GI_TYPE_TAG_INTERFACE)
260         {
261           return get_interface_size_alignment (type, module, modules, size, alignment, who);
262         }
263       else
264         {
265           type_ffi = g_ir_ffi_get_ffi_type (type->tag);
266
267           if (type_ffi == &ffi_type_void)
268             {
269               g_warning ("%s has void type", who);
270               *size = -1;
271               *alignment = -1;
272               return FALSE;
273             }
274           else if (type_ffi == &ffi_type_pointer)
275             {
276               g_warning ("%s has is not a pointer and is of type %s",
277                          who,
278                          g_type_tag_to_string (type->tag));
279               *size = -1;
280               *alignment = -1;
281               return FALSE;
282             }
283         }
284     }
285
286   g_assert (type_ffi);
287   *size = type_ffi->size;
288   *alignment = type_ffi->alignment;
289
290   return TRUE;
291 }
292
293 static gboolean
294 get_field_size_alignment (GIrNodeField *field,
295                           GIrNode      *parent_node,
296                           GIrModule    *module,
297                           GList        *modules,
298                           gint         *size,
299                           gint         *alignment)
300 {
301   gchar *who;
302   gboolean success;
303   
304   who = g_strdup_printf ("field %s.%s.%s", module->name, parent_node->name, ((GIrNode *)field)->name);
305
306   success = get_type_size_alignment (field->type, module, modules, size, alignment, who);
307   g_free (who);
308
309   return success;
310 }
311
312 #define ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1))
313
314 static gboolean
315 compute_struct_field_offsets (GIrNode     *node,
316                               GList       *members,
317                               GIrModule   *module,
318                               GList       *modules,
319                               gint        *size_out,
320                               gint        *alignment_out)
321 {
322   int size = 0;
323   int alignment = 1;
324   GList *l;
325   gboolean have_error = FALSE;
326
327   *alignment_out = -2; /* mark to detect recursion */
328
329   for (l = members; l; l = l->next)
330     {
331       GIrNode *member = (GIrNode *)l->data;
332
333       if (member->type == G_IR_NODE_FIELD)
334         {
335           GIrNodeField *field = (GIrNodeField *)member;
336
337           if (!have_error)
338             {
339               int member_size;
340               int member_alignment;
341
342               if (get_field_size_alignment (field, node,
343                                             module, modules,
344                                             &member_size, &member_alignment))
345                 {
346                   size = ALIGN (size, member_alignment);
347                   alignment = MAX (alignment, member_alignment);
348                   field->offset = size;
349                   size += member_size;
350                 }
351               else
352                 have_error = TRUE;
353             }
354
355           if (have_error)
356             field->offset = -1;
357         }
358       else if (member->type == G_IR_NODE_CALLBACK)
359         {
360           size = ALIGN (size, ffi_type_pointer.alignment);
361           alignment = MAX (alignment, ffi_type_pointer.alignment);
362           size += ffi_type_pointer.size;
363         }
364     }
365
366   /* Structs are tail-padded out to a multiple of their alignment */
367   size = ALIGN (size, alignment);
368
369   if (!have_error)
370     {
371       *size_out = size;
372       *alignment_out = alignment;
373     }
374   else
375     {
376       *size_out = -1;
377       *alignment_out = -1;
378     }
379
380   return !have_error;
381 }
382
383 static gboolean
384 compute_union_field_offsets (GIrNode     *node,
385                              GList       *members,
386                              GIrModule   *module,
387                              GList       *modules,
388                              gint        *size_out,
389                              gint        *alignment_out)
390 {
391   int size = 0;
392   int alignment = 1;
393   GList *l;
394   gboolean have_error = FALSE;
395
396   *alignment_out = -2; /* mark to detect recursion */
397
398   for (l = members; l; l = l->next)
399     {
400       GIrNode *member = (GIrNode *)l->data;
401
402       if (member->type == G_IR_NODE_FIELD)
403         {
404           GIrNodeField *field = (GIrNodeField *)member;
405
406           if (!have_error)
407             {
408               int member_size;
409               int member_alignment;
410
411               if (get_field_size_alignment (field, node,
412                                             module, modules,
413                                             &member_size, &member_alignment))
414                 {
415                   size = MAX (size, member_size);
416                   alignment = MAX (alignment, member_alignment);
417                 }
418               else
419                 have_error = TRUE;
420             }
421         }
422     }
423
424   /* Unions are tail-padded out to a multiple of their alignment */
425   size = ALIGN (size, alignment);
426
427   if (!have_error)
428     {
429       *size_out = size;
430       *alignment_out = alignment;
431     }
432   else
433     {
434       *size_out = -1;
435       *alignment_out = -1;
436     }
437
438   return !have_error;
439 }
440
441 static gboolean
442 check_needs_computation (GIrNode   *node,
443                          GIrModule *module,
444                          gint       alignment)
445 {
446   /*
447    *  0: Not yet computed
448    * >0: Previously succeeded
449    * -1: Previously failed
450    * -2: In progress
451    */
452   if (alignment == -2)
453     {
454       g_warning ("Recursion encountered when computing the size of %s.%s",
455                  module->name, node->name);
456     }
457
458   return alignment == 0;
459 }
460
461 /**
462  * g_ir_node_compute_offsets:
463  * @node: a #GIrNode
464  * @module: Current module being processed
465  * @modules: all currently loaded modules
466  *
467  * If a node is a a structure or union, makes sure that the field
468  * offsets have been computed, and also computes the overall size and
469  * alignment for the type.
470  */
471 void
472 g_ir_node_compute_offsets (GIrNode   *node,
473                            GIrModule *module,
474                            GList     *modules)
475 {
476   switch (node->type)
477     {
478     case G_IR_NODE_BOXED:
479       {
480         GIrNodeBoxed *boxed = (GIrNodeBoxed *)node;
481
482         if (!check_needs_computation (node, module, boxed->alignment))
483           return;
484
485         compute_struct_field_offsets (node, boxed->members,
486                                       module, modules,
487                                       &boxed->size, &boxed->alignment);
488         break;
489       }
490     case G_IR_NODE_STRUCT:
491       {
492         GIrNodeStruct *struct_ = (GIrNodeStruct *)node;
493
494         if (!check_needs_computation (node, module, struct_->alignment))
495           return;
496
497         compute_struct_field_offsets (node, struct_->members,
498                                       module, modules,
499                                       &struct_->size, &struct_->alignment);
500         break;
501       }
502     case G_IR_NODE_OBJECT:
503     case G_IR_NODE_INTERFACE:
504       {
505         GIrNodeInterface *iface = (GIrNodeInterface *)node;
506
507         if (!check_needs_computation (node, module, iface->alignment))
508           return;
509
510         compute_struct_field_offsets (node, iface->members,
511                                       module, modules,
512                                       &iface->size, &iface->alignment);
513         break;
514       }
515     case G_IR_NODE_UNION:
516       {
517         GIrNodeUnion *union_ = (GIrNodeUnion *)node;
518
519         if (!check_needs_computation (node, module, union_->alignment))
520           return;
521
522         compute_union_field_offsets (node, union_->members,
523                                      module, modules,
524                                      &union_->size, &union_->alignment);
525         break;
526       }
527     case G_IR_NODE_ENUM:
528     case G_IR_NODE_FLAGS:
529       {
530         GIrNodeEnum *enum_ = (GIrNodeEnum *)node;
531
532         if (enum_->storage_type != GI_TYPE_TAG_VOID) /* already done */
533           return;
534
535         compute_enum_storage_type (enum_);
536
537         break;
538       }
539     default:
540       /* Nothing to do */
541       return;
542     }
543 }