[girffi] Clean up API, add g_function_info_prep_invoker
[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-private.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, type->is_pointer);
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   if (field->callback)
307     {
308       *size = ffi_type_pointer.size;
309       *alignment = ffi_type_pointer.alignment;
310       success = TRUE;
311     }
312   else
313     success = get_type_size_alignment (field->type, module, modules, size, alignment, who);
314   g_free (who);
315
316   return success;
317 }
318
319 #define ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1))
320
321 static gboolean
322 compute_struct_field_offsets (GIrNode     *node,
323                               GList       *members,
324                               GIrModule   *module,
325                               GList       *modules,
326                               gint        *size_out,
327                               gint        *alignment_out)
328 {
329   int size = 0;
330   int alignment = 1;
331   GList *l;
332   gboolean have_error = FALSE;
333
334   *alignment_out = -2; /* mark to detect recursion */
335
336   for (l = members; l; l = l->next)
337     {
338       GIrNode *member = (GIrNode *)l->data;
339
340       if (member->type == G_IR_NODE_FIELD)
341         {
342           GIrNodeField *field = (GIrNodeField *)member;
343
344           if (!have_error)
345             {
346               int member_size;
347               int member_alignment;
348
349               if (get_field_size_alignment (field, node,
350                                             module, modules,
351                                             &member_size, &member_alignment))
352                 {
353                   size = ALIGN (size, member_alignment);
354                   alignment = MAX (alignment, member_alignment);
355                   field->offset = size;
356                   size += member_size;
357                 }
358               else
359                 have_error = TRUE;
360             }
361
362           if (have_error)
363             field->offset = -1;
364         }
365       else if (member->type == G_IR_NODE_CALLBACK)
366         {
367           size = ALIGN (size, ffi_type_pointer.alignment);
368           alignment = MAX (alignment, ffi_type_pointer.alignment);
369           size += ffi_type_pointer.size;
370         }
371     }
372
373   /* Structs are tail-padded out to a multiple of their alignment */
374   size = ALIGN (size, alignment);
375
376   if (!have_error)
377     {
378       *size_out = size;
379       *alignment_out = alignment;
380     }
381   else
382     {
383       *size_out = -1;
384       *alignment_out = -1;
385     }
386
387   return !have_error;
388 }
389
390 static gboolean
391 compute_union_field_offsets (GIrNode     *node,
392                              GList       *members,
393                              GIrModule   *module,
394                              GList       *modules,
395                              gint        *size_out,
396                              gint        *alignment_out)
397 {
398   int size = 0;
399   int alignment = 1;
400   GList *l;
401   gboolean have_error = FALSE;
402
403   *alignment_out = -2; /* mark to detect recursion */
404
405   for (l = members; l; l = l->next)
406     {
407       GIrNode *member = (GIrNode *)l->data;
408
409       if (member->type == G_IR_NODE_FIELD)
410         {
411           GIrNodeField *field = (GIrNodeField *)member;
412
413           if (!have_error)
414             {
415               int member_size;
416               int member_alignment;
417
418               if (get_field_size_alignment (field, node,
419                                             module, modules,
420                                             &member_size, &member_alignment))
421                 {
422                   size = MAX (size, member_size);
423                   alignment = MAX (alignment, member_alignment);
424                 }
425               else
426                 have_error = TRUE;
427             }
428         }
429     }
430
431   /* Unions are tail-padded out to a multiple of their alignment */
432   size = ALIGN (size, alignment);
433
434   if (!have_error)
435     {
436       *size_out = size;
437       *alignment_out = alignment;
438     }
439   else
440     {
441       *size_out = -1;
442       *alignment_out = -1;
443     }
444
445   return !have_error;
446 }
447
448 static gboolean
449 check_needs_computation (GIrNode   *node,
450                          GIrModule *module,
451                          gint       alignment)
452 {
453   /*
454    *  0: Not yet computed
455    * >0: Previously succeeded
456    * -1: Previously failed
457    * -2: In progress
458    */
459   if (alignment == -2)
460     {
461       g_warning ("Recursion encountered when computing the size of %s.%s",
462                  module->name, node->name);
463     }
464
465   return alignment == 0;
466 }
467
468 /**
469  * g_ir_node_compute_offsets:
470  * @node: a #GIrNode
471  * @module: Current module being processed
472  * @modules: all currently loaded modules
473  *
474  * If a node is a a structure or union, makes sure that the field
475  * offsets have been computed, and also computes the overall size and
476  * alignment for the type.
477  */
478 void
479 g_ir_node_compute_offsets (GIrNode   *node,
480                            GIrModule *module,
481                            GList     *modules)
482 {
483   switch (node->type)
484     {
485     case G_IR_NODE_BOXED:
486       {
487         GIrNodeBoxed *boxed = (GIrNodeBoxed *)node;
488
489         if (!check_needs_computation (node, module, boxed->alignment))
490           return;
491
492         compute_struct_field_offsets (node, boxed->members,
493                                       module, modules,
494                                       &boxed->size, &boxed->alignment);
495         break;
496       }
497     case G_IR_NODE_STRUCT:
498       {
499         GIrNodeStruct *struct_ = (GIrNodeStruct *)node;
500
501         if (!check_needs_computation (node, module, struct_->alignment))
502           return;
503
504         compute_struct_field_offsets (node, struct_->members,
505                                       module, modules,
506                                       &struct_->size, &struct_->alignment);
507         break;
508       }
509     case G_IR_NODE_OBJECT:
510     case G_IR_NODE_INTERFACE:
511       {
512         GIrNodeInterface *iface = (GIrNodeInterface *)node;
513
514         if (!check_needs_computation (node, module, iface->alignment))
515           return;
516
517         compute_struct_field_offsets (node, iface->members,
518                                       module, modules,
519                                       &iface->size, &iface->alignment);
520         break;
521       }
522     case G_IR_NODE_UNION:
523       {
524         GIrNodeUnion *union_ = (GIrNodeUnion *)node;
525
526         if (!check_needs_computation (node, module, union_->alignment))
527           return;
528
529         compute_union_field_offsets (node, union_->members,
530                                      module, modules,
531                                      &union_->size, &union_->alignment);
532         break;
533       }
534     case G_IR_NODE_ENUM:
535     case G_IR_NODE_FLAGS:
536       {
537         GIrNodeEnum *enum_ = (GIrNodeEnum *)node;
538
539         if (enum_->storage_type != GI_TYPE_TAG_VOID) /* already done */
540           return;
541
542         compute_enum_storage_type (enum_);
543
544         break;
545       }
546     default:
547       /* Nothing to do */
548       return;
549     }
550 }