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