Branch data Line data Source code
1 : : /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2 : : * GObject introspection: Repository implementation
3 : : *
4 : : * Copyright (C) 2005 Matthias Clasen
5 : : * Copyright (C) 2008 Colin Walters <walters@verbum.org>
6 : : * Copyright (C) 2008 Red Hat, Inc.
7 : : *
8 : : * SPDX-License-Identifier: LGPL-2.1-or-later
9 : : *
10 : : * This library is free software; you can redistribute it and/or
11 : : * modify it under the terms of the GNU Lesser General Public
12 : : * License as published by the Free Software Foundation; either
13 : : * version 2 of the License, or (at your option) any later version.
14 : : *
15 : : * This library is distributed in the hope that it will be useful,
16 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 : : * Lesser General Public License for more details.
19 : : *
20 : : * You should have received a copy of the GNU Lesser General Public
21 : : * License along with this library; if not, write to the
22 : : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
23 : : * Boston, MA 02111-1307, USA.
24 : : */
25 : :
26 : : #include "config.h"
27 : :
28 : : #include <stdio.h>
29 : : #include <string.h>
30 : : #include <stdlib.h>
31 : :
32 : : #include <glib.h>
33 : : #include <glib-private.h>
34 : : #include <glib/gprintf.h>
35 : : #include <gmodule.h>
36 : : #include "gibaseinfo-private.h"
37 : : #include "girepository.h"
38 : : #include "gitypelib-internal.h"
39 : : #include "girepository-private.h"
40 : :
41 : : /**
42 : : * GIRepository:
43 : : *
44 : : * `GIRepository` is used to manage repositories of namespaces. Namespaces
45 : : * are represented on disk by type libraries (`.typelib` files).
46 : : *
47 : : * The individual pieces of API within a type library are represented by
48 : : * subclasses of [class@GIRepository.BaseInfo]. These can be found using
49 : : * methods like [method@GIRepository.Repository.find_by_name] or
50 : : * [method@GIRepository.Repository.get_info].
51 : : *
52 : : * You are responsible for ensuring that the lifetime of the
53 : : * [class@GIRepository.Repository] exceeds that of the lifetime of any of its
54 : : * [class@GIRepository.BaseInfo]s. This cannot be guaranteed by using internal
55 : : * references within libgirepository as that would affect performance.
56 : : *
57 : : * ### Discovery of type libraries
58 : : *
59 : : * `GIRepository` will typically look for a `girepository-1.0` directory
60 : : * under the library directory used when compiling gobject-introspection. On a
61 : : * standard Linux system this will end up being `/usr/lib/girepository-1.0`.
62 : : *
63 : : * It is possible to control the search paths programmatically, using
64 : : * [method@GIRepository.Repository.prepend_search_path]. It is also possible to
65 : : * modify the search paths by using the `GI_TYPELIB_PATH` environment variable.
66 : : * The environment variable takes precedence over the default search path
67 : : * and the [method@GIRepository.Repository.prepend_search_path] calls.
68 : : *
69 : : * ### Namespace ordering
70 : : *
71 : : * In situations where namespaces may be searched in order, or returned in a
72 : : * list, the namespaces will be returned in alphabetical order, with all fully
73 : : * loaded namespaces being returned before any lazily loaded ones (those loaded
74 : : * with `GI_REPOSITORY_LOAD_FLAG_LAZY`). This allows for deterministic and
75 : : * reproducible results.
76 : : *
77 : : * Similarly, if a symbol (such as a `GType` or error domain) is being searched
78 : : * for in the set of loaded namespaces, the namespaces will be searched in that
79 : : * order. In particular, this means that a symbol which exists in two namespaces
80 : : * will always be returned from the alphabetically-higher namespace. This should
81 : : * only happen in the case of `Gio` and `GioUnix`/`GioWin32`, which all refer to
82 : : * the same `.so` file and expose overlapping sets of symbols. Symbols should
83 : : * always end up being resolved to `GioUnix` or `GioWin32` if they are platform
84 : : * dependent, rather than `Gio` itself.
85 : : *
86 : : * Since: 2.80
87 : : */
88 : :
89 : : /* The namespace and version corresponding to libgirepository itself, so
90 : : * that we can refuse to load typelibs corresponding to the older,
91 : : * incompatible version of this same library in gobject-introspection. */
92 : : #define GIREPOSITORY_TYPELIB_NAME "GIRepository"
93 : : #define GIREPOSITORY_TYPELIB_VERSION "3.0"
94 : : #define GIREPOSITORY_TYPELIB_FILENAME \
95 : : GIREPOSITORY_TYPELIB_NAME "-" GIREPOSITORY_TYPELIB_VERSION ".typelib"
96 : :
97 : : typedef struct {
98 : : size_t n_interfaces;
99 : : GIBaseInfo *interfaces[];
100 : : } GTypeInterfaceCache;
101 : :
102 : : static void
103 : 2 : gtype_interface_cache_free (gpointer data)
104 : : {
105 : 2 : GTypeInterfaceCache *cache = data;
106 : :
107 : 6 : for (size_t i = 0; i < cache->n_interfaces; i++)
108 : 4 : gi_base_info_unref ((GIBaseInfo*) cache->interfaces[i]);
109 : 2 : g_free (cache);
110 : 2 : }
111 : :
112 : : struct _GIRepository
113 : : {
114 : : GObject parent;
115 : :
116 : : GPtrArray *typelib_search_path; /* (element-type filename) (owned) */
117 : : GPtrArray *library_paths; /* (element-type filename) (owned) */
118 : :
119 : : /* Certain operations require iterating over the typelibs and the iteration
120 : : * order may affect the results. So keep an ordered list of the typelibs,
121 : : * alongside the hash table which keep the canonical strong reference to them. */
122 : : GHashTable *typelibs; /* (string) namespace -> GITypelib */
123 : : GPtrArray *ordered_typelibs; /* (element-type unowned GITypelib) (owned) (not nullable) */
124 : : GHashTable *lazy_typelibs; /* (string) namespace-version -> GITypelib */
125 : : GPtrArray *ordered_lazy_typelibs; /* (element-type unowned GITypelib) (owned) (not nullable) */
126 : :
127 : : GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
128 : : GHashTable *info_by_error_domain; /* GQuark -> GIBaseInfo */
129 : : GHashTable *interfaces_for_gtype; /* GType -> GTypeInterfaceCache */
130 : : GHashTable *unknown_gtypes; /* hashset of GType */
131 : :
132 : : char **cached_shared_libraries; /* (owned) (nullable) (array zero-terminated=1) */
133 : : size_t cached_n_shared_libraries; /* length of @cached_shared_libraries, not including NULL terminator */
134 : : };
135 : :
136 : 12145 : G_DEFINE_TYPE (GIRepository, gi_repository, G_TYPE_OBJECT);
137 : :
138 : : static GITypelib *
139 : : require_internal (GIRepository *repository,
140 : : const char *namespace,
141 : : const char *version,
142 : : GIRepositoryLoadFlags flags,
143 : : const char * const *search_paths,
144 : : size_t n_search_paths,
145 : : GError **error);
146 : :
147 : : #ifdef G_PLATFORM_WIN32
148 : : #include <windows.h>
149 : :
150 : : static HMODULE girepository_dll = NULL;
151 : :
152 : : BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved);
153 : :
154 : : BOOL WINAPI
155 : 127 : DllMain (HINSTANCE hinstDLL,
156 : : DWORD fdwReason,
157 : : LPVOID lpvReserved)
158 : : {
159 : 127 : if (fdwReason == DLL_PROCESS_ATTACH)
160 : 42 : girepository_dll = hinstDLL;
161 : :
162 : 127 : return TRUE;
163 : : }
164 : :
165 : : #endif /* G_PLATFORM_WIN32 */
166 : :
167 : : #ifdef __APPLE__
168 : : #include <mach-o/dyld.h>
169 : :
170 : : /* This function returns the file path of the loaded libgirepository1.0.dylib.
171 : : * It iterates over all the loaded images to find the one with the
172 : : * gi_repository_init symbol and returns its file path.
173 : : */
174 : : static const char *
175 : : gi_repository_get_library_path_macos (void)
176 : : {
177 : : /*
178 : : * Relevant documentation:
179 : : * https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/0-Introduction/introduction.html
180 : : * https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dyld.3.html
181 : : * https://opensource.apple.com/source/xnu/xnu-2050.18.24/EXTERNAL_HEADERS/mach-o/loader.h
182 : : */
183 : : const void *ptr = gi_repository_init;
184 : : const struct mach_header *header;
185 : : intptr_t offset;
186 : : uint32_t i, count;
187 : :
188 : : /* Iterate over all the loaded images */
189 : : count = _dyld_image_count ();
190 : : for (i = 0; i < count; i++)
191 : : {
192 : : header = _dyld_get_image_header (i);
193 : : offset = _dyld_get_image_vmaddr_slide (i);
194 : :
195 : : /* Locate the first `load` command */
196 : : struct load_command *cmd = (struct load_command *) ((char *) header + sizeof (struct mach_header));
197 : : if (header->magic == MH_MAGIC_64)
198 : : cmd = (struct load_command *) ((char *) header + sizeof (struct mach_header_64));
199 : :
200 : : /* Find the first `segment` command iterating over all the `load` commands.
201 : : * Then, check if the gi_repository_init symbol is in this image by checking
202 : : * if the pointer is in the segment's memory address range.
203 : : */
204 : : uint32_t j = 0;
205 : : while (j < header->ncmds)
206 : : {
207 : : if (cmd->cmd == LC_SEGMENT)
208 : : {
209 : : struct segment_command *seg = (struct segment_command *) cmd;
210 : : if (((intptr_t) ptr >= ((intptr_t) seg->vmaddr + offset)) && ((intptr_t) ptr < ((intptr_t) seg->vmaddr + offset + (intptr_t) seg->vmsize)))
211 : : return _dyld_get_image_name (i);
212 : : }
213 : : if (cmd->cmd == LC_SEGMENT_64)
214 : : {
215 : : struct segment_command_64 *seg = (struct segment_command_64 *) cmd;
216 : : if (((intptr_t) ptr >= ((intptr_t) seg->vmaddr + offset)) && ((intptr_t) ptr < ((intptr_t) seg->vmaddr + offset + (intptr_t) seg->vmsize)))
217 : : return _dyld_get_image_name (i);
218 : : }
219 : : /* Jump to the next command */
220 : : j++;
221 : : cmd = (struct load_command *) ((char *) cmd + cmd->cmdsize);
222 : : }
223 : : }
224 : : return NULL;
225 : : }
226 : : #endif /* __APPLE__ */
227 : :
228 : : /*
229 : : * gi_repository_get_libdir:
230 : : *
231 : : * Returns the directory where the typelib files are installed.
232 : : *
233 : : * In platforms without relocation support, this functions returns the
234 : : * `GOBJECT_INTROSPECTION_LIBDIR` directory defined at build time .
235 : : *
236 : : * On Windows and macOS this function returns the directory
237 : : * relative to the installation directory detected at runtime.
238 : : *
239 : : * On macOS, if the library is installed in
240 : : * `/Applications/MyApp.app/Contents/Home/lib/libgirepository-1.0.dylib`, it returns
241 : : * `/Applications/MyApp.app/Contents/Home/lib/girepository-1.0`
242 : : *
243 : : * On Windows, if the application is installed in
244 : : * `C:/Program Files/MyApp/bin/MyApp.exe`, it returns
245 : : * `C:/Program Files/MyApp/lib/girepository-1.0`
246 : : */
247 : : static const gchar *
248 : 173 : gi_repository_get_libdir (void)
249 : : {
250 : : static gchar *static_libdir;
251 : :
252 : 173 : if (g_once_init_enter_pointer (&static_libdir))
253 : : {
254 : : gchar *libdir;
255 : : #if defined(G_PLATFORM_WIN32)
256 : 21 : const char *toplevel = g_win32_get_package_installation_directory_of_module (girepository_dll);
257 : 21 : libdir = g_build_filename (toplevel, GOBJECT_INTROSPECTION_RELATIVE_LIBDIR, NULL);
258 : 21 : g_ignore_leak (libdir);
259 : : #elif defined(__APPLE__)
260 : : const char *libpath = gi_repository_get_library_path_macos ();
261 : : if (libpath != NULL)
262 : : {
263 : : libdir = g_path_get_dirname (libpath);
264 : : g_ignore_leak (libdir);
265 : : } else {
266 : : libdir = GOBJECT_INTROSPECTION_LIBDIR;
267 : : }
268 : : #else /* !G_PLATFORM_WIN32 && !__APPLE__ */
269 : 21 : libdir = GOBJECT_INTROSPECTION_LIBDIR;
270 : : #endif
271 : 42 : g_once_init_leave_pointer (&static_libdir, libdir);
272 : 21 : }
273 : 173 : return static_libdir;
274 : : }
275 : :
276 : : static void
277 : 173 : gi_repository_init (GIRepository *repository)
278 : : {
279 : : /* typelib search path */
280 : : {
281 : : const char *libdir;
282 : : char *typelib_dir;
283 : : const char *type_lib_path_env;
284 : :
285 : : /* This variable is intended to take precedence over both:
286 : : * - the default search path;
287 : : * - all gi_repository_prepend_search_path() calls.
288 : : */
289 : 173 : type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
290 : :
291 : 173 : if (type_lib_path_env)
292 : : {
293 : : char **custom_dirs;
294 : :
295 : 171 : custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
296 : 171 : repository->typelib_search_path =
297 : 171 : g_ptr_array_new_take_null_terminated ((gpointer) g_steal_pointer (&custom_dirs), g_free);
298 : 85 : }
299 : : else
300 : : {
301 : 2 : repository->typelib_search_path = g_ptr_array_new_null_terminated (1, g_free, TRUE);
302 : : }
303 : :
304 : 173 : libdir = gi_repository_get_libdir ();
305 : :
306 : 173 : typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
307 : :
308 : 173 : g_ptr_array_add (repository->typelib_search_path, g_steal_pointer (&typelib_dir));
309 : : }
310 : :
311 : 173 : repository->library_paths = g_ptr_array_new_null_terminated (1, g_free, TRUE);
312 : :
313 : 86 : repository->typelibs
314 : 259 : = g_hash_table_new_full (g_str_hash, g_str_equal,
315 : : (GDestroyNotify) g_free,
316 : : (GDestroyNotify) gi_typelib_unref);
317 : 173 : repository->ordered_typelibs = g_ptr_array_new_with_free_func (NULL);
318 : 86 : repository->lazy_typelibs
319 : 259 : = g_hash_table_new_full (g_str_hash, g_str_equal,
320 : : (GDestroyNotify) g_free,
321 : : (GDestroyNotify) gi_typelib_unref);
322 : 173 : repository->ordered_lazy_typelibs = g_ptr_array_new_with_free_func (NULL);
323 : :
324 : 86 : repository->info_by_gtype
325 : 259 : = g_hash_table_new_full (g_direct_hash, g_direct_equal,
326 : : (GDestroyNotify) NULL,
327 : : (GDestroyNotify) gi_base_info_unref);
328 : 86 : repository->info_by_error_domain
329 : 259 : = g_hash_table_new_full (g_direct_hash, g_direct_equal,
330 : : (GDestroyNotify) NULL,
331 : : (GDestroyNotify) gi_base_info_unref);
332 : 86 : repository->interfaces_for_gtype
333 : 259 : = g_hash_table_new_full (g_direct_hash, g_direct_equal,
334 : : (GDestroyNotify) NULL,
335 : : (GDestroyNotify) gtype_interface_cache_free);
336 : 173 : repository->unknown_gtypes = g_hash_table_new (NULL, NULL);
337 : 173 : }
338 : :
339 : : static void
340 : 171 : gi_repository_finalize (GObject *object)
341 : : {
342 : 171 : GIRepository *repository = GI_REPOSITORY (object);
343 : :
344 : 171 : g_hash_table_destroy (repository->typelibs);
345 : 171 : g_ptr_array_unref (repository->ordered_typelibs);
346 : 171 : g_hash_table_destroy (repository->lazy_typelibs);
347 : 171 : g_ptr_array_unref (repository->ordered_lazy_typelibs);
348 : :
349 : 171 : g_hash_table_destroy (repository->info_by_gtype);
350 : 171 : g_hash_table_destroy (repository->info_by_error_domain);
351 : 171 : g_hash_table_destroy (repository->interfaces_for_gtype);
352 : 171 : g_hash_table_destroy (repository->unknown_gtypes);
353 : :
354 : 171 : g_clear_pointer (&repository->cached_shared_libraries, g_strfreev);
355 : :
356 : 171 : g_clear_pointer (&repository->library_paths, g_ptr_array_unref);
357 : 171 : g_clear_pointer (&repository->typelib_search_path, g_ptr_array_unref);
358 : :
359 : 171 : (* G_OBJECT_CLASS (gi_repository_parent_class)->finalize) (G_OBJECT (repository));
360 : 171 : }
361 : :
362 : : static void
363 : 44 : gi_repository_class_init (GIRepositoryClass *class)
364 : : {
365 : : GObjectClass *gobject_class;
366 : :
367 : 44 : gobject_class = G_OBJECT_CLASS (class);
368 : :
369 : 44 : gobject_class->finalize = gi_repository_finalize;
370 : 44 : }
371 : :
372 : : /**
373 : : * gi_repository_prepend_search_path:
374 : : * @repository: A #GIRepository
375 : : * @directory: (type filename): directory name to prepend to the typelib
376 : : * search path
377 : : *
378 : : * Prepends @directory to the typelib search path.
379 : : *
380 : : * See also: gi_repository_get_search_path().
381 : : *
382 : : * Since: 2.80
383 : : */
384 : : void
385 : 145 : gi_repository_prepend_search_path (GIRepository *repository,
386 : : const char *directory)
387 : : {
388 : 145 : g_return_if_fail (GI_IS_REPOSITORY (repository));
389 : :
390 : 145 : g_ptr_array_insert (repository->typelib_search_path, 0, g_strdup (directory));
391 : 72 : }
392 : :
393 : : /**
394 : : * gi_repository_get_search_path:
395 : : * @repository: A #GIRepository
396 : : * @n_paths_out: (optional) (out): The number of search paths returned.
397 : : *
398 : : * Returns the current search path [class@GIRepository.Repository] will use when
399 : : * loading typelib files.
400 : : *
401 : : * The list is internal to [class@GIRepository.Repository] and should not be
402 : : * freed, nor should its string elements.
403 : : *
404 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
405 : : * counted in @n_paths_out.
406 : : *
407 : : * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
408 : : * important first
409 : : * Since: 2.80
410 : : */
411 : : const char * const *
412 : 8 : gi_repository_get_search_path (GIRepository *repository,
413 : : size_t *n_paths_out)
414 : : {
415 : 8 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
416 : :
417 : 8 : if G_UNLIKELY (!repository->typelib_search_path ||
418 : 4 : !repository->typelib_search_path->pdata)
419 : : {
420 : : static const char * const empty_search_path[] = {NULL};
421 : :
422 : 0 : if (n_paths_out)
423 : 0 : *n_paths_out = 0;
424 : :
425 : 0 : return empty_search_path;
426 : : }
427 : :
428 : 8 : if (n_paths_out)
429 : 6 : *n_paths_out = repository->typelib_search_path->len;
430 : :
431 : 8 : return (const char * const *) repository->typelib_search_path->pdata;
432 : 4 : }
433 : :
434 : : /**
435 : : * gi_repository_prepend_library_path:
436 : : * @repository: A #GIRepository
437 : : * @directory: (type filename): a single directory to scan for shared libraries
438 : : *
439 : : * Prepends @directory to the search path that is used to
440 : : * search shared libraries referenced by imported namespaces.
441 : : *
442 : : * Multiple calls to this function all contribute to the final
443 : : * list of paths.
444 : : *
445 : : * The list of paths is unique to @repository. When a typelib is loaded by the
446 : : * repository, the list of paths from the @repository at that instant is used
447 : : * by the typelib for loading its modules.
448 : : *
449 : : * If the library is not found in the directories configured
450 : : * in this way, loading will fall back to the system library
451 : : * path (i.e. `LD_LIBRARY_PATH` and `DT_RPATH` in ELF systems).
452 : : * See the documentation of your dynamic linker for full details.
453 : : *
454 : : * Since: 2.80
455 : : */
456 : : void
457 : 4 : gi_repository_prepend_library_path (GIRepository *repository,
458 : : const char *directory)
459 : : {
460 : 4 : g_return_if_fail (GI_IS_REPOSITORY (repository));
461 : :
462 : 4 : g_ptr_array_insert (repository->library_paths, 0, g_strdup (directory));
463 : 2 : }
464 : :
465 : : /**
466 : : * gi_repository_get_library_path:
467 : : * @repository: A #GIRepository
468 : : * @n_paths_out: (optional) (out): The number of library paths returned.
469 : : *
470 : : * Returns the current search path [class@GIRepository.Repository] will use when
471 : : * loading shared libraries referenced by imported namespaces.
472 : : *
473 : : * The list is internal to [class@GIRepository.Repository] and should not be
474 : : * freed, nor should its string elements.
475 : : *
476 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
477 : : * counted in @n_paths_out.
478 : : *
479 : : * Returns: (element-type filename) (transfer none) (array length=n_paths_out): list of search paths, most
480 : : * important first
481 : : * Since: 2.80
482 : : */
483 : : const char * const *
484 : 6 : gi_repository_get_library_path (GIRepository *repository,
485 : : size_t *n_paths_out)
486 : : {
487 : 6 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
488 : :
489 : 6 : if G_UNLIKELY (!repository->library_paths || !repository->library_paths->pdata)
490 : : {
491 : : static const char * const empty_search_path[] = {NULL};
492 : :
493 : 0 : if (n_paths_out)
494 : 0 : *n_paths_out = 0;
495 : :
496 : 0 : return empty_search_path;
497 : : }
498 : :
499 : 6 : if (n_paths_out)
500 : 6 : *n_paths_out = repository->library_paths->len;
501 : :
502 : 6 : return (const char * const *) repository->library_paths->pdata;
503 : 3 : }
504 : :
505 : : static char *
506 : 585 : build_typelib_key (const char *name, const char *source)
507 : : {
508 : 585 : GString *str = g_string_new (name);
509 : 290 : g_string_append_c (str, '\0');
510 : 290 : g_string_append (str, source);
511 : 585 : return g_string_free (str, FALSE);
512 : : }
513 : :
514 : : /* Note: Returns %NULL (not an empty %NULL-terminated array) if there are no
515 : : * dependencies. */
516 : : static char **
517 : 625 : get_typelib_dependencies (GITypelib *typelib)
518 : : {
519 : : Header *header;
520 : : const char *dependencies_glob;
521 : :
522 : 625 : header = (Header *)typelib->data;
523 : :
524 : 625 : if (header->dependencies == 0)
525 : 181 : return NULL;
526 : :
527 : 444 : dependencies_glob = gi_typelib_get_string (typelib, header->dependencies);
528 : 444 : return g_strsplit (dependencies_glob, "|", 0);
529 : 310 : }
530 : :
531 : : static GITypelib *
532 : 4656 : check_version_conflict (GITypelib *typelib,
533 : : const char *namespace,
534 : : const char *expected_version,
535 : : char **version_conflict)
536 : : {
537 : : Header *header;
538 : : const char *loaded_version;
539 : :
540 : 4656 : if (expected_version == NULL)
541 : : {
542 : 4084 : if (version_conflict)
543 : 0 : *version_conflict = NULL;
544 : 4084 : return typelib;
545 : : }
546 : :
547 : 572 : header = (Header*)typelib->data;
548 : 572 : loaded_version = gi_typelib_get_string (typelib, header->nsversion);
549 : 572 : g_assert (loaded_version != NULL);
550 : :
551 : 572 : if (strcmp (expected_version, loaded_version) != 0)
552 : : {
553 : 0 : if (version_conflict)
554 : 0 : *version_conflict = (char*)loaded_version;
555 : 0 : return NULL;
556 : : }
557 : 572 : if (version_conflict)
558 : 572 : *version_conflict = NULL;
559 : 572 : return typelib;
560 : 2326 : }
561 : :
562 : : static GITypelib *
563 : 5243 : get_registered_status (GIRepository *repository,
564 : : const char *namespace,
565 : : const char *version,
566 : : gboolean allow_lazy,
567 : : gboolean *lazy_status,
568 : : char **version_conflict)
569 : : {
570 : : GITypelib *typelib;
571 : :
572 : 5243 : if (lazy_status)
573 : 1159 : *lazy_status = FALSE;
574 : 5243 : typelib = g_hash_table_lookup (repository->typelibs, namespace);
575 : 5243 : if (typelib)
576 : 4656 : return check_version_conflict (typelib, namespace, version, version_conflict);
577 : 587 : typelib = g_hash_table_lookup (repository->lazy_typelibs, namespace);
578 : 587 : if (!typelib)
579 : 587 : return NULL;
580 : 0 : if (lazy_status)
581 : 0 : *lazy_status = TRUE;
582 : 0 : if (!allow_lazy)
583 : 0 : return NULL;
584 : 0 : return check_version_conflict (typelib, namespace, version, version_conflict);
585 : 2617 : }
586 : :
587 : : static GITypelib *
588 : 4084 : get_registered (GIRepository *repository,
589 : : const char *namespace,
590 : : const char *version)
591 : : {
592 : 4084 : return get_registered_status (repository, namespace, version, TRUE, NULL, NULL);
593 : : }
594 : :
595 : : static gboolean
596 : 585 : load_dependencies_recurse (GIRepository *repository,
597 : : GITypelib *typelib,
598 : : GError **error)
599 : : {
600 : : char **dependencies;
601 : :
602 : 585 : dependencies = get_typelib_dependencies (typelib);
603 : :
604 : 585 : if (dependencies != NULL)
605 : : {
606 : : int i;
607 : :
608 : 426 : const char * const *search_path =
609 : 426 : (const char * const *) repository->typelib_search_path->pdata;
610 : 426 : gsize search_path_len = repository->typelib_search_path->len;
611 : :
612 : 1297 : for (i = 0; dependencies[i]; i++)
613 : : {
614 : 871 : char *dependency = dependencies[i];
615 : : const char *last_dash;
616 : : char *dependency_namespace;
617 : : const char *dependency_version;
618 : :
619 : 871 : last_dash = strrchr (dependency, '-');
620 : 871 : g_assert (last_dash != NULL); /* get_typelib_dependencies() guarantees this */
621 : 871 : dependency_namespace = g_strndup (dependency, (size_t) (last_dash - dependency));
622 : 871 : dependency_version = last_dash+1;
623 : :
624 : 1302 : if (!require_internal (repository, dependency_namespace, dependency_version,
625 : 431 : 0, search_path, search_path_len,
626 : 431 : error))
627 : : {
628 : 0 : g_free (dependency_namespace);
629 : 0 : g_strfreev (dependencies);
630 : 0 : return FALSE;
631 : : }
632 : 871 : g_free (dependency_namespace);
633 : 431 : }
634 : 426 : g_strfreev (dependencies);
635 : 211 : }
636 : 585 : return TRUE;
637 : 290 : }
638 : :
639 : : /* Sort typelibs by namespace. The main requirement here is just to make iteration
640 : : * deterministic, otherwise results can change as a lot of the code here would
641 : : * just iterate over a `GHashTable`.
642 : : *
643 : : * A sub-requirement of this is that namespaces are sorted such that if a GType
644 : : * or symbol is found in multiple namespaces where one is a prefix of the other,
645 : : * the longest namespace wins. In practice, this only happens in
646 : : * Gio/GioUnix/GioWin32, as all three of those namespaces refer to the same
647 : : * `.so` file and overlapping sets of the same symbols, but we want the platform
648 : : * specific namespace to be returned in preference to anything else (even though
649 : : * either namespace is valid).
650 : : * See https://gitlab.gnome.org/GNOME/glib/-/issues/3303 */
651 : : static int
652 : 1138 : sort_typelibs_cb (const void *a,
653 : : const void *b)
654 : : {
655 : 1138 : GITypelib *typelib_a = *(GITypelib **) a;
656 : 1138 : GITypelib *typelib_b = *(GITypelib **) b;
657 : :
658 : 1701 : return strcmp (gi_typelib_get_namespace (typelib_a),
659 : 563 : gi_typelib_get_namespace (typelib_b));
660 : : }
661 : :
662 : : static const char *
663 : 585 : register_internal (GIRepository *repository,
664 : : const char *source,
665 : : gboolean lazy,
666 : : GITypelib *typelib,
667 : : GError **error)
668 : : {
669 : : Header *header;
670 : : const char *namespace;
671 : :
672 : 585 : g_return_val_if_fail (typelib != NULL, NULL);
673 : :
674 : 585 : header = (Header *)typelib->data;
675 : :
676 : 585 : g_return_val_if_fail (header != NULL, NULL);
677 : :
678 : 585 : namespace = gi_typelib_get_string (typelib, header->namespace);
679 : :
680 : 585 : if (lazy)
681 : : {
682 : 0 : g_assert (!g_hash_table_lookup (repository->lazy_typelibs,
683 : 0 : namespace));
684 : 0 : g_hash_table_insert (repository->lazy_typelibs,
685 : 0 : build_typelib_key (namespace, source), gi_typelib_ref (typelib));
686 : 0 : g_ptr_array_add (repository->ordered_lazy_typelibs, typelib);
687 : 0 : g_ptr_array_sort (repository->ordered_lazy_typelibs, sort_typelibs_cb);
688 : 0 : }
689 : : else
690 : : {
691 : : gpointer value;
692 : : char *key;
693 : :
694 : : /* First, try loading all the dependencies */
695 : 585 : if (!load_dependencies_recurse (repository, typelib, error))
696 : 0 : return NULL;
697 : :
698 : : /* Check if we are transitioning from lazily loaded state */
699 : 875 : if (g_hash_table_lookup_extended (repository->lazy_typelibs,
700 : 290 : namespace,
701 : : (gpointer)&key, &value))
702 : : {
703 : 0 : g_hash_table_remove (repository->lazy_typelibs, key);
704 : 0 : g_ptr_array_remove (repository->ordered_lazy_typelibs, typelib);
705 : 0 : }
706 : : else
707 : : {
708 : 585 : key = build_typelib_key (namespace, source);
709 : : }
710 : :
711 : 875 : g_hash_table_insert (repository->typelibs,
712 : 290 : g_steal_pointer (&key),
713 : 585 : gi_typelib_ref (typelib));
714 : 585 : g_ptr_array_add (repository->ordered_typelibs, typelib);
715 : 585 : g_ptr_array_sort (repository->ordered_typelibs, sort_typelibs_cb);
716 : : }
717 : :
718 : : /* These types might be resolved now, clear the cache */
719 : 585 : g_hash_table_remove_all (repository->unknown_gtypes);
720 : :
721 : : /* Success */
722 : 585 : g_assert (namespace != NULL);
723 : :
724 : 585 : return namespace;
725 : 290 : }
726 : :
727 : : /**
728 : : * gi_repository_get_immediate_dependencies:
729 : : * @repository: A #GIRepository
730 : : * @namespace_: Namespace of interest
731 : : * @n_dependencies_out: (optional) (out): Return location for the number of
732 : : * dependencies
733 : : *
734 : : * Return an array of the immediate versioned dependencies for @namespace_.
735 : : * Returned strings are of the form `namespace-version`.
736 : : *
737 : : * Note: @namespace_ must have already been loaded using a function
738 : : * such as [method@GIRepository.Repository.require] before calling this
739 : : * function.
740 : : *
741 : : * To get the transitive closure of dependencies for @namespace_, use
742 : : * [method@GIRepository.Repository.get_dependencies].
743 : : *
744 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
745 : : * counted in @n_dependencies_out.
746 : : *
747 : : * Returns: (transfer full) (array length=n_dependencies_out): String array of
748 : : * immediate versioned dependencies
749 : : * Since: 2.80
750 : : */
751 : : char **
752 : 0 : gi_repository_get_immediate_dependencies (GIRepository *repository,
753 : : const char *namespace,
754 : : size_t *n_dependencies_out)
755 : : {
756 : : GITypelib *typelib;
757 : : char **deps;
758 : :
759 : 0 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
760 : 0 : g_return_val_if_fail (namespace != NULL, NULL);
761 : :
762 : 0 : typelib = get_registered (repository, namespace, NULL);
763 : 0 : g_return_val_if_fail (typelib != NULL, NULL);
764 : :
765 : : /* Ensure we always return a non-%NULL vector. */
766 : 0 : deps = get_typelib_dependencies (typelib);
767 : 0 : if (deps == NULL)
768 : 0 : deps = g_strsplit ("", "|", 0);
769 : :
770 : 0 : if (n_dependencies_out != NULL)
771 : 0 : *n_dependencies_out = g_strv_length (deps);
772 : :
773 : 0 : return deps;
774 : 0 : }
775 : :
776 : : /* Load the transitive closure of dependency namespace-version strings for the
777 : : * given @typelib. @repository must be non-%NULL. @transitive_dependencies must
778 : : * be a pre-existing GHashTable<owned utf8, owned utf8> set for storing the
779 : : * dependencies. */
780 : : static void
781 : 40 : get_typelib_dependencies_transitive (GIRepository *repository,
782 : : GITypelib *typelib,
783 : : GHashTable *transitive_dependencies)
784 : : {
785 : : char **immediate_dependencies;
786 : :
787 : 40 : immediate_dependencies = get_typelib_dependencies (typelib);
788 : :
789 : 66 : for (size_t i = 0; immediate_dependencies != NULL && immediate_dependencies[i]; i++)
790 : : {
791 : : char *dependency;
792 : : const char *last_dash;
793 : : char *dependency_namespace;
794 : :
795 : 26 : dependency = immediate_dependencies[i];
796 : :
797 : : /* Steal from the strv. */
798 : 26 : g_hash_table_add (transitive_dependencies, dependency);
799 : 26 : immediate_dependencies[i] = NULL;
800 : :
801 : : /* Recurse for this namespace. */
802 : 26 : last_dash = strrchr (dependency, '-');
803 : 26 : g_assert (last_dash != NULL); /* get_typelib_dependencies() guarantees this */
804 : 26 : dependency_namespace = g_strndup (dependency, (size_t) (last_dash - dependency));
805 : :
806 : 26 : typelib = get_registered (repository, dependency_namespace, NULL);
807 : 26 : g_return_if_fail (typelib != NULL);
808 : 39 : get_typelib_dependencies_transitive (repository, typelib,
809 : 13 : transitive_dependencies);
810 : :
811 : 26 : g_free (dependency_namespace);
812 : 13 : }
813 : :
814 : 40 : g_free (immediate_dependencies);
815 : 20 : }
816 : :
817 : : /**
818 : : * gi_repository_get_dependencies:
819 : : * @repository: A #GIRepository
820 : : * @namespace_: Namespace of interest
821 : : * @n_dependencies_out: (optional) (out): Return location for the number of
822 : : * dependencies
823 : : *
824 : : * Retrieves all (transitive) versioned dependencies for
825 : : * @namespace_.
826 : : *
827 : : * The returned strings are of the form `namespace-version`.
828 : : *
829 : : * Note: @namespace_ must have already been loaded using a function
830 : : * such as [method@GIRepository.Repository.require] before calling this
831 : : * function.
832 : : *
833 : : * To get only the immediate dependencies for @namespace_, use
834 : : * [method@GIRepository.Repository.get_immediate_dependencies].
835 : : *
836 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
837 : : * counted in @n_dependencies_out.
838 : : *
839 : : * Returns: (transfer full) (array length=n_dependencies_out): String array of
840 : : * all versioned dependencies
841 : : * Since: 2.80
842 : : */
843 : : char **
844 : 14 : gi_repository_get_dependencies (GIRepository *repository,
845 : : const char *namespace,
846 : : size_t *n_dependencies_out)
847 : : {
848 : : GITypelib *typelib;
849 : : GHashTable *transitive_dependencies; /* set of owned utf8 */
850 : : GHashTableIter iter;
851 : : char *dependency;
852 : : GPtrArray *out; /* owned utf8 elements */
853 : :
854 : 14 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
855 : 14 : g_return_val_if_fail (namespace != NULL, NULL);
856 : :
857 : 14 : typelib = get_registered (repository, namespace, NULL);
858 : 14 : g_return_val_if_fail (typelib != NULL, NULL);
859 : :
860 : : /* Load the dependencies. */
861 : 14 : transitive_dependencies = g_hash_table_new_full (g_str_hash, g_str_equal,
862 : : g_free, NULL);
863 : 21 : get_typelib_dependencies_transitive (repository, typelib,
864 : 7 : transitive_dependencies);
865 : :
866 : : /* Convert to a string array. */
867 : 14 : out = g_ptr_array_new_null_terminated (g_hash_table_size (transitive_dependencies),
868 : : g_free, TRUE);
869 : 14 : g_hash_table_iter_init (&iter, transitive_dependencies);
870 : :
871 : 32 : while (g_hash_table_iter_next (&iter, (gpointer) &dependency, NULL))
872 : : {
873 : 18 : g_ptr_array_add (out, dependency);
874 : 18 : g_hash_table_iter_steal (&iter);
875 : : }
876 : :
877 : 14 : g_hash_table_unref (transitive_dependencies);
878 : :
879 : 14 : if (n_dependencies_out != NULL)
880 : 2 : *n_dependencies_out = out->len;
881 : :
882 : 14 : return (char **) g_ptr_array_free (out, FALSE);
883 : 7 : }
884 : :
885 : : /**
886 : : * gi_repository_load_typelib:
887 : : * @repository: A #GIRepository
888 : : * @typelib: (transfer none): the typelib to load
889 : : * @flags: flags affecting the loading operation
890 : : * @error: return location for a [type@GLib.Error], or `NULL`
891 : : *
892 : : * Load the given @typelib into the repository.
893 : : *
894 : : * Returns: namespace of the loaded typelib
895 : : * Since: 2.80
896 : : */
897 : : const char *
898 : 0 : gi_repository_load_typelib (GIRepository *repository,
899 : : GITypelib *typelib,
900 : : GIRepositoryLoadFlags flags,
901 : : GError **error)
902 : : {
903 : : Header *header;
904 : : const char *namespace;
905 : : const char *nsversion;
906 : 0 : gboolean allow_lazy = flags & GI_REPOSITORY_LOAD_FLAG_LAZY;
907 : : gboolean is_lazy;
908 : : char *version_conflict;
909 : :
910 : 0 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
911 : :
912 : 0 : header = (Header *) typelib->data;
913 : 0 : namespace = gi_typelib_get_string (typelib, header->namespace);
914 : 0 : nsversion = gi_typelib_get_string (typelib, header->nsversion);
915 : :
916 : 0 : if (get_registered_status (repository, namespace, nsversion, allow_lazy,
917 : : &is_lazy, &version_conflict))
918 : : {
919 : 0 : if (version_conflict != NULL)
920 : : {
921 : 0 : g_set_error (error, GI_REPOSITORY_ERROR,
922 : : GI_REPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
923 : : "Attempting to load namespace '%s', version '%s', but '%s' is already loaded",
924 : 0 : namespace, nsversion, version_conflict);
925 : 0 : return NULL;
926 : : }
927 : 0 : return namespace;
928 : : }
929 : 0 : return register_internal (repository, "<builtin>",
930 : 0 : allow_lazy, typelib, error);
931 : 0 : }
932 : :
933 : : /**
934 : : * gi_repository_is_registered:
935 : : * @repository: A #GIRepository
936 : : * @namespace_: Namespace of interest
937 : : * @version: (nullable): Required version, may be `NULL` for latest
938 : : *
939 : : * Check whether a particular namespace (and optionally, a specific
940 : : * version thereof) is currently loaded.
941 : : *
942 : : * This function is likely to only be useful in unusual circumstances; in order
943 : : * to act upon metadata in the namespace, you should call
944 : : * [method@GIRepository.Repository.require] instead which will ensure the
945 : : * namespace is loaded, and return as quickly as this function will if it has
946 : : * already been loaded.
947 : : *
948 : : * Returns: `TRUE` if namespace-version is loaded, `FALSE` otherwise
949 : : * Since: 2.80
950 : : */
951 : : gboolean
952 : 2 : gi_repository_is_registered (GIRepository *repository,
953 : : const char *namespace,
954 : : const char *version)
955 : : {
956 : 2 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), FALSE);
957 : :
958 : 2 : return get_registered (repository, namespace, version) != NULL;
959 : 1 : }
960 : :
961 : : /**
962 : : * gi_repository_new:
963 : : *
964 : : * Create a new [class@GIRepository.Repository].
965 : : *
966 : : * Returns: (transfer full): a new [class@GIRepository.Repository]
967 : : * Since: 2.80
968 : : */
969 : : GIRepository *
970 : 173 : gi_repository_new (void)
971 : : {
972 : 173 : return g_object_new (GI_TYPE_REPOSITORY, NULL);
973 : : }
974 : :
975 : : /**
976 : : * gi_repository_get_n_infos:
977 : : * @repository: A #GIRepository
978 : : * @namespace_: Namespace to inspect
979 : : *
980 : : * This function returns the number of metadata entries in
981 : : * given namespace @namespace_.
982 : : *
983 : : * The namespace must have already been loaded before calling this function.
984 : : *
985 : : * Returns: number of metadata entries
986 : : * Since: 2.80
987 : : */
988 : : unsigned int
989 : 4 : gi_repository_get_n_infos (GIRepository *repository,
990 : : const char *namespace)
991 : : {
992 : : GITypelib *typelib;
993 : 4 : unsigned int n_interfaces = 0;
994 : :
995 : 4 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), 0);
996 : 4 : g_return_val_if_fail (namespace != NULL, 0);
997 : :
998 : 4 : typelib = get_registered (repository, namespace, NULL);
999 : :
1000 : 4 : g_return_val_if_fail (typelib != NULL, 0);
1001 : :
1002 : 4 : n_interfaces = ((Header *)typelib->data)->n_local_entries;
1003 : :
1004 : 4 : return n_interfaces;
1005 : 2 : }
1006 : :
1007 : : /**
1008 : : * gi_repository_get_info:
1009 : : * @repository: A #GIRepository
1010 : : * @namespace_: Namespace to inspect
1011 : : * @idx: 0-based offset into namespace metadata for entry
1012 : : *
1013 : : * This function returns a particular metadata entry in the
1014 : : * given namespace @namespace_.
1015 : : *
1016 : : * The namespace must have already been loaded before calling this function.
1017 : : * See [method@GIRepository.Repository.get_n_infos] to find the maximum number
1018 : : * of entries. It is an error to pass an invalid @idx to this function.
1019 : : *
1020 : : * Returns: (transfer full) (not nullable): [class@GIRepository.BaseInfo]
1021 : : * containing metadata
1022 : : * Since: 2.80
1023 : : */
1024 : : GIBaseInfo *
1025 : 3884 : gi_repository_get_info (GIRepository *repository,
1026 : : const char *namespace,
1027 : : unsigned int idx)
1028 : : {
1029 : : GITypelib *typelib;
1030 : : DirEntry *entry;
1031 : :
1032 : 3884 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1033 : 3884 : g_return_val_if_fail (namespace != NULL, NULL);
1034 : 3884 : g_return_val_if_fail (idx < G_MAXUINT16, NULL);
1035 : :
1036 : 3884 : typelib = get_registered (repository, namespace, NULL);
1037 : :
1038 : 3884 : g_return_val_if_fail (typelib != NULL, NULL);
1039 : :
1040 : 3884 : entry = gi_typelib_get_dir_entry (typelib, idx + 1);
1041 : 3884 : g_return_val_if_fail (entry != NULL, NULL);
1042 : :
1043 : 5828 : return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
1044 : 1944 : repository,
1045 : 1944 : NULL, typelib, entry->offset);
1046 : 1944 : }
1047 : :
1048 : : static DirEntry *
1049 : 18 : find_by_gtype (GPtrArray *ordered_table,
1050 : : const char *gtype_name,
1051 : : gboolean check_prefix,
1052 : : GITypelib **out_result_typelib)
1053 : : {
1054 : : /* Search in reverse order as the longest namespaces will be listed last, and
1055 : : * those are the ones we want to search first. */
1056 : 46 : for (guint i = ordered_table->len; i > 0; i--)
1057 : : {
1058 : 38 : GITypelib *typelib = g_ptr_array_index (ordered_table, i - 1);
1059 : : DirEntry *ret;
1060 : :
1061 : 38 : if (check_prefix)
1062 : : {
1063 : 28 : if (!gi_typelib_matches_gtype_name_prefix (typelib, gtype_name))
1064 : 0 : continue;
1065 : 14 : }
1066 : :
1067 : 38 : ret = gi_typelib_get_dir_entry_by_gtype_name (typelib, gtype_name);
1068 : 38 : if (ret)
1069 : : {
1070 : 10 : *out_result_typelib = typelib;
1071 : 10 : return ret;
1072 : : }
1073 : 14 : }
1074 : :
1075 : 8 : return NULL;
1076 : 9 : }
1077 : :
1078 : : /**
1079 : : * gi_repository_find_by_gtype:
1080 : : * @repository: A #GIRepository
1081 : : * @gtype: [type@GObject.Type] to search for
1082 : : *
1083 : : * Searches all loaded namespaces for a particular [type@GObject.Type].
1084 : : *
1085 : : * Note that in order to locate the metadata, the namespace corresponding to
1086 : : * the type must first have been loaded. There is currently no
1087 : : * mechanism for determining the namespace which corresponds to an
1088 : : * arbitrary [type@GObject.Type] — thus, this function will operate most
1089 : : * reliably when you know the [type@GObject.Type] is from a loaded namespace.
1090 : : *
1091 : : * Returns: (transfer full) (nullable): [class@GIRepository.BaseInfo]
1092 : : * representing metadata about @type, or `NULL` if none found
1093 : : * Since: 2.80
1094 : : */
1095 : : GIBaseInfo *
1096 : 16 : gi_repository_find_by_gtype (GIRepository *repository,
1097 : : GType gtype)
1098 : : {
1099 : : const char *gtype_name;
1100 : 16 : GITypelib *result_typelib = NULL;
1101 : : GIBaseInfo *cached;
1102 : : DirEntry *entry;
1103 : :
1104 : 16 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1105 : 16 : g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL);
1106 : :
1107 : 24 : cached = g_hash_table_lookup (repository->info_by_gtype,
1108 : 8 : (gpointer)gtype);
1109 : :
1110 : 16 : if (cached != NULL)
1111 : 2 : return gi_base_info_ref (cached);
1112 : :
1113 : 14 : if (g_hash_table_contains (repository->unknown_gtypes, (gpointer)gtype))
1114 : 2 : return NULL;
1115 : :
1116 : 12 : gtype_name = g_type_name (gtype);
1117 : :
1118 : : /* Inside each typelib, we include the "C prefix" which acts as
1119 : : * a namespace mechanism. For GtkTreeView, the C prefix is Gtk.
1120 : : * Given the assumption that GTypes for a library also use the
1121 : : * C prefix, we know we can skip examining a typelib if our
1122 : : * target type does not have this typelib's C prefix. Use this
1123 : : * assumption as our first attempt at locating the DirEntry.
1124 : : */
1125 : 12 : entry = find_by_gtype (repository->ordered_typelibs, gtype_name, TRUE, &result_typelib);
1126 : 12 : if (entry == NULL)
1127 : 2 : entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, TRUE, &result_typelib);
1128 : :
1129 : : /* Not every class library necessarily specifies a correct c_prefix,
1130 : : * so take a second pass. This time we will try a global lookup,
1131 : : * ignoring prefixes.
1132 : : * See http://bugzilla.gnome.org/show_bug.cgi?id=564016
1133 : : */
1134 : 12 : if (entry == NULL)
1135 : 2 : entry = find_by_gtype (repository->ordered_typelibs, gtype_name, FALSE, &result_typelib);
1136 : 12 : if (entry == NULL)
1137 : 2 : entry = find_by_gtype (repository->ordered_lazy_typelibs, gtype_name, FALSE, &result_typelib);
1138 : :
1139 : 12 : if (entry != NULL)
1140 : : {
1141 : 15 : cached = gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
1142 : 5 : repository,
1143 : 5 : NULL, result_typelib, entry->offset);
1144 : :
1145 : 15 : g_hash_table_insert (repository->info_by_gtype,
1146 : 5 : (gpointer) gtype,
1147 : 10 : gi_base_info_ref (cached));
1148 : 10 : return cached;
1149 : : }
1150 : : else
1151 : : {
1152 : 2 : g_hash_table_add (repository->unknown_gtypes, (gpointer) gtype);
1153 : 2 : return NULL;
1154 : : }
1155 : 8 : }
1156 : :
1157 : : /**
1158 : : * gi_repository_find_by_name:
1159 : : * @repository: A #GIRepository
1160 : : * @namespace_: Namespace which will be searched
1161 : : * @name: Entry name to find
1162 : : *
1163 : : * Searches for a particular entry in a namespace.
1164 : : *
1165 : : * Before calling this function for a particular namespace, you must call
1166 : : * [method@GIRepository.Repository.require] to load the namespace, or otherwise
1167 : : * ensure the namespace has already been loaded.
1168 : : *
1169 : : * Returns: (transfer full) (nullable): [class@GIRepository.BaseInfo]
1170 : : * representing metadata about @name, or `NULL` if none found
1171 : : * Since: 2.80
1172 : : */
1173 : : GIBaseInfo *
1174 : 138 : gi_repository_find_by_name (GIRepository *repository,
1175 : : const char *namespace,
1176 : : const char *name)
1177 : : {
1178 : : GITypelib *typelib;
1179 : : DirEntry *entry;
1180 : :
1181 : 138 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1182 : 138 : g_return_val_if_fail (namespace != NULL, NULL);
1183 : :
1184 : 138 : typelib = get_registered (repository, namespace, NULL);
1185 : 138 : g_return_val_if_fail (typelib != NULL, NULL);
1186 : :
1187 : 138 : entry = gi_typelib_get_dir_entry_by_name (typelib, name);
1188 : 138 : if (entry == NULL)
1189 : 3 : return NULL;
1190 : 202 : return gi_info_new_full (gi_typelib_blob_type_to_info_type (entry->blob_type),
1191 : 67 : repository,
1192 : 67 : NULL, typelib, entry->offset);
1193 : 68 : }
1194 : :
1195 : : static DirEntry *
1196 : 10 : find_by_error_domain (GPtrArray *ordered_typelibs,
1197 : : GQuark target_domain,
1198 : : GITypelib **out_typelib)
1199 : : {
1200 : : /* Search in reverse order as the longest namespaces will be listed last, and
1201 : : * those are the ones we want to search first. */
1202 : 32 : for (guint i = ordered_typelibs->len; i > 0; i--)
1203 : : {
1204 : 24 : GITypelib *typelib = g_ptr_array_index (ordered_typelibs, i - 1);
1205 : : DirEntry *entry;
1206 : :
1207 : 24 : entry = gi_typelib_get_dir_entry_by_error_domain (typelib, target_domain);
1208 : 24 : if (entry != NULL)
1209 : : {
1210 : 2 : *out_typelib = typelib;
1211 : 2 : return entry;
1212 : : }
1213 : 11 : }
1214 : :
1215 : 8 : return NULL;
1216 : 5 : }
1217 : :
1218 : : /**
1219 : : * gi_repository_find_by_error_domain:
1220 : : * @repository: A #GIRepository
1221 : : * @domain: a [type@GLib.Error] domain
1222 : : *
1223 : : * Searches for the enum type corresponding to the given [type@GLib.Error]
1224 : : * domain.
1225 : : *
1226 : : * Before calling this function for a particular namespace, you must call
1227 : : * [method@GIRepository.Repository.require] to load the namespace, or otherwise
1228 : : * ensure the namespace has already been loaded.
1229 : : *
1230 : : * Returns: (transfer full) (nullable): [class@GIRepository.EnumInfo]
1231 : : * representing metadata about @domain’s enum type, or `NULL` if none found
1232 : : * Since: 2.80
1233 : : */
1234 : : GIEnumInfo *
1235 : 8 : gi_repository_find_by_error_domain (GIRepository *repository,
1236 : : GQuark domain)
1237 : : {
1238 : : GIEnumInfo *cached;
1239 : 8 : DirEntry *result = NULL;
1240 : 8 : GITypelib *result_typelib = NULL;
1241 : :
1242 : 8 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1243 : :
1244 : 12 : cached = g_hash_table_lookup (repository->info_by_error_domain,
1245 : 8 : GUINT_TO_POINTER (domain));
1246 : :
1247 : 8 : if (cached != NULL)
1248 : 2 : return (GIEnumInfo *) gi_base_info_ref ((GIBaseInfo *)cached);
1249 : :
1250 : 6 : result = find_by_error_domain (repository->ordered_typelibs, domain, &result_typelib);
1251 : 6 : if (result == NULL)
1252 : 4 : result = find_by_error_domain (repository->ordered_lazy_typelibs, domain, &result_typelib);
1253 : :
1254 : 6 : if (result != NULL)
1255 : : {
1256 : 3 : cached = (GIEnumInfo *) gi_info_new_full (gi_typelib_blob_type_to_info_type (result->blob_type),
1257 : 1 : repository,
1258 : 1 : NULL, result_typelib, result->offset);
1259 : :
1260 : 3 : g_hash_table_insert (repository->info_by_error_domain,
1261 : 2 : GUINT_TO_POINTER (domain),
1262 : 2 : gi_base_info_ref ((GIBaseInfo *) cached));
1263 : 2 : return cached;
1264 : : }
1265 : 4 : return NULL;
1266 : 4 : }
1267 : :
1268 : : /**
1269 : : * gi_repository_get_object_gtype_interfaces:
1270 : : * @repository: a #GIRepository
1271 : : * @gtype: a [type@GObject.Type] whose fundamental type is `G_TYPE_OBJECT`
1272 : : * @n_interfaces_out: (out): Number of interfaces
1273 : : * @interfaces_out: (out) (transfer none) (array length=n_interfaces_out): Interfaces for @gtype
1274 : : *
1275 : : * Look up the implemented interfaces for @gtype.
1276 : : *
1277 : : * This function cannot fail per se; but for a totally ‘unknown’
1278 : : * [type@GObject.Type], it may return 0 implemented interfaces.
1279 : : *
1280 : : * The semantics of this function are designed for a dynamic binding,
1281 : : * where in certain cases (such as a function which returns an
1282 : : * interface which may have ‘hidden’ implementation classes), not all
1283 : : * data may be statically known, and will have to be determined from
1284 : : * the [type@GObject.Type] of the object. An example is
1285 : : * [func@Gio.File.new_for_path] returning a concrete class of
1286 : : * `GLocalFile`, which is a [type@GObject.Type] we see at runtime, but
1287 : : * not statically.
1288 : : *
1289 : : * Since: 2.80
1290 : : */
1291 : : void
1292 : 2 : gi_repository_get_object_gtype_interfaces (GIRepository *repository,
1293 : : GType gtype,
1294 : : size_t *n_interfaces_out,
1295 : : GIInterfaceInfo ***interfaces_out)
1296 : : {
1297 : : GTypeInterfaceCache *cache;
1298 : :
1299 : 2 : g_return_if_fail (GI_IS_REPOSITORY (repository));
1300 : 2 : g_return_if_fail (g_type_fundamental (gtype) == G_TYPE_OBJECT);
1301 : :
1302 : 3 : cache = g_hash_table_lookup (repository->interfaces_for_gtype,
1303 : 1 : (void *) gtype);
1304 : 2 : if (cache == NULL)
1305 : : {
1306 : : GType *interfaces;
1307 : : unsigned int i;
1308 : : unsigned int n_interfaces;
1309 : 2 : GList *interface_infos = NULL, *iter;
1310 : :
1311 : 2 : interfaces = g_type_interfaces (gtype, &n_interfaces);
1312 : 6 : for (i = 0; i < n_interfaces; i++)
1313 : : {
1314 : : GIBaseInfo *base_info;
1315 : :
1316 : 4 : base_info = gi_repository_find_by_gtype (repository, interfaces[i]);
1317 : 4 : if (base_info == NULL)
1318 : 0 : continue;
1319 : :
1320 : 4 : if (gi_base_info_get_info_type (base_info) != GI_INFO_TYPE_INTERFACE)
1321 : : {
1322 : : /* FIXME - could this really happen? */
1323 : 0 : gi_base_info_unref (base_info);
1324 : 0 : continue;
1325 : : }
1326 : :
1327 : 4 : if (!g_list_find (interface_infos, base_info))
1328 : 4 : interface_infos = g_list_prepend (interface_infos, base_info);
1329 : 2 : }
1330 : :
1331 : 2 : cache = g_malloc (sizeof (GTypeInterfaceCache)
1332 : 2 : + sizeof (GIBaseInfo*) * g_list_length (interface_infos));
1333 : 2 : cache->n_interfaces = g_list_length (interface_infos);
1334 : 6 : for (iter = interface_infos, i = 0; iter; iter = iter->next, i++)
1335 : 4 : cache->interfaces[i] = iter->data;
1336 : 2 : g_list_free (interface_infos);
1337 : :
1338 : 3 : g_hash_table_insert (repository->interfaces_for_gtype, (gpointer) gtype,
1339 : 1 : cache);
1340 : :
1341 : 2 : g_free (interfaces);
1342 : 1 : }
1343 : :
1344 : 2 : *n_interfaces_out = cache->n_interfaces;
1345 : 2 : *interfaces_out = (GIInterfaceInfo**)&cache->interfaces[0];
1346 : 1 : }
1347 : :
1348 : : static void
1349 : 12 : collect_namespaces (GPtrArray *ordered_typelibs,
1350 : : char **names,
1351 : : size_t *inout_i)
1352 : : {
1353 : 36 : for (guint j = 0; j < ordered_typelibs->len; j++)
1354 : : {
1355 : 24 : GITypelib *typelib = g_ptr_array_index (ordered_typelibs, j);
1356 : 24 : const char *namespace = gi_typelib_get_namespace (typelib);
1357 : 36 : names[(*inout_i)++] = g_strdup (namespace);
1358 : 12 : }
1359 : 12 : }
1360 : :
1361 : : /**
1362 : : * gi_repository_get_loaded_namespaces:
1363 : : * @repository: A #GIRepository
1364 : : * @n_namespaces_out: (optional) (out): Return location for the number of
1365 : : * namespaces
1366 : : *
1367 : : * Return the list of currently loaded namespaces.
1368 : : *
1369 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
1370 : : * counted in @n_namespaces_out.
1371 : : *
1372 : : * Returns: (element-type utf8) (transfer full) (array length=n_namespaces_out):
1373 : : * list of namespaces
1374 : : * Since: 2.80
1375 : : */
1376 : : char **
1377 : 6 : gi_repository_get_loaded_namespaces (GIRepository *repository,
1378 : : size_t *n_namespaces_out)
1379 : : {
1380 : : char **names;
1381 : : size_t i;
1382 : : size_t n_typelibs;
1383 : :
1384 : 6 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1385 : :
1386 : 6 : n_typelibs = repository->ordered_typelibs->len + repository->ordered_lazy_typelibs->len;
1387 : 6 : names = g_malloc0 (sizeof (char *) * (n_typelibs + 1));
1388 : 6 : i = 0;
1389 : :
1390 : 6 : collect_namespaces (repository->ordered_typelibs, names, &i);
1391 : 6 : collect_namespaces (repository->ordered_lazy_typelibs, names, &i);
1392 : :
1393 : 6 : if (n_namespaces_out != NULL)
1394 : 4 : *n_namespaces_out = i;
1395 : :
1396 : 6 : return names;
1397 : 3 : }
1398 : :
1399 : : /**
1400 : : * gi_repository_get_version:
1401 : : * @repository: A #GIRepository
1402 : : * @namespace_: Namespace to inspect
1403 : : *
1404 : : * This function returns the loaded version associated with the given
1405 : : * namespace @namespace_.
1406 : : *
1407 : : * Note: The namespace must have already been loaded using a function
1408 : : * such as [method@GIRepository.Repository.require] before calling this
1409 : : * function.
1410 : : *
1411 : : * Returns: Loaded version
1412 : : * Since: 2.80
1413 : : */
1414 : : const char *
1415 : 2 : gi_repository_get_version (GIRepository *repository,
1416 : : const char *namespace)
1417 : : {
1418 : : GITypelib *typelib;
1419 : : Header *header;
1420 : :
1421 : 2 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1422 : 2 : g_return_val_if_fail (namespace != NULL, NULL);
1423 : :
1424 : 2 : typelib = get_registered (repository, namespace, NULL);
1425 : :
1426 : 2 : g_return_val_if_fail (typelib != NULL, NULL);
1427 : :
1428 : 2 : header = (Header *) typelib->data;
1429 : 2 : return gi_typelib_get_string (typelib, header->nsversion);
1430 : 1 : }
1431 : :
1432 : : /**
1433 : : * gi_repository_get_shared_libraries:
1434 : : * @repository: A #GIRepository
1435 : : * @namespace_: Namespace to inspect
1436 : : * @out_n_elements: (out) (optional): Return location for the number of elements
1437 : : * in the returned array
1438 : : *
1439 : : * This function returns an array of paths to the
1440 : : * shared C libraries associated with the given namespace @namespace_.
1441 : : *
1442 : : * There may be no shared library path associated, in which case this
1443 : : * function will return `NULL`.
1444 : : *
1445 : : * Note: The namespace must have already been loaded using a function
1446 : : * such as [method@GIRepository.Repository.require] before calling this
1447 : : * function.
1448 : : *
1449 : : * The list is internal to [class@GIRepository.Repository] and should not be
1450 : : * freed, nor should its string elements.
1451 : : *
1452 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
1453 : : * counted in @out_n_elements.
1454 : : *
1455 : : * Returns: (nullable) (array length=out_n_elements) (transfer none): Array of
1456 : : * paths to shared libraries, or `NULL` if none are associated
1457 : : * Since: 2.80
1458 : : */
1459 : : const char * const *
1460 : 12 : gi_repository_get_shared_libraries (GIRepository *repository,
1461 : : const char *namespace,
1462 : : size_t *out_n_elements)
1463 : : {
1464 : : GITypelib *typelib;
1465 : : Header *header;
1466 : :
1467 : 12 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1468 : 12 : g_return_val_if_fail (namespace != NULL, NULL);
1469 : :
1470 : 12 : typelib = get_registered (repository, namespace, NULL);
1471 : :
1472 : 12 : g_return_val_if_fail (typelib != NULL, NULL);
1473 : :
1474 : 12 : header = (Header *) typelib->data;
1475 : 12 : if (!header->shared_library)
1476 : : {
1477 : 0 : if (out_n_elements != NULL)
1478 : 0 : *out_n_elements = 0;
1479 : 0 : return NULL;
1480 : : }
1481 : :
1482 : : /* Populate the cache. */
1483 : 12 : if (repository->cached_shared_libraries == NULL)
1484 : : {
1485 : 12 : const char *comma_separated = gi_typelib_get_string (typelib, header->shared_library);
1486 : :
1487 : 12 : if (comma_separated != NULL && *comma_separated != '\0')
1488 : : {
1489 : 12 : repository->cached_shared_libraries = g_strsplit (comma_separated, ",", -1);
1490 : 12 : repository->cached_n_shared_libraries = g_strv_length (repository->cached_shared_libraries);
1491 : 6 : }
1492 : 6 : }
1493 : :
1494 : 12 : if (out_n_elements != NULL)
1495 : 0 : *out_n_elements = repository->cached_n_shared_libraries;
1496 : :
1497 : 12 : return (const char * const *) repository->cached_shared_libraries;
1498 : 6 : }
1499 : :
1500 : : /**
1501 : : * gi_repository_get_c_prefix:
1502 : : * @repository: A #GIRepository
1503 : : * @namespace_: Namespace to inspect
1504 : : *
1505 : : * This function returns the ‘C prefix’, or the C level namespace
1506 : : * associated with the given introspection namespace.
1507 : : *
1508 : : * Each C symbol starts with this prefix, as well each [type@GObject.Type] in
1509 : : * the library.
1510 : : *
1511 : : * Note: The namespace must have already been loaded using a function
1512 : : * such as [method@GIRepository.Repository.require] before calling this
1513 : : * function.
1514 : : *
1515 : : * Returns: (nullable): C namespace prefix, or `NULL` if none associated
1516 : : * Since: 2.80
1517 : : */
1518 : : const char *
1519 : 2 : gi_repository_get_c_prefix (GIRepository *repository,
1520 : : const char *namespace_)
1521 : : {
1522 : : GITypelib *typelib;
1523 : : Header *header;
1524 : :
1525 : 2 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1526 : 2 : g_return_val_if_fail (namespace_ != NULL, NULL);
1527 : :
1528 : 2 : typelib = get_registered (repository, namespace_, NULL);
1529 : :
1530 : 2 : g_return_val_if_fail (typelib != NULL, NULL);
1531 : :
1532 : 2 : header = (Header *) typelib->data;
1533 : 2 : if (header->c_prefix)
1534 : 2 : return gi_typelib_get_string (typelib, header->c_prefix);
1535 : : else
1536 : 0 : return NULL;
1537 : 1 : }
1538 : :
1539 : : /**
1540 : : * gi_repository_get_typelib_path:
1541 : : * @repository: A #GIRepository
1542 : : * @namespace_: GI namespace to use, e.g. `Gtk`
1543 : : *
1544 : : * If namespace @namespace_ is loaded, return the full path to the
1545 : : * .typelib file it was loaded from.
1546 : : *
1547 : : * If the typelib for namespace @namespace_ was included in a shared library,
1548 : : * return the special string `<builtin>`.
1549 : : *
1550 : : * Returns: (type filename) (nullable): Filesystem path (or `<builtin>`) if
1551 : : * successful, `NULL` if namespace is not loaded
1552 : : * Since: 2.80
1553 : : */
1554 : : const char *
1555 : 0 : gi_repository_get_typelib_path (GIRepository *repository,
1556 : : const char *namespace)
1557 : : {
1558 : : gpointer orig_key, value;
1559 : :
1560 : 0 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1561 : :
1562 : 0 : if (!g_hash_table_lookup_extended (repository->typelibs, namespace,
1563 : : &orig_key, &value))
1564 : : {
1565 : 0 : if (!g_hash_table_lookup_extended (repository->lazy_typelibs, namespace,
1566 : : &orig_key, &value))
1567 : :
1568 : 0 : return NULL;
1569 : 0 : }
1570 : 0 : return ((char*)orig_key) + strlen ((char *) orig_key) + 1;
1571 : 0 : }
1572 : :
1573 : : /* This simple search function looks for a specified namespace-version;
1574 : : it's faster than the full directory listing required for latest version. */
1575 : : static GMappedFile *
1576 : 585 : find_namespace_version (const char *namespace,
1577 : : const char *version,
1578 : : const char * const *search_paths,
1579 : : size_t n_search_paths,
1580 : : char **path_ret)
1581 : : {
1582 : 585 : GError *error = NULL;
1583 : 585 : GMappedFile *mfile = NULL;
1584 : : char *fname;
1585 : :
1586 : 585 : if (g_str_equal (namespace, GIREPOSITORY_TYPELIB_NAME) &&
1587 : 0 : !g_str_equal (version, GIREPOSITORY_TYPELIB_VERSION))
1588 : : {
1589 : 0 : g_debug ("Ignoring %s-%s.typelib because this libgirepository "
1590 : : "corresponds to %s-%s",
1591 : 0 : namespace, version,
1592 : 0 : namespace, GIREPOSITORY_TYPELIB_VERSION);
1593 : 0 : return NULL;
1594 : : }
1595 : :
1596 : 585 : fname = g_strdup_printf ("%s-%s.typelib", namespace, version);
1597 : :
1598 : 585 : for (size_t i = 0; i < n_search_paths; ++i)
1599 : : {
1600 : 585 : char *path = g_build_filename (search_paths[i], fname, NULL);
1601 : :
1602 : 585 : mfile = g_mapped_file_new (path, FALSE, &error);
1603 : 585 : if (error)
1604 : : {
1605 : 0 : g_free (path);
1606 : 0 : g_clear_error (&error);
1607 : 0 : continue;
1608 : : }
1609 : 585 : *path_ret = path;
1610 : 585 : break;
1611 : : }
1612 : 585 : g_free (fname);
1613 : 585 : return mfile;
1614 : 290 : }
1615 : :
1616 : : static gboolean
1617 : 2 : parse_version (const char *version,
1618 : : int *major,
1619 : : int *minor)
1620 : : {
1621 : : const char *dot;
1622 : : char *end;
1623 : :
1624 : 2 : *major = strtol (version, &end, 10);
1625 : 2 : dot = strchr (version, '.');
1626 : 2 : if (dot == NULL)
1627 : : {
1628 : 0 : *minor = 0;
1629 : 0 : return TRUE;
1630 : : }
1631 : 2 : if (dot != end)
1632 : 0 : return FALSE;
1633 : 2 : *minor = strtol (dot+1, &end, 10);
1634 : 2 : if (end != (version + strlen (version)))
1635 : 0 : return FALSE;
1636 : 2 : return TRUE;
1637 : 1 : }
1638 : :
1639 : : static int
1640 : 0 : compare_version (const char *v1,
1641 : : const char *v2)
1642 : : {
1643 : : gboolean success;
1644 : : int v1_major, v1_minor;
1645 : : int v2_major, v2_minor;
1646 : :
1647 : 0 : success = parse_version (v1, &v1_major, &v1_minor);
1648 : 0 : g_assert (success);
1649 : :
1650 : 0 : success = parse_version (v2, &v2_major, &v2_minor);
1651 : 0 : g_assert (success);
1652 : :
1653 : : /* Avoid a compiler warning about `success` being unused with G_DISABLE_ASSERT */
1654 : 0 : (void) success;
1655 : :
1656 : 0 : if (v1_major > v2_major)
1657 : 0 : return 1;
1658 : 0 : else if (v2_major > v1_major)
1659 : 0 : return -1;
1660 : 0 : else if (v1_minor > v2_minor)
1661 : 0 : return 1;
1662 : 0 : else if (v2_minor > v1_minor)
1663 : 0 : return -1;
1664 : 0 : return 0;
1665 : 0 : }
1666 : :
1667 : : struct NamespaceVersionCandidadate
1668 : : {
1669 : : GMappedFile *mfile;
1670 : : int path_index;
1671 : : char *path;
1672 : : char *version;
1673 : : };
1674 : :
1675 : : static int
1676 : 0 : compare_candidate_reverse (struct NamespaceVersionCandidadate *c1,
1677 : : struct NamespaceVersionCandidadate *c2)
1678 : : {
1679 : 0 : int result = compare_version (c1->version, c2->version);
1680 : : /* First, check the version */
1681 : 0 : if (result > 0)
1682 : 0 : return -1;
1683 : 0 : else if (result < 0)
1684 : 0 : return 1;
1685 : : else
1686 : : {
1687 : : /* Now check the path index, which says how early in the search path
1688 : : * we found it. This ensures that of equal version targets, we
1689 : : * pick the earlier one.
1690 : : */
1691 : 0 : if (c1->path_index == c2->path_index)
1692 : 0 : return 0;
1693 : 0 : else if (c1->path_index > c2->path_index)
1694 : 0 : return 1;
1695 : : else
1696 : 0 : return -1;
1697 : : }
1698 : 0 : }
1699 : :
1700 : : static void
1701 : 2 : free_candidate (struct NamespaceVersionCandidadate *candidate)
1702 : : {
1703 : 2 : g_mapped_file_unref (candidate->mfile);
1704 : 2 : g_free (candidate->path);
1705 : 2 : g_free (candidate->version);
1706 : 2 : g_slice_free (struct NamespaceVersionCandidadate, candidate);
1707 : 2 : }
1708 : :
1709 : : static GSList *
1710 : 6 : enumerate_namespace_versions (const char *namespace,
1711 : : const char * const *search_paths,
1712 : : size_t n_search_paths)
1713 : : {
1714 : 6 : GSList *candidates = NULL;
1715 : 6 : GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal);
1716 : : char *namespace_dash;
1717 : : char *namespace_typelib;
1718 : 6 : GError *error = NULL;
1719 : : int index;
1720 : :
1721 : 6 : namespace_dash = g_strdup_printf ("%s-", namespace);
1722 : 6 : namespace_typelib = g_strdup_printf ("%s.typelib", namespace);
1723 : :
1724 : 6 : index = 0;
1725 : 20 : for (size_t i = 0; i < n_search_paths; ++i)
1726 : : {
1727 : : GDir *dir;
1728 : : const char *dirname;
1729 : : const char *entry;
1730 : :
1731 : 16 : dirname = search_paths[i];
1732 : 16 : dir = g_dir_open (dirname, 0, NULL);
1733 : 16 : if (dir == NULL)
1734 : 10 : continue;
1735 : 62 : while ((entry = g_dir_read_name (dir)) != NULL)
1736 : : {
1737 : : GMappedFile *mfile;
1738 : : char *path, *version;
1739 : : struct NamespaceVersionCandidadate *candidate;
1740 : :
1741 : 86 : if (!g_str_has_suffix (entry, ".typelib"))
1742 : 28 : continue;
1743 : :
1744 : 30 : if (g_str_has_prefix (entry, namespace_dash))
1745 : : {
1746 : : const char *last_dash;
1747 : : const char *name_end;
1748 : : int major, minor;
1749 : :
1750 : 26 : if (g_str_equal (namespace, GIREPOSITORY_TYPELIB_NAME) &&
1751 : 0 : !g_str_equal (entry, GIREPOSITORY_TYPELIB_FILENAME))
1752 : : {
1753 : 0 : g_debug ("Ignoring %s because this libgirepository "
1754 : : "corresponds to %s",
1755 : 0 : entry, GIREPOSITORY_TYPELIB_FILENAME);
1756 : 0 : continue;
1757 : : }
1758 : :
1759 : 2 : name_end = strrchr (entry, '.');
1760 : 2 : last_dash = strrchr (entry, '-');
1761 : :
1762 : : /* These are guaranteed by the suffix and prefix checks above: */
1763 : 2 : g_assert (name_end != NULL);
1764 : 2 : g_assert (last_dash != NULL);
1765 : :
1766 : 2 : version = g_strndup (last_dash + 1, (size_t) (name_end - (last_dash + 1u)));
1767 : 2 : if (!parse_version (version, &major, &minor))
1768 : : {
1769 : 0 : g_free (version);
1770 : 0 : continue;
1771 : : }
1772 : 1 : }
1773 : : else
1774 : 26 : continue;
1775 : :
1776 : 2 : if (g_hash_table_lookup (found_versions, version) != NULL)
1777 : : {
1778 : 0 : g_free (version);
1779 : 0 : continue;
1780 : : }
1781 : :
1782 : 2 : path = g_build_filename (dirname, entry, NULL);
1783 : 2 : mfile = g_mapped_file_new (path, FALSE, &error);
1784 : 2 : if (mfile == NULL)
1785 : : {
1786 : 0 : g_free (path);
1787 : 0 : g_free (version);
1788 : 0 : g_clear_error (&error);
1789 : 0 : continue;
1790 : : }
1791 : 2 : candidate = g_slice_new0 (struct NamespaceVersionCandidadate);
1792 : 2 : candidate->mfile = mfile;
1793 : 2 : candidate->path_index = index;
1794 : 2 : candidate->path = path;
1795 : 2 : candidate->version = version;
1796 : 2 : candidates = g_slist_prepend (candidates, candidate);
1797 : 2 : g_hash_table_add (found_versions, version);
1798 : : }
1799 : 4 : g_dir_close (dir);
1800 : 4 : index++;
1801 : 2 : }
1802 : :
1803 : 4 : g_free (namespace_dash);
1804 : 4 : g_free (namespace_typelib);
1805 : 4 : g_hash_table_destroy (found_versions);
1806 : :
1807 : 4 : return candidates;
1808 : : }
1809 : :
1810 : : static GMappedFile *
1811 : 2 : find_namespace_latest (const char *namespace,
1812 : : const char * const *search_paths,
1813 : : size_t n_search_paths,
1814 : : char **version_ret,
1815 : : char **path_ret)
1816 : : {
1817 : : GSList *candidates;
1818 : 2 : GMappedFile *result = NULL;
1819 : :
1820 : 2 : *version_ret = NULL;
1821 : 2 : *path_ret = NULL;
1822 : :
1823 : 2 : candidates = enumerate_namespace_versions (namespace, search_paths, n_search_paths);
1824 : :
1825 : 2 : if (candidates != NULL)
1826 : : {
1827 : : struct NamespaceVersionCandidadate *elected;
1828 : 0 : candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse);
1829 : :
1830 : 0 : elected = (struct NamespaceVersionCandidadate *) candidates->data;
1831 : : /* Remove the elected one so we don't try to free its contents */
1832 : 0 : candidates = g_slist_delete_link (candidates, candidates);
1833 : :
1834 : 0 : result = elected->mfile;
1835 : 0 : *path_ret = elected->path;
1836 : 0 : *version_ret = elected->version;
1837 : 0 : g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */
1838 : 0 : g_slist_foreach (candidates, (GFunc) (void *) free_candidate, NULL);
1839 : 0 : g_slist_free (candidates);
1840 : 0 : }
1841 : 2 : return result;
1842 : : }
1843 : :
1844 : : /**
1845 : : * gi_repository_enumerate_versions:
1846 : : * @repository: A #GIRepository
1847 : : * @namespace_: GI namespace, e.g. `Gtk`
1848 : : * @n_versions_out: (optional) (out): The number of versions returned.
1849 : : *
1850 : : * Obtain an unordered list of versions (either currently loaded or
1851 : : * available) for @namespace_ in this @repository.
1852 : : *
1853 : : * The list is guaranteed to be `NULL` terminated. The `NULL` terminator is not
1854 : : * counted in @n_versions_out.
1855 : : *
1856 : : * Returns: (element-type utf8) (transfer full) (array length=n_versions_out): the array of versions.
1857 : : * Since: 2.80
1858 : : */
1859 : : char **
1860 : 4 : gi_repository_enumerate_versions (GIRepository *repository,
1861 : : const char *namespace_,
1862 : : size_t *n_versions_out)
1863 : : {
1864 : : GPtrArray *versions;
1865 : : GSList *candidates, *link;
1866 : : const char *loaded_version;
1867 : : char **ret;
1868 : :
1869 : 4 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1870 : :
1871 : 6 : candidates = enumerate_namespace_versions (namespace_,
1872 : 4 : (const char * const *) repository->typelib_search_path->pdata,
1873 : 4 : repository->typelib_search_path->len);
1874 : :
1875 : 4 : if (!candidates)
1876 : : {
1877 : 2 : if (n_versions_out)
1878 : 2 : *n_versions_out = 0;
1879 : 2 : return g_strdupv ((char *[]) {NULL});
1880 : : }
1881 : :
1882 : 2 : versions = g_ptr_array_new_null_terminated (1, g_free, TRUE);
1883 : 4 : for (link = candidates; link; link = link->next)
1884 : : {
1885 : 2 : struct NamespaceVersionCandidadate *candidate = link->data;
1886 : 2 : g_ptr_array_add (versions, g_steal_pointer (&candidate->version));
1887 : 2 : free_candidate (candidate);
1888 : 1 : }
1889 : 2 : g_slist_free (candidates);
1890 : :
1891 : : /* The currently loaded version of a namespace is also part of the
1892 : : * available versions, as it could have been loaded using
1893 : : * require_private().
1894 : : */
1895 : 2 : if (gi_repository_is_registered (repository, namespace_, NULL))
1896 : : {
1897 : 2 : loaded_version = gi_repository_get_version (repository, namespace_);
1898 : 3 : if (loaded_version &&
1899 : 2 : !g_ptr_array_find_with_equal_func (versions, loaded_version, g_str_equal, NULL))
1900 : 0 : g_ptr_array_add (versions, g_strdup (loaded_version));
1901 : 1 : }
1902 : :
1903 : 2 : ret = (char **) g_ptr_array_steal (versions, n_versions_out);
1904 : 2 : g_ptr_array_unref (g_steal_pointer (&versions));
1905 : :
1906 : 2 : return ret;
1907 : 2 : }
1908 : :
1909 : : static GITypelib *
1910 : 1159 : require_internal (GIRepository *repository,
1911 : : const char *namespace,
1912 : : const char *version,
1913 : : GIRepositoryLoadFlags flags,
1914 : : const char * const *search_paths,
1915 : : size_t n_search_paths,
1916 : : GError **error)
1917 : : {
1918 : : GMappedFile *mfile;
1919 : 1159 : GITypelib *ret = NULL;
1920 : : Header *header;
1921 : 1159 : GITypelib *typelib = NULL;
1922 : 1159 : GITypelib *typelib_owned = NULL;
1923 : : const char *typelib_namespace, *typelib_version;
1924 : 1159 : gboolean allow_lazy = (flags & GI_REPOSITORY_LOAD_FLAG_LAZY) > 0;
1925 : : gboolean is_lazy;
1926 : 1159 : char *version_conflict = NULL;
1927 : 1159 : char *path = NULL;
1928 : 1159 : char *tmp_version = NULL;
1929 : :
1930 : 1159 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
1931 : 1159 : g_return_val_if_fail (namespace != NULL, NULL);
1932 : :
1933 : 1159 : typelib = get_registered_status (repository, namespace, version, allow_lazy,
1934 : : &is_lazy, &version_conflict);
1935 : 1159 : if (typelib)
1936 : 572 : return typelib;
1937 : :
1938 : 587 : if (version_conflict != NULL)
1939 : : {
1940 : 0 : g_set_error (error, GI_REPOSITORY_ERROR,
1941 : : GI_REPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
1942 : : "Requiring namespace '%s' version '%s', but '%s' is already loaded",
1943 : 0 : namespace, version, version_conflict);
1944 : 0 : return NULL;
1945 : : }
1946 : :
1947 : 587 : if (version != NULL)
1948 : : {
1949 : 875 : mfile = find_namespace_version (namespace, version, search_paths,
1950 : 290 : n_search_paths, &path);
1951 : 585 : tmp_version = g_strdup (version);
1952 : 290 : }
1953 : : else
1954 : : {
1955 : 2 : mfile = find_namespace_latest (namespace, search_paths, n_search_paths,
1956 : : &tmp_version, &path);
1957 : : }
1958 : :
1959 : 587 : if (mfile == NULL)
1960 : : {
1961 : 2 : if (version != NULL)
1962 : 0 : g_set_error (error, GI_REPOSITORY_ERROR,
1963 : : GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1964 : : "Typelib file for namespace '%s', version '%s' not found",
1965 : 0 : namespace, version);
1966 : : else
1967 : 3 : g_set_error (error, GI_REPOSITORY_ERROR,
1968 : : GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1969 : : "Typelib file for namespace '%s' (any version) not found",
1970 : 1 : namespace);
1971 : 2 : goto out;
1972 : : }
1973 : :
1974 : : {
1975 : 585 : GError *temp_error = NULL;
1976 : 585 : GBytes *bytes = NULL;
1977 : :
1978 : 585 : bytes = g_mapped_file_get_bytes (mfile);
1979 : 585 : typelib_owned = typelib = gi_typelib_new_from_bytes (bytes, &temp_error);
1980 : 585 : g_bytes_unref (bytes);
1981 : 585 : g_clear_pointer (&mfile, g_mapped_file_unref);
1982 : :
1983 : 585 : if (!typelib)
1984 : : {
1985 : 0 : g_set_error (error, GI_REPOSITORY_ERROR,
1986 : : GI_REPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1987 : : "Failed to load typelib file '%s' for namespace '%s': %s",
1988 : 0 : path, namespace, temp_error->message);
1989 : 0 : g_clear_error (&temp_error);
1990 : 0 : goto out;
1991 : : }
1992 : :
1993 : 585 : typelib->library_paths = (repository->library_paths != NULL) ? g_ptr_array_ref (repository->library_paths) : NULL;
1994 : : }
1995 : 585 : header = (Header *) typelib->data;
1996 : 585 : typelib_namespace = gi_typelib_get_string (typelib, header->namespace);
1997 : 585 : typelib_version = gi_typelib_get_string (typelib, header->nsversion);
1998 : :
1999 : 585 : if (strcmp (typelib_namespace, namespace) != 0)
2000 : : {
2001 : 0 : g_set_error (error, GI_REPOSITORY_ERROR,
2002 : : GI_REPOSITORY_ERROR_NAMESPACE_MISMATCH,
2003 : : "Typelib file %s for namespace '%s' contains "
2004 : : "namespace '%s' which doesn't match the file name",
2005 : 0 : path, namespace, typelib_namespace);
2006 : 0 : goto out;
2007 : : }
2008 : 585 : if (version != NULL && strcmp (typelib_version, version) != 0)
2009 : : {
2010 : 0 : g_set_error (error, GI_REPOSITORY_ERROR,
2011 : : GI_REPOSITORY_ERROR_NAMESPACE_MISMATCH,
2012 : : "Typelib file %s for namespace '%s' contains "
2013 : : "version '%s' which doesn't match the expected version '%s'",
2014 : 0 : path, namespace, typelib_version, version);
2015 : 0 : goto out;
2016 : : }
2017 : :
2018 : 875 : if (!register_internal (repository, path, allow_lazy,
2019 : 290 : typelib, error))
2020 : 0 : goto out;
2021 : 585 : ret = typelib;
2022 : 296 : out:
2023 : 587 : g_clear_pointer (&typelib_owned, gi_typelib_unref);
2024 : 587 : g_free (tmp_version);
2025 : 587 : g_free (path);
2026 : 587 : return ret;
2027 : 574 : }
2028 : :
2029 : : static GITypelib *
2030 : 163 : require_internal_with_platform_data (GIRepository *repository,
2031 : : const char *namespace,
2032 : : const char *version,
2033 : : GIRepositoryLoadFlags flags,
2034 : : const char * const *search_paths,
2035 : : size_t search_paths_len,
2036 : : GError **error)
2037 : : {
2038 : : GITypelib *typelib;
2039 : :
2040 : 244 : typelib = require_internal (repository, namespace, version, flags,
2041 : 81 : search_paths, search_paths_len,
2042 : 81 : error);
2043 : 163 : if (!typelib)
2044 : 2 : return NULL;
2045 : :
2046 : : #if defined (G_OS_UNIX) || defined (G_OS_WIN32)
2047 : : /* Backward compatibility hack: if we're loading GLib/Gio-2.0, we automatically
2048 : : * load the platform specific introspection data that used to exist inside
2049 : : * GLib/Gio-2.0
2050 : : */
2051 : 223 : if ((g_str_equal (namespace, "GLib") || g_str_equal (namespace, "Gio")) &&
2052 : 187 : (!version || g_str_equal (version, "2.0")))
2053 : : {
2054 : 125 : GError *local_error = NULL;
2055 : 125 : char *platform_namespace = NULL;
2056 : :
2057 : : # if defined (G_OS_UNIX)
2058 : 63 : platform_namespace = g_strconcat (namespace, "Unix", NULL);
2059 : : # elif defined (G_OS_WIN32)
2060 : 62 : platform_namespace = g_strconcat (namespace, "Win32", NULL);
2061 : : # endif /* defined (G_OS_LINUX) */
2062 : :
2063 : 187 : if (!require_internal (repository, platform_namespace, version, flags,
2064 : 62 : search_paths, search_paths_len,
2065 : : &local_error))
2066 : : {
2067 : 0 : g_critical ("Unable to load platform-specific GIO introspection data: %s",
2068 : 0 : local_error->message);
2069 : 0 : g_error_free (local_error);
2070 : 0 : }
2071 : :
2072 : 125 : g_clear_pointer (&platform_namespace, g_free);
2073 : 62 : }
2074 : : #endif /* defined(G_OS_UNIX) || defined(G_OS_WIN32) */
2075 : :
2076 : 249 : return typelib;
2077 : 169 : }
2078 : :
2079 : : /**
2080 : : * gi_repository_require:
2081 : : * @repository: A #GIRepository
2082 : : * @namespace_: GI namespace to use, e.g. `Gtk`
2083 : : * @version: (nullable): Version of namespace, may be `NULL` for latest
2084 : : * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
2085 : : * @error: a [type@GLib.Error].
2086 : : *
2087 : : * Force the namespace @namespace_ to be loaded if it isn’t already.
2088 : : *
2089 : : * If @namespace_ is not loaded, this function will search for a
2090 : : * `.typelib` file using the repository search path. In addition, a
2091 : : * version @version of namespace may be specified. If @version is
2092 : : * not specified, the latest will be used.
2093 : : *
2094 : : * Returns: (transfer none): a pointer to the [type@GIRepository.Typelib] if
2095 : : * successful, `NULL` otherwise
2096 : : * Since: 2.80
2097 : : */
2098 : : GITypelib *
2099 : 163 : gi_repository_require (GIRepository *repository,
2100 : : const char *namespace,
2101 : : const char *version,
2102 : : GIRepositoryLoadFlags flags,
2103 : : GError **error)
2104 : : {
2105 : : const char * const *search_paths;
2106 : : size_t search_paths_len;
2107 : :
2108 : 163 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
2109 : 163 : g_return_val_if_fail (namespace != NULL, NULL);
2110 : :
2111 : 163 : search_paths = (const char * const *) repository->typelib_search_path->pdata;
2112 : 163 : search_paths_len = repository->typelib_search_path->len;
2113 : :
2114 : 244 : return require_internal_with_platform_data (repository, namespace, version, flags,
2115 : 81 : search_paths, search_paths_len,
2116 : 81 : error);
2117 : 81 : }
2118 : :
2119 : : /**
2120 : : * gi_repository_require_private:
2121 : : * @repository: A #GIRepository
2122 : : * @typelib_dir: (type filename): Private directory where to find the requested
2123 : : * typelib
2124 : : * @namespace_: GI namespace to use, e.g. `Gtk`
2125 : : * @version: (nullable): Version of namespace, may be `NULL` for latest
2126 : : * @flags: Set of [flags@GIRepository.RepositoryLoadFlags], may be 0
2127 : : * @error: a [type@GLib.Error].
2128 : : *
2129 : : * Force the namespace @namespace_ to be loaded if it isn’t already.
2130 : : *
2131 : : * If @namespace_ is not loaded, this function will search for a
2132 : : * `.typelib` file within the private directory only. In addition, a
2133 : : * version @version of namespace should be specified. If @version is
2134 : : * not specified, the latest will be used.
2135 : : *
2136 : : * Returns: (transfer none): a pointer to the [type@GIRepository.Typelib] if
2137 : : * successful, `NULL` otherwise
2138 : : * Since: 2.80
2139 : : */
2140 : : GITypelib *
2141 : 0 : gi_repository_require_private (GIRepository *repository,
2142 : : const char *typelib_dir,
2143 : : const char *namespace,
2144 : : const char *version,
2145 : : GIRepositoryLoadFlags flags,
2146 : : GError **error)
2147 : : {
2148 : 0 : const char * const search_path[] = { typelib_dir, NULL };
2149 : :
2150 : 0 : g_return_val_if_fail (GI_IS_REPOSITORY (repository), NULL);
2151 : 0 : g_return_val_if_fail (namespace != NULL, NULL);
2152 : :
2153 : 0 : return require_internal_with_platform_data (repository, namespace, version, flags,
2154 : 0 : search_path, 1,
2155 : 0 : error);
2156 : 0 : }
2157 : :
2158 : : static gboolean
2159 : 0 : gi_repository_introspect_cb (const char *option_name,
2160 : : const char *value,
2161 : : gpointer data,
2162 : : GError **error)
2163 : : {
2164 : 0 : GError *tmp_error = NULL;
2165 : : char **args;
2166 : :
2167 : 0 : args = g_strsplit (value, ",", 2);
2168 : :
2169 : 0 : if (!gi_repository_dump (args[0], args[1], &tmp_error))
2170 : : {
2171 : 0 : g_error ("Failed to extract GType data: %s",
2172 : 0 : tmp_error->message);
2173 : 0 : exit (1);
2174 : : }
2175 : 0 : exit (0);
2176 : : }
2177 : :
2178 : : static const GOptionEntry introspection_args[] = {
2179 : : { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
2180 : : gi_repository_introspect_cb, "Dump introspection information",
2181 : : "infile.txt,outfile.xml" },
2182 : : G_OPTION_ENTRY_NULL
2183 : : };
2184 : :
2185 : : /**
2186 : : * gi_repository_get_option_group:
2187 : : *
2188 : : * Obtain the option group for girepository.
2189 : : *
2190 : : * It’s used by the dumper and for programs that want to provide introspection
2191 : : * information
2192 : : *
2193 : : * Returns: (transfer full): the option group
2194 : : * Since: 2.80
2195 : : */
2196 : : GOptionGroup *
2197 : 0 : gi_repository_get_option_group (void)
2198 : : {
2199 : : GOptionGroup *group;
2200 : 0 : group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL);
2201 : :
2202 : 0 : g_option_group_add_entries (group, introspection_args);
2203 : 0 : return group;
2204 : : }
2205 : :
2206 : : GQuark
2207 : 8 : gi_repository_error_quark (void)
2208 : : {
2209 : : static GQuark quark = 0;
2210 : 8 : if (quark == 0)
2211 : 6 : quark = g_quark_from_static_string ("g-irepository-error-quark");
2212 : 8 : return quark;
2213 : : }
2214 : :
2215 : : /**
2216 : : * gi_type_tag_to_string:
2217 : : * @type: the type_tag
2218 : : *
2219 : : * Obtain a string representation of @type
2220 : : *
2221 : : * Returns: the string
2222 : : * Since: 2.80
2223 : : */
2224 : : const char *
2225 : 412235 : gi_type_tag_to_string (GITypeTag type)
2226 : : {
2227 : 412235 : switch (type)
2228 : : {
2229 : 532 : case GI_TYPE_TAG_VOID:
2230 : 1082 : return "void";
2231 : 6 : case GI_TYPE_TAG_BOOLEAN:
2232 : 12 : return "gboolean";
2233 : 15 : case GI_TYPE_TAG_INT8:
2234 : 30 : return "gint8";
2235 : 558 : case GI_TYPE_TAG_UINT8:
2236 : 1116 : return "guint8";
2237 : 0 : case GI_TYPE_TAG_INT16:
2238 : 0 : return "gint16";
2239 : 6 : case GI_TYPE_TAG_UINT16:
2240 : 12 : return "guint16";
2241 : 54 : case GI_TYPE_TAG_INT32:
2242 : 84 : return "gint32";
2243 : 29 : case GI_TYPE_TAG_UINT32:
2244 : 58 : return "guint32";
2245 : 0 : case GI_TYPE_TAG_INT64:
2246 : 0 : return "gint64";
2247 : 6 : case GI_TYPE_TAG_UINT64:
2248 : 12 : return "guint64";
2249 : 0 : case GI_TYPE_TAG_FLOAT:
2250 : 0 : return "gfloat";
2251 : 6 : case GI_TYPE_TAG_DOUBLE:
2252 : 12 : return "gdouble";
2253 : 9 : case GI_TYPE_TAG_UNICHAR:
2254 : 18 : return "gunichar";
2255 : 30 : case GI_TYPE_TAG_GTYPE:
2256 : 60 : return "GType";
2257 : 717 : case GI_TYPE_TAG_UTF8:
2258 : 1424 : return "utf8";
2259 : 124 : case GI_TYPE_TAG_FILENAME:
2260 : 248 : return "filename";
2261 : 13415 : case GI_TYPE_TAG_ARRAY:
2262 : 26740 : return "array";
2263 : 186824 : case GI_TYPE_TAG_INTERFACE:
2264 : 371735 : return "interface";
2265 : 2857 : case GI_TYPE_TAG_GLIST:
2266 : 5656 : return "glist";
2267 : 180 : case GI_TYPE_TAG_GSLIST:
2268 : 360 : return "gslist";
2269 : 945 : case GI_TYPE_TAG_GHASH:
2270 : 1890 : return "ghash";
2271 : 843 : case GI_TYPE_TAG_ERROR:
2272 : 1686 : return "error";
2273 : 0 : default:
2274 : 0 : return "unknown";
2275 : : }
2276 : 205079 : }
2277 : :
2278 : : /**
2279 : : * gi_info_type_to_string:
2280 : : * @type: the info type
2281 : : *
2282 : : * Obtain a string representation of @type
2283 : : *
2284 : : * Returns: the string
2285 : : * Since: 2.80
2286 : : */
2287 : : const char *
2288 : 0 : gi_info_type_to_string (GIInfoType type)
2289 : : {
2290 : 0 : switch (type)
2291 : : {
2292 : 0 : case GI_INFO_TYPE_INVALID:
2293 : 0 : return "invalid";
2294 : 0 : case GI_INFO_TYPE_FUNCTION:
2295 : 0 : return "function";
2296 : 0 : case GI_INFO_TYPE_CALLBACK:
2297 : 0 : return "callback";
2298 : 0 : case GI_INFO_TYPE_STRUCT:
2299 : 0 : return "struct";
2300 : 0 : case GI_INFO_TYPE_ENUM:
2301 : 0 : return "enum";
2302 : 0 : case GI_INFO_TYPE_FLAGS:
2303 : 0 : return "flags";
2304 : 0 : case GI_INFO_TYPE_OBJECT:
2305 : 0 : return "object";
2306 : 0 : case GI_INFO_TYPE_INTERFACE:
2307 : 0 : return "interface";
2308 : 0 : case GI_INFO_TYPE_CONSTANT:
2309 : 0 : return "constant";
2310 : 0 : case GI_INFO_TYPE_UNION:
2311 : 0 : return "union";
2312 : 0 : case GI_INFO_TYPE_VALUE:
2313 : 0 : return "value";
2314 : 0 : case GI_INFO_TYPE_SIGNAL:
2315 : 0 : return "signal";
2316 : 0 : case GI_INFO_TYPE_VFUNC:
2317 : 0 : return "vfunc";
2318 : 0 : case GI_INFO_TYPE_PROPERTY:
2319 : 0 : return "property";
2320 : 0 : case GI_INFO_TYPE_FIELD:
2321 : 0 : return "field";
2322 : 0 : case GI_INFO_TYPE_ARG:
2323 : 0 : return "arg";
2324 : 0 : case GI_INFO_TYPE_TYPE:
2325 : 0 : return "type";
2326 : 0 : case GI_INFO_TYPE_UNRESOLVED:
2327 : 0 : return "unresolved";
2328 : 0 : default:
2329 : 0 : return "unknown";
2330 : : }
2331 : 0 : }
2332 : :
2333 : : GIInfoType
2334 : 4045 : gi_typelib_blob_type_to_info_type (GITypelibBlobType blob_type)
2335 : : {
2336 : 4045 : switch (blob_type)
2337 : : {
2338 : 2 : case BLOB_TYPE_BOXED:
2339 : : /* `BLOB_TYPE_BOXED` now always refers to a `StructBlob`, and
2340 : : * `GIRegisteredTypeInfo` (the parent type of `GIStructInfo`) has a method
2341 : : * for distinguishing whether the struct is a boxed type. So presenting
2342 : : * `BLOB_TYPE_BOXED` as its own `GIBaseInfo` subclass is not helpful.
2343 : : * See commit e28078c70cbf4a57c7dbd39626f43f9bd2674145 and
2344 : : * https://gitlab.gnome.org/GNOME/glib/-/issues/3245. */
2345 : 4 : return GI_INFO_TYPE_STRUCT;
2346 : 2019 : default:
2347 : 4041 : return (GIInfoType) blob_type;
2348 : : }
2349 : 2024 : }
2350 : :
2351 : : /**
2352 : : * gi_repository_dup_default:
2353 : : *
2354 : : * Gets the singleton process-global default `GIRepository`.
2355 : : *
2356 : : * The singleton is needed for situations where you must coordinate between
2357 : : * bindings and libraries which also need to interact with introspection which
2358 : : * could affect the bindings. For example, a Python application using a
2359 : : * GObject-based library through `GIRepository` to load plugins also written in
2360 : : * Python.
2361 : : *
2362 : : * Returns: (transfer full): the global singleton repository
2363 : : *
2364 : : * Since: 2.86
2365 : : */
2366 : : GIRepository *
2367 : 4 : gi_repository_dup_default (void)
2368 : : {
2369 : : static GIRepository *instance;
2370 : :
2371 : 4 : if (g_once_init_enter (&instance))
2372 : : {
2373 : 2 : GIRepository *repository = gi_repository_new ();
2374 : 2 : g_object_add_weak_pointer (G_OBJECT (repository), (gpointer *)&instance);
2375 : 2 : g_once_init_leave (&instance, repository);
2376 : 1 : }
2377 : :
2378 : 4 : return g_object_ref (instance);
2379 : : }
|