1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 * GObject introspection: Typelib creation
3 *
4 * Copyright (C) 2005 Matthias Clasen
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 "config.h"
25
26 #include "girmodule-private.h"
27
28 #include "girnode-private.h"
29 #include "gitypelib-internal.h"
30
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34
35 #define ALIGN_VALUE(this, boundary) \
36 (( ((unsigned long)(this)) + (((unsigned long)(boundary)) -1)) & (~(((unsigned long)(boundary))-1)))
37
38 #define NUM_SECTIONS 2
39
40 GIIrModule *
41 gi_ir_module_new (const gchar *name,
42 const gchar *version,
43 const gchar *shared_library,
44 const gchar *c_prefix)
45 {
46 GIIrModule *module;
47
48 module = g_slice_new0 (GIIrModule);
49
50 module->name = g_strdup (name);
51 module->version = g_strdup (version);
52 if (shared_library)
53 module->shared_library = g_strdup (shared_library);
54 else
55 module->shared_library = NULL;
56 module->c_prefix = g_strdup (c_prefix);
57 module->dependencies = NULL;
58 module->entries = NULL;
59
60 module->include_modules = NULL;
61 module->aliases = NULL;
62
63 return module;
64 }
65
66 void
67 gi_ir_module_free (GIIrModule *module)
68 {
69 GList *e;
70
71 g_free (module->name);
72
73 for (e = module->entries; e; e = e->next)
74 gi_ir_node_free ((GIIrNode *)e->data);
75
76 g_list_free (module->entries);
77 /* Don't free dependencies, we inherit that from the parser */
78
79 g_list_free (module->include_modules);
80
81 g_hash_table_destroy (module->aliases);
82 g_hash_table_destroy (module->pointer_structures);
83 g_hash_table_destroy (module->disguised_structures);
84
85 g_slice_free (GIIrModule, module);
86 }
87
88 /**
89 * gi_ir_module_fatal:
90 * @build: Current build
91 * @line: Origin line number, or 0 if unknown
92 * @msg: printf-format string
93 * @args: Remaining arguments
94 *
95 * Report a fatal error, then exit.
96 *
97 * Since: 2.80
98 */
99 void
100 gi_ir_module_fatal (GIIrTypelibBuild *build,
101 guint line,
102 const char *msg,
103 ...)
104 {
105 GString *context;
106 char *formatted;
107 GList *link;
108
109 va_list args;
110
111 va_start (args, msg);
112
113 formatted = g_strdup_vprintf (msg, args);
114
115 context = g_string_new ("");
116 if (line > 0)
117 g_string_append_printf (context, "%d: ", line);
118 if (build->stack)
119 g_string_append (context, "In ");
120 for (link = g_list_last (build->stack); link; link = link->prev)
121 {
122 GIIrNode *node = link->data;
123 const char *name = node->name;
124 if (name)
125 g_string_append (context, name);
126 if (link->prev)
127 g_string_append (context, ".");
128 }
129 if (build->stack)
130 g_string_append (context, ": ");
131
132 g_printerr ("%s-%s.gir:%serror: %s\n", build->module->name,
133 build->module->version,
134 context->str, formatted);
135 g_string_free (context, TRUE);
136
137 exit (1);
138
139 va_end (args);
140 }
141
142 static void
143 add_alias_foreach (gpointer key,
144 gpointer value,
145 gpointer data)
146 {
147 GIIrModule *module = data;
148
149 g_hash_table_replace (module->aliases, g_strdup (key), g_strdup (value));
150 }
151
152 static void
153 add_pointer_structure_foreach (gpointer key,
154 gpointer value,
155 gpointer data)
156 {
157 GIIrModule *module = data;
158
159 g_hash_table_replace (module->pointer_structures, g_strdup (key), value);
160 }
161
162 static void
163 add_disguised_structure_foreach (gpointer key,
164 gpointer value,
165 gpointer data)
166 {
167 GIIrModule *module = data;
168
169 g_hash_table_replace (module->disguised_structures, g_strdup (key), value);
170 }
171
172 void
173 gi_ir_module_add_include_module (GIIrModule *module,
174 GIIrModule *include_module)
175 {
176 module->include_modules = g_list_prepend (module->include_modules,
177 include_module);
178
179 g_hash_table_foreach (include_module->aliases,
180 add_alias_foreach,
181 module);
182
183 g_hash_table_foreach (include_module->pointer_structures,
184 add_pointer_structure_foreach,
185 module);
186 g_hash_table_foreach (include_module->disguised_structures,
187 add_disguised_structure_foreach,
188 module);
189 }
190
191 struct AttributeWriteData
192 {
193 guint count;
194 guchar *databuf;
195 GIIrNode *node;
196 GHashTable *strings;
197 guint32 *offset;
198 guint32 *offset2;
199 };
200
201 static void
202 write_attribute (gpointer key, gpointer value, gpointer datap)
203 {
204 struct AttributeWriteData *data = datap;
205 guint32 old_offset = *(data->offset);
206 AttributeBlob *blob = (AttributeBlob*)&(data->databuf[old_offset]);
207
208 *(data->offset) += sizeof (AttributeBlob);
209
210 blob->offset = data->node->offset;
211 blob->name = gi_ir_write_string ((const char*) key, data->strings, data->databuf, data->offset2);
212 blob->value = gi_ir_write_string ((const char*) value, data->strings, data->databuf, data->offset2);
213
214 data->count++;
215 }
216
217 static guint
218 write_attributes (GIIrModule *module,
219 GIIrNode *node,
220 GHashTable *strings,
221 guchar *data,
222 guint32 *offset,
223 guint32 *offset2)
224 {
225 struct AttributeWriteData wdata;
226 wdata.count = 0;
227 wdata.databuf = data;
228 wdata.node = node;
229 wdata.offset = offset;
230 wdata.offset2 = offset2;
231 wdata.strings = strings;
232
233 g_hash_table_foreach (node->attributes, write_attribute, &wdata);
234
235 return wdata.count;
236 }
237
238 static gint
239 node_cmp_offset_func (gconstpointer a,
240 gconstpointer b)
241 {
242 const GIIrNode *na = a;
243 const GIIrNode *nb = b;
244 return na->offset - nb->offset;
245 }
246
247 static void
248 alloc_section (guint8 *data, SectionType section_id, guint32 offset)
249 {
250 int i;
251 Header *header = (Header*)data;
252 Section *section_data = (Section*)&data[header->sections];
253
254 g_assert (section_id != GI_SECTION_END);
255
256 for (i = 0; i < NUM_SECTIONS; i++)
257 {
258 if (section_data->id == GI_SECTION_END)
259 {
260 section_data->id = section_id;
261 section_data->offset = offset;
262 return;
263 }
264 section_data++;
265 }
266 g_assert_not_reached ();
267 }
268
269 static guint8*
270 add_directory_index_section (guint8 *data, GIIrModule *module, guint32 *offset2)
271 {
272 DirEntry *entry;
273 Header *header = (Header*)data;
274 GITypelibHashBuilder *dirindex_builder;
275 guint i, n_interfaces;
276 guint16 required_size;
277 guint32 new_offset;
278
279 dirindex_builder = gi_typelib_hash_builder_new ();
280
281 n_interfaces = ((Header *)data)->n_local_entries;
282
283 for (i = 0; i < n_interfaces; i++)
284 {
285 const char *str;
286 entry = (DirEntry *)&data[header->directory + (i * header->entry_blob_size)];
287 str = (const char *) (&data[entry->name]);
288 gi_typelib_hash_builder_add_string (dirindex_builder, str, i);
289 }
290
291 if (!gi_typelib_hash_builder_prepare (dirindex_builder))
292 {
293 /* This happens if CMPH couldn't create a perfect hash. So
294 * we just punt and leave no directory index section.
295 */
296 gi_typelib_hash_builder_destroy (dirindex_builder);
297 return data;
298 }
299
300 alloc_section (data, GI_SECTION_DIRECTORY_INDEX, *offset2);
301
302 required_size = gi_typelib_hash_builder_get_buffer_size (dirindex_builder);
303 required_size = ALIGN_VALUE (required_size, 4);
304
305 new_offset = *offset2 + required_size;
306
307 data = g_realloc (data, new_offset);
308
309 gi_typelib_hash_builder_pack (dirindex_builder, ((guint8*)data) + *offset2, required_size);
310
311 *offset2 = new_offset;
312
313 gi_typelib_hash_builder_destroy (dirindex_builder);
314 return data;
315 }
316
317 GITypelib *
318 gi_ir_module_build_typelib (GIIrModule *module)
319 {
320 GError *error = NULL;
321 GITypelib *typelib;
322 gsize length;
323 guint i;
324 GList *e;
325 Header *header;
326 DirEntry *entry;
327 guint32 header_size;
328 guint32 dir_size;
329 guint32 n_entries;
330 guint32 n_local_entries;
331 guint32 size, offset, offset2, old_offset;
332 GHashTable *strings;
333 GHashTable *types;
334 GList *nodes_with_attributes;
335 char *dependencies;
336 guchar *data;
337 Section *section;
338
339 header_size = ALIGN_VALUE (sizeof (Header), 4);
340 n_local_entries = g_list_length (module->entries);
341
342 /* Serialize dependencies into one string; this is convenient
343 * and not a major change to the typelib format. */
344 {
345 GString *dependencies_str = g_string_new ("");
346 GList *link;
347 for (link = module->dependencies; link; link = link->next)
348 {
349 const char *dependency = link->data;
350 if (!strcmp (dependency, module->name))
351 continue;
352 g_string_append (dependencies_str, dependency);
353 if (link->next)
354 g_string_append_c (dependencies_str, '|');
355 }
356 dependencies = g_string_free (dependencies_str, FALSE);
357 if (!dependencies[0])
358 {
359 g_free (dependencies);
360 dependencies = NULL;
361 }
362 }
363
364 restart:
365 gi_ir_node_init_stats ();
366 strings = g_hash_table_new (g_str_hash, g_str_equal);
367 types = g_hash_table_new (g_str_hash, g_str_equal);
368 nodes_with_attributes = NULL;
369 n_entries = g_list_length (module->entries);
370
371 g_message ("%d entries (%d local), %d dependencies\n", n_entries, n_local_entries,
372 g_list_length (module->dependencies));
373
374 dir_size = n_entries * sizeof (DirEntry);
375 size = header_size + dir_size;
376
377 size += ALIGN_VALUE (strlen (module->name) + 1, 4);
378
379 for (e = module->entries; e; e = e->next)
380 {
381 GIIrNode *node = e->data;
382
383 size += gi_ir_node_get_full_size (node);
384
385 /* Also reset the offset here */
386 node->offset = 0;
387 }
388
389 /* Adjust size for strings allocated in header below specially */
390 size += ALIGN_VALUE (strlen (module->name) + 1, 4);
391 if (module->shared_library)
392 size += ALIGN_VALUE (strlen (module->shared_library) + 1, 4);
393 if (dependencies != NULL)
394 size += ALIGN_VALUE (strlen (dependencies) + 1, 4);
395 if (module->c_prefix != NULL)
396 size += ALIGN_VALUE (strlen (module->c_prefix) + 1, 4);
397
398 size += sizeof (Section) * NUM_SECTIONS;
399
400 g_message ("allocating %d bytes (%d header, %d directory, %d entries)\n",
401 size, header_size, dir_size, size - header_size - dir_size);
402
403 data = g_malloc0 (size);
404
405 /* fill in header */
406 header = (Header *)data;
407 memcpy (header, GI_IR_MAGIC, 16);
408 header->major_version = 4;
409 header->minor_version = 0;
410 header->reserved = 0;
411 header->n_entries = n_entries;
412 header->n_local_entries = n_local_entries;
413 header->n_attributes = 0;
414 header->attributes = 0; /* filled in later */
415 /* NOTE: When writing strings to the typelib here, you should also update
416 * the size calculations above.
417 */
418 if (dependencies != NULL)
419 header->dependencies = gi_ir_write_string (dependencies, strings, data, &header_size);
420 else
421 header->dependencies = 0;
422 header->size = 0; /* filled in later */
423 header->namespace = gi_ir_write_string (module->name, strings, data, &header_size);
424 header->nsversion = gi_ir_write_string (module->version, strings, data, &header_size);
425 header->shared_library = (module->shared_library?
426 gi_ir_write_string (module->shared_library, strings, data, &header_size)
427 : 0);
428 if (module->c_prefix != NULL)
429 header->c_prefix = gi_ir_write_string (module->c_prefix, strings, data, &header_size);
430 else
431 header->c_prefix = 0;
432 header->entry_blob_size = sizeof (DirEntry);
433 header->function_blob_size = sizeof (FunctionBlob);
434 header->callback_blob_size = sizeof (CallbackBlob);
435 header->signal_blob_size = sizeof (SignalBlob);
436 header->vfunc_blob_size = sizeof (VFuncBlob);
437 header->arg_blob_size = sizeof (ArgBlob);
438 header->property_blob_size = sizeof (PropertyBlob);
439 header->field_blob_size = sizeof (FieldBlob);
440 header->value_blob_size = sizeof (ValueBlob);
441 header->constant_blob_size = sizeof (ConstantBlob);
442 header->error_domain_blob_size = 16; /* No longer used */
443 header->attribute_blob_size = sizeof (AttributeBlob);
444 header->signature_blob_size = sizeof (SignatureBlob);
445 header->enum_blob_size = sizeof (EnumBlob);
446 header->struct_blob_size = sizeof (StructBlob);
447 header->object_blob_size = sizeof(ObjectBlob);
448 header->interface_blob_size = sizeof (InterfaceBlob);
449 header->union_blob_size = sizeof (UnionBlob);
450
451 offset2 = ALIGN_VALUE (header_size, 4);
452 header->sections = offset2;
453
454 /* Initialize all the sections to _END/0; we fill them in later using
455 * alloc_section(). (Right now there's just the directory index
456 * though, note)
457 */
458 for (i = 0; i < NUM_SECTIONS; i++)
459 {
460 section = (Section*) &data[offset2];
461 section->id = GI_SECTION_END;
462 section->offset = 0;
463 offset2 += sizeof(Section);
464 }
465 header->directory = offset2;
466
467 /* fill in directory and content */
468 entry = (DirEntry *)&data[header->directory];
469
470 offset2 += dir_size;
471
472 for (e = module->entries, i = 0; e; e = e->next, i++)
473 {
474 GIIrTypelibBuild build;
475 GIIrNode *node = e->data;
476
477 if (strchr (node->name, '.'))
478 {
479 g_error ("Names may not contain '.'");
480 }
481
482 /* we picked up implicit xref nodes, start over */
483 if (i == n_entries)
484 {
485 GList *link;
486 g_message ("Found implicit cross references, starting over");
487
488 g_hash_table_destroy (strings);
489 g_hash_table_destroy (types);
490
491 /* Reset the cached offsets */
492 for (link = nodes_with_attributes; link; link = link->next)
493 ((GIIrNode *) link->data)->offset = 0;
494
495 g_list_free (nodes_with_attributes);
496 strings = NULL;
497
498 g_free (data);
499 data = NULL;
500
501 goto restart;
502 }
503
504 offset = offset2;
505
506 if (node->type == GI_IR_NODE_XREF)
507 {
508 const char *namespace = ((GIIrNodeXRef*)node)->namespace;
509
510 entry->blob_type = 0;
511 entry->local = FALSE;
512 entry->offset = gi_ir_write_string (namespace, strings, data, &offset2);
513 entry->name = gi_ir_write_string (node->name, strings, data, &offset2);
514 }
515 else
516 {
517 old_offset = offset;
518 offset2 = offset + gi_ir_node_get_size (node);
519
520 entry->blob_type = node->type;
521 entry->local = TRUE;
522 entry->offset = offset;
523 entry->name = gi_ir_write_string (node->name, strings, data, &offset2);
524
525 memset (&build, 0, sizeof (build));
526 build.module = module;
527 build.strings = strings;
528 build.types = types;
529 build.nodes_with_attributes = nodes_with_attributes;
530 build.n_attributes = header->n_attributes;
531 build.data = data;
532 gi_ir_node_build_typelib (node, NULL, &build, &offset, &offset2, NULL);
533
534 nodes_with_attributes = build.nodes_with_attributes;
535 header->n_attributes = build.n_attributes;
536
537 if (offset2 > old_offset + gi_ir_node_get_full_size (node))
538 g_error ("left a hole of %d bytes\n", offset2 - old_offset - gi_ir_node_get_full_size (node));
539 }
540
541 entry++;
542 }
543
544 /* GIBaseInfo expects the AttributeBlob array to be sorted on the field (offset) */
545 nodes_with_attributes = g_list_sort (nodes_with_attributes, node_cmp_offset_func);
546
547 g_message ("header: %d entries, %d attributes", header->n_entries, header->n_attributes);
548
549 gi_ir_node_dump_stats ();
550
551 /* Write attributes after the blobs */
552 offset = offset2;
553 header->attributes = offset;
554 offset2 = offset + header->n_attributes * header->attribute_blob_size;
555
556 for (e = nodes_with_attributes; e; e = e->next)
557 {
558 GIIrNode *node = e->data;
559 write_attributes (module, node, strings, data, &offset, &offset2);
560 }
561
562 g_message ("reallocating to %d bytes", offset2);
563
564 data = g_realloc (data, offset2);
565 header = (Header*) data;
566
567 data = add_directory_index_section (data, module, &offset2);
568 header = (Header *)data;
569
570 length = header->size = offset2;
571 typelib = gi_typelib_new_from_memory (data, length, &error);
572 if (!typelib)
573 {
574 g_error ("error building typelib: %s",
575 error->message);
576 }
577
578 g_hash_table_destroy (strings);
579 g_hash_table_destroy (types);
580 g_list_free (nodes_with_attributes);
581
582 return typelib;
583 }
584