Branch data Line data Source code
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 = ((unsigned int)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 : 178 : compute_enum_storage_type (GIIrNodeEnum *enum_node)
74 : : {
75 : : GList *l;
76 : 178 : int64_t max_value = 0;
77 : 178 : int64_t min_value = 0;
78 : : int width;
79 : : gboolean signed_type;
80 : :
81 : 178 : if (enum_node->storage_type != GI_TYPE_TAG_VOID) /* already done */
82 : 12 : return;
83 : :
84 : 1463 : for (l = enum_node->values; l; l = l->next)
85 : : {
86 : 1297 : GIIrNodeValue *value = l->data;
87 : 1297 : if (value->value > max_value)
88 : 1111 : max_value = value->value;
89 : 1297 : if (value->value < min_value)
90 : 5 : min_value = value->value;
91 : : }
92 : :
93 : 166 : if (min_value < 0)
94 : : {
95 : 5 : signed_type = TRUE;
96 : :
97 : 5 : if (min_value > -128 && max_value <= 127)
98 : 2 : width = sizeof(Enum7);
99 : 3 : else if (min_value >= G_MINSHORT && max_value <= G_MAXSHORT)
100 : 3 : width = sizeof(Enum8);
101 : : else
102 : 0 : width = sizeof(Enum9);
103 : : }
104 : : else
105 : : {
106 : 161 : if (max_value <= 127)
107 : : {
108 : 145 : width = sizeof (Enum1);
109 : 145 : signed_type = (int64_t)(Enum1)(-1) < 0;
110 : : }
111 : 16 : else if (max_value <= 255)
112 : : {
113 : 4 : width = sizeof (Enum2);
114 : 4 : signed_type = (int64_t)(Enum2)(-1) < 0;
115 : : }
116 : 12 : else if (max_value <= G_MAXSHORT)
117 : : {
118 : 7 : width = sizeof (Enum3);
119 : 7 : signed_type = (int64_t)(Enum3)(-1) < 0;
120 : : }
121 : 5 : else if (max_value <= G_MAXUSHORT)
122 : : {
123 : 0 : width = sizeof (Enum4);
124 : 0 : signed_type = (int64_t)(Enum4)(-1) < 0;
125 : : }
126 : 5 : else if (max_value <= G_MAXINT)
127 : : {
128 : 4 : width = sizeof (Enum5);
129 : 4 : signed_type = (int64_t)(Enum5)(-1) < 0;
130 : : }
131 : : else
132 : : {
133 : 1 : width = sizeof (Enum6);
134 : 1 : signed_type = (int64_t)(Enum6)(-1) < 0;
135 : : }
136 : : }
137 : :
138 : 166 : if (width == 1)
139 : 0 : enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT8 : GI_TYPE_TAG_UINT8;
140 : 166 : else if (width == 2)
141 : 0 : enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT16 : GI_TYPE_TAG_UINT16;
142 : 166 : else if (width == 4)
143 : 166 : enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT32 : GI_TYPE_TAG_UINT32;
144 : 0 : else if (width == 8)
145 : 0 : enum_node->storage_type = signed_type ? GI_TYPE_TAG_INT64 : GI_TYPE_TAG_UINT64;
146 : : else
147 : 0 : g_error ("Unexpected enum width %d", width);
148 : : }
149 : :
150 : : static gboolean
151 : 12 : get_enum_size_alignment (GIIrNodeEnum *enum_node,
152 : : size_t *size,
153 : : size_t *alignment)
154 : : {
155 : : ffi_type *type_ffi;
156 : :
157 : 12 : compute_enum_storage_type (enum_node);
158 : :
159 : 12 : switch (enum_node->storage_type)
160 : : {
161 : 0 : case GI_TYPE_TAG_INT8:
162 : : case GI_TYPE_TAG_UINT8:
163 : 0 : type_ffi = &ffi_type_uint8;
164 : 0 : break;
165 : 0 : case GI_TYPE_TAG_INT16:
166 : : case GI_TYPE_TAG_UINT16:
167 : 0 : type_ffi = &ffi_type_uint16;
168 : 0 : break;
169 : 12 : case GI_TYPE_TAG_INT32:
170 : : case GI_TYPE_TAG_UINT32:
171 : 12 : type_ffi = &ffi_type_uint32;
172 : 12 : break;
173 : 0 : case GI_TYPE_TAG_INT64:
174 : : case GI_TYPE_TAG_UINT64:
175 : 0 : type_ffi = &ffi_type_uint64;
176 : 0 : break;
177 : 0 : default:
178 : 0 : g_error ("Unexpected enum storage type %s",
179 : : gi_type_tag_to_string (enum_node->storage_type));
180 : : }
181 : :
182 : 12 : *size = type_ffi->size;
183 : 12 : *alignment = type_ffi->alignment;
184 : :
185 : 12 : return TRUE;
186 : : }
187 : :
188 : : static gboolean
189 : 306 : get_interface_size_alignment (GIIrTypelibBuild *build,
190 : : GIIrNodeType *type,
191 : : size_t *size,
192 : : size_t *alignment,
193 : : const char *who)
194 : : {
195 : : GIIrNode *iface;
196 : :
197 : 306 : iface = gi_ir_find_node (build, ((GIIrNode*)type)->module, type->giinterface);
198 : 306 : if (!iface)
199 : : {
200 : 0 : gi_ir_module_fatal (build, 0, "Can't resolve type '%s' for %s", type->giinterface, who);
201 : : *size = 0;
202 : : *alignment = 0;
203 : : return FALSE;
204 : : }
205 : :
206 : 306 : gi_ir_node_compute_offsets (build, iface);
207 : :
208 : 306 : switch (iface->type)
209 : : {
210 : 0 : case GI_IR_NODE_BOXED:
211 : : {
212 : 0 : GIIrNodeBoxed *boxed = (GIIrNodeBoxed *)iface;
213 : 0 : *size = boxed->size;
214 : 0 : *alignment = boxed->alignment;
215 : 0 : break;
216 : : }
217 : 150 : case GI_IR_NODE_STRUCT:
218 : : {
219 : 150 : GIIrNodeStruct *struct_ = (GIIrNodeStruct *)iface;
220 : 150 : *size = struct_->size;
221 : 150 : *alignment = struct_->alignment;
222 : 150 : break;
223 : : }
224 : 104 : case GI_IR_NODE_OBJECT:
225 : : case GI_IR_NODE_INTERFACE:
226 : : {
227 : 104 : GIIrNodeInterface *interface = (GIIrNodeInterface *)iface;
228 : 104 : *size = interface->size;
229 : 104 : *alignment = interface->alignment;
230 : 104 : break;
231 : : }
232 : 3 : case GI_IR_NODE_UNION:
233 : : {
234 : 3 : GIIrNodeUnion *union_ = (GIIrNodeUnion *)iface;
235 : 3 : *size = union_->size;
236 : 3 : *alignment = union_->alignment;
237 : 3 : break;
238 : : }
239 : 12 : case GI_IR_NODE_ENUM:
240 : : case GI_IR_NODE_FLAGS:
241 : : {
242 : 12 : return get_enum_size_alignment ((GIIrNodeEnum *)iface,
243 : : size, alignment);
244 : : }
245 : 37 : case GI_IR_NODE_CALLBACK:
246 : : {
247 : 37 : *size = ffi_type_pointer.size;
248 : 37 : *alignment = ffi_type_pointer.alignment;
249 : 37 : break;
250 : : }
251 : 0 : default:
252 : : {
253 : 0 : 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 : 0 : *size = 0;
257 : 0 : *alignment = 0;
258 : 0 : return FALSE;
259 : : }
260 : : }
261 : :
262 : 294 : return *alignment > 0;
263 : : }
264 : :
265 : : static gboolean
266 : 1170 : get_type_size_alignment (GIIrTypelibBuild *build,
267 : : GIIrNodeType *type,
268 : : size_t *size,
269 : : size_t *alignment,
270 : : const char *who)
271 : : {
272 : : ffi_type *type_ffi;
273 : :
274 : 1170 : if (type->is_pointer)
275 : : {
276 : 547 : type_ffi = &ffi_type_pointer;
277 : : }
278 : 623 : else if (type->tag == GI_TYPE_TAG_ARRAY)
279 : : {
280 : : size_t elt_size;
281 : : size_t elt_alignment;
282 : :
283 : 48 : if (!type->has_size
284 : 48 : || !get_type_size_alignment(build, type->parameter_type1,
285 : : &elt_size, &elt_alignment, who))
286 : : {
287 : 0 : *size = 0;
288 : 0 : *alignment = 0;
289 : 0 : return FALSE;
290 : : }
291 : :
292 : 48 : *size = type->size * elt_size;
293 : 48 : *alignment = elt_alignment;
294 : :
295 : 48 : return TRUE;
296 : : }
297 : : else
298 : : {
299 : 575 : if (type->tag == GI_TYPE_TAG_INTERFACE)
300 : : {
301 : 306 : return get_interface_size_alignment (build, type, size, alignment, who);
302 : : }
303 : : else
304 : : {
305 : 269 : type_ffi = gi_type_tag_get_ffi_type (type->tag, type->is_pointer);
306 : :
307 : 269 : if (type_ffi == &ffi_type_void)
308 : : {
309 : 0 : g_warning ("%s has void type", who);
310 : 0 : *size = 0;
311 : 0 : *alignment = 0;
312 : 0 : return FALSE;
313 : : }
314 : 269 : else if (type_ffi == &ffi_type_pointer)
315 : : {
316 : 0 : g_warning ("%s has is not a pointer and is of type %s",
317 : : who,
318 : : gi_type_tag_to_string (type->tag));
319 : 0 : *size = 0;
320 : 0 : *alignment = 0;
321 : 0 : return FALSE;
322 : : }
323 : : }
324 : : }
325 : :
326 : 816 : g_assert (type_ffi);
327 : 816 : *size = type_ffi->size;
328 : 816 : *alignment = type_ffi->alignment;
329 : :
330 : 816 : return TRUE;
331 : : }
332 : :
333 : : static gboolean
334 : 1773 : get_field_size_alignment (GIIrTypelibBuild *build,
335 : : GIIrNodeField *field,
336 : : GIIrNode *parent_node,
337 : : size_t *size,
338 : : size_t *alignment)
339 : : {
340 : 1773 : GIIrModule *module = build->module;
341 : : char *who;
342 : : gboolean success;
343 : :
344 : 1773 : who = g_strdup_printf ("field %s.%s.%s", module->name, parent_node->name, ((GIIrNode *)field)->name);
345 : :
346 : 1773 : if (field->callback)
347 : : {
348 : 651 : *size = ffi_type_pointer.size;
349 : 651 : *alignment = ffi_type_pointer.alignment;
350 : 651 : success = TRUE;
351 : : }
352 : : else
353 : 1122 : success = get_type_size_alignment (build, field->type, size, alignment, who);
354 : 1773 : g_free (who);
355 : :
356 : 1773 : return success;
357 : : }
358 : :
359 : : #define GI_ALIGN(n, align) (((n) + (align) - 1) & ~((align) - 1))
360 : :
361 : : static gboolean
362 : 619 : compute_struct_field_offsets (GIIrTypelibBuild *build,
363 : : GIIrNode *node,
364 : : GList *members,
365 : : size_t *size_out,
366 : : size_t *alignment_out,
367 : : GIIrOffsetsState *offsets_state_out)
368 : : {
369 : 619 : size_t size = 0;
370 : 619 : size_t alignment = 1;
371 : : GList *l;
372 : 619 : gboolean have_error = FALSE;
373 : :
374 : 619 : *offsets_state_out = GI_IR_OFFSETS_IN_PROGRESS; /* mark to detect recursion */
375 : :
376 : 6627 : for (l = members; l; l = l->next)
377 : : {
378 : 6008 : GIIrNode *member = (GIIrNode *)l->data;
379 : :
380 : 6008 : if (member->type == GI_IR_NODE_FIELD)
381 : : {
382 : 1722 : GIIrNodeField *field = (GIIrNodeField *)member;
383 : :
384 : 1722 : if (!have_error)
385 : : {
386 : : size_t member_size;
387 : : size_t member_alignment;
388 : :
389 : 1722 : if (get_field_size_alignment (build, field, node,
390 : : &member_size, &member_alignment))
391 : : {
392 : 1722 : size = GI_ALIGN (size, member_alignment);
393 : 1722 : alignment = MAX (alignment, member_alignment);
394 : 1722 : field->offset = size;
395 : 1722 : field->offset_state = GI_IR_OFFSETS_COMPUTED;
396 : 1722 : size += member_size;
397 : : }
398 : : else
399 : 0 : have_error = TRUE;
400 : : }
401 : :
402 : 1722 : if (have_error)
403 : : {
404 : 0 : field->offset = 0;
405 : 0 : field->offset_state = GI_IR_OFFSETS_FAILED;
406 : : }
407 : : }
408 : 4286 : else if (member->type == GI_IR_NODE_CALLBACK)
409 : : {
410 : 0 : size = GI_ALIGN (size, ffi_type_pointer.alignment);
411 : 0 : alignment = MAX (alignment, ffi_type_pointer.alignment);
412 : 0 : size += ffi_type_pointer.size;
413 : : }
414 : : }
415 : :
416 : : /* Structs are tail-padded out to a multiple of their alignment */
417 : 619 : size = GI_ALIGN (size, alignment);
418 : :
419 : 619 : if (!have_error)
420 : : {
421 : 619 : *size_out = size;
422 : 619 : *alignment_out = alignment;
423 : 619 : *offsets_state_out = GI_IR_OFFSETS_COMPUTED;
424 : : }
425 : : else
426 : : {
427 : 0 : *size_out = 0;
428 : 0 : *alignment_out = 0;
429 : 0 : *offsets_state_out = GI_IR_OFFSETS_FAILED;
430 : : }
431 : :
432 : 619 : return !have_error;
433 : : }
434 : :
435 : : static gboolean
436 : 7 : compute_union_field_offsets (GIIrTypelibBuild *build,
437 : : GIIrNode *node,
438 : : GList *members,
439 : : size_t *size_out,
440 : : size_t *alignment_out,
441 : : GIIrOffsetsState *offsets_state_out)
442 : : {
443 : 7 : size_t size = 0;
444 : 7 : size_t alignment = 1;
445 : : GList *l;
446 : 7 : gboolean have_error = FALSE;
447 : :
448 : 7 : *offsets_state_out = GI_IR_OFFSETS_IN_PROGRESS; /* mark to detect recursion */
449 : :
450 : 63 : for (l = members; l; l = l->next)
451 : : {
452 : 56 : GIIrNode *member = (GIIrNode *)l->data;
453 : :
454 : 56 : if (member->type == GI_IR_NODE_FIELD)
455 : : {
456 : 51 : GIIrNodeField *field = (GIIrNodeField *)member;
457 : :
458 : 51 : if (!have_error)
459 : : {
460 : : size_t member_size;
461 : : size_t member_alignment;
462 : :
463 : 51 : if (get_field_size_alignment (build,field, node,
464 : : &member_size, &member_alignment))
465 : : {
466 : 51 : size = MAX (size, member_size);
467 : 51 : alignment = MAX (alignment, member_alignment);
468 : : }
469 : : else
470 : 0 : have_error = TRUE;
471 : : }
472 : : }
473 : : }
474 : :
475 : : /* Unions are tail-padded out to a multiple of their alignment */
476 : 7 : size = GI_ALIGN (size, alignment);
477 : :
478 : 7 : if (!have_error)
479 : : {
480 : 7 : *size_out = size;
481 : 7 : *alignment_out = alignment;
482 : 7 : *offsets_state_out = GI_IR_OFFSETS_COMPUTED;
483 : : }
484 : : else
485 : : {
486 : 0 : *size_out = 0;
487 : 0 : *alignment_out = 0;
488 : 0 : *offsets_state_out = GI_IR_OFFSETS_FAILED;
489 : : }
490 : :
491 : 7 : return !have_error;
492 : : }
493 : :
494 : : static gboolean
495 : 1382 : check_needs_computation (GIIrTypelibBuild *build,
496 : : GIIrNode *node,
497 : : GIIrOffsetsState offsets_state)
498 : : {
499 : 1382 : GIIrModule *module = build->module;
500 : :
501 : 1382 : if (offsets_state == GI_IR_OFFSETS_IN_PROGRESS)
502 : : {
503 : 0 : g_warning ("Recursion encountered when computing the size of %s.%s",
504 : : module->name, node->name);
505 : : }
506 : :
507 : 1382 : return offsets_state == GI_IR_OFFSETS_UNKNOWN;
508 : : }
509 : :
510 : : /*
511 : : * gi_ir_node_compute_offsets:
512 : : * @build: Current typelib build
513 : : * @node: a #GIIrNode
514 : : *
515 : : * If a node is a a structure or union, makes sure that the field
516 : : * offsets have been computed, and also computes the overall size and
517 : : * alignment for the type.
518 : : *
519 : : * Since: 2.80
520 : : */
521 : : void
522 : 60723 : gi_ir_node_compute_offsets (GIIrTypelibBuild *build,
523 : : GIIrNode *node)
524 : : {
525 : : gboolean appended_stack;
526 : :
527 : 60723 : if (build->stack)
528 : 60723 : appended_stack = node != (GIIrNode*)build->stack->data;
529 : : else
530 : 0 : appended_stack = TRUE;
531 : 60723 : if (appended_stack)
532 : 306 : build->stack = g_list_prepend (build->stack, node);
533 : :
534 : 60723 : switch (node->type)
535 : : {
536 : 70 : case GI_IR_NODE_BOXED:
537 : : {
538 : 70 : GIIrNodeBoxed *boxed = (GIIrNodeBoxed *)node;
539 : :
540 : 70 : if (!check_needs_computation (build, node, boxed->offsets_state))
541 : 35 : return;
542 : :
543 : 35 : compute_struct_field_offsets (build, node, boxed->members,
544 : : &boxed->size, &boxed->alignment, &boxed->offsets_state);
545 : 35 : break;
546 : : }
547 : 781 : case GI_IR_NODE_STRUCT:
548 : : {
549 : 781 : GIIrNodeStruct *struct_ = (GIIrNodeStruct *)node;
550 : :
551 : 781 : if (!check_needs_computation (build, node, struct_->offsets_state))
552 : 409 : return;
553 : :
554 : 372 : compute_struct_field_offsets (build, node, struct_->members,
555 : : &struct_->size, &struct_->alignment, &struct_->offsets_state);
556 : 372 : break;
557 : : }
558 : 518 : case GI_IR_NODE_OBJECT:
559 : : case GI_IR_NODE_INTERFACE:
560 : : {
561 : 518 : GIIrNodeInterface *iface = (GIIrNodeInterface *)node;
562 : :
563 : 518 : if (!check_needs_computation (build, node, iface->offsets_state))
564 : 306 : return;
565 : :
566 : 212 : compute_struct_field_offsets (build, node, iface->members,
567 : : &iface->size, &iface->alignment, &iface->offsets_state);
568 : 212 : break;
569 : : }
570 : 13 : case GI_IR_NODE_UNION:
571 : : {
572 : 13 : GIIrNodeUnion *union_ = (GIIrNodeUnion *)node;
573 : :
574 : 13 : if (!check_needs_computation (build, node, union_->offsets_state))
575 : 6 : return;
576 : :
577 : 7 : compute_union_field_offsets (build, (GIIrNode*)union_, union_->members,
578 : : &union_->size, &union_->alignment, &union_->offsets_state);
579 : 7 : break;
580 : : }
581 : 281 : case GI_IR_NODE_ENUM:
582 : : case GI_IR_NODE_FLAGS:
583 : : {
584 : 281 : GIIrNodeEnum *enum_ = (GIIrNodeEnum *)node;
585 : :
586 : 281 : if (enum_->storage_type != GI_TYPE_TAG_VOID) /* already done */
587 : 115 : return;
588 : :
589 : 166 : compute_enum_storage_type (enum_);
590 : :
591 : 166 : break;
592 : : }
593 : 59060 : default:
594 : 59060 : break;
595 : : }
596 : :
597 : 59852 : if (appended_stack)
598 : 85 : build->stack = g_list_delete_link (build->stack, build->stack);
599 : : }
|