Use GLib types consistently
[platform/upstream/gobject-introspection.git] / girepository / girepository.c
1 /* -*- Mode: C; c-file-style: "gnu"; -*- */
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  * 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 <stdio.h>
25 #include <string.h>
26 #include <stdlib.h>
27
28 #include <glib.h>
29 #include <glib/gprintf.h>
30 #include <gmodule.h>
31 #include "girepository.h"
32 #include "gitypelib-internal.h"
33 #include "girepository-private.h"
34 #include "glib-compat.h"
35
36 #include "config.h"
37
38 static GStaticMutex globals_lock = G_STATIC_MUTEX_INIT;
39 static GIRepository *default_repository = NULL;
40 static GSList *search_path = NULL;
41 static GSList *override_search_path = NULL;
42
43 struct _GIRepositoryPrivate
44 {
45   GHashTable *typelibs; /* (string) namespace -> GTypelib */
46   GHashTable *lazy_typelibs; /* (string) namespace-version -> GTypelib */
47   GHashTable *info_by_gtype; /* GType -> GIBaseInfo */
48 };
49
50 G_DEFINE_TYPE (GIRepository, g_irepository, G_TYPE_OBJECT);
51
52 static void
53 g_irepository_init (GIRepository *repository)
54 {
55   repository->priv = G_TYPE_INSTANCE_GET_PRIVATE (repository, G_TYPE_IREPOSITORY,
56                                                   GIRepositoryPrivate);
57   repository->priv->typelibs
58     = g_hash_table_new_full (g_str_hash, g_str_equal,
59                              (GDestroyNotify) NULL,
60                              (GDestroyNotify) g_typelib_free);
61   repository->priv->lazy_typelibs
62     = g_hash_table_new (g_str_hash, g_str_equal);
63   repository->priv->info_by_gtype
64     = g_hash_table_new_full (g_direct_hash, g_direct_equal,
65                              (GDestroyNotify) NULL,
66                              (GDestroyNotify) g_base_info_unref);
67 }
68
69 static void
70 g_irepository_finalize (GObject *object)
71 {
72   GIRepository *repository = G_IREPOSITORY (object);
73
74   g_hash_table_destroy (repository->priv->typelibs);
75   g_hash_table_destroy (repository->priv->lazy_typelibs);
76   g_hash_table_destroy (repository->priv->info_by_gtype);
77
78   (* G_OBJECT_CLASS (g_irepository_parent_class)->finalize) (G_OBJECT (repository));
79 }
80
81 static void
82 g_irepository_class_init (GIRepositoryClass *class)
83 {
84   GObjectClass *gobject_class;
85
86   gobject_class = G_OBJECT_CLASS (class);
87
88   gobject_class->finalize = g_irepository_finalize;
89
90   g_type_class_add_private (class, sizeof (GIRepositoryPrivate));
91 }
92
93 static void
94 init_globals (void)
95 {
96   g_static_mutex_lock (&globals_lock);
97
98   if (default_repository == NULL)
99     {
100       default_repository = g_object_new (G_TYPE_IREPOSITORY, NULL);
101     }
102
103   if (search_path == NULL)
104     {
105       const char *libdir;
106       char *typelib_dir;
107       const gchar *type_lib_path_env;
108
109       /* This variable is intended to take precedence over both the default
110        * search path, as well as anything written into code with g_irepository_prepend_search_path.
111        */
112       type_lib_path_env = g_getenv ("GI_TYPELIB_PATH");
113
114       search_path = NULL;
115       override_search_path = NULL;
116       if (type_lib_path_env)
117         {
118           gchar **custom_dirs;
119           gchar **d;
120
121           custom_dirs = g_strsplit (type_lib_path_env, G_SEARCHPATH_SEPARATOR_S, 0);
122
123           d = custom_dirs;
124           while (*d)
125             {
126               override_search_path = g_slist_prepend (override_search_path, *d);
127               d++;
128             }
129
130           /* ownership of the array content was passed to the list */
131           g_free (custom_dirs);
132         }
133
134       if (override_search_path != NULL)
135         override_search_path = g_slist_reverse (override_search_path);
136
137       libdir = GOBJECT_INTROSPECTION_LIBDIR;
138
139       typelib_dir = g_build_filename (libdir, "girepository-1.0", NULL);
140
141       search_path = g_slist_prepend (search_path, typelib_dir);
142
143       search_path = g_slist_reverse (search_path);
144     }
145
146   g_static_mutex_unlock (&globals_lock);
147 }
148
149 void
150 g_irepository_prepend_search_path (const char *directory)
151 {
152   init_globals ();
153   search_path = g_slist_prepend (search_path, g_strdup (directory));
154 }
155
156 /**
157  * g_irepository_get_search_path:
158  *
159  * Returns the search path the GIRepository will use when looking for typelibs.
160  * The string is internal to GIRespository and should not be freed, nor should
161  * the elements.
162  *
163  * Return value: (element-type filename) (transfer none): list of strings
164  */
165 GSList *
166 g_irepository_get_search_path (void)
167 {
168   return search_path;
169 }
170
171 static
172 GSList *
173 build_search_path_with_overrides (void)
174 {
175         GSList *result;
176         if (override_search_path != NULL)
177           {
178                 result = g_slist_copy (override_search_path);
179                 g_slist_last (result)->next = g_slist_copy (search_path);
180           }
181         else
182           result = g_slist_copy (search_path);
183         return result;
184 }
185
186 static char *
187 build_typelib_key (const char *name, const char *source)
188 {
189   GString *str = g_string_new (name);
190   g_string_append_c (str, '\0');
191   g_string_append (str, source);
192   return g_string_free (str, FALSE);
193 }
194
195 static char **
196 get_typelib_dependencies (GTypelib *typelib)
197 {
198   Header *header;
199   const char *dependencies_glob;
200
201   header = (Header *)typelib->data;
202
203   if (header->dependencies == 0)
204     return NULL;
205
206   dependencies_glob = g_typelib_get_string (typelib, header->dependencies);
207   return g_strsplit (dependencies_glob, "|", 0);
208 }
209
210 static GIRepository *
211 get_repository (GIRepository *repository)
212 {
213   init_globals ();
214
215   if (repository != NULL)
216     return repository;
217   else
218     return default_repository;
219 }
220
221 static GTypelib *
222 check_version_conflict (GTypelib *typelib,
223                         const gchar *namespace,
224                         const gchar *expected_version,
225                         char       **version_conflict)
226 {
227   Header *header;
228   const char *loaded_version;
229
230   if (expected_version == NULL)
231     {
232       if (version_conflict)
233         *version_conflict = NULL;
234       return typelib;
235     }
236
237   header = (Header*)typelib->data;
238   loaded_version = g_typelib_get_string (typelib, header->nsversion);
239   g_assert (loaded_version != NULL);
240
241   if (strcmp (expected_version, loaded_version) != 0)
242     {
243       if (version_conflict)
244         *version_conflict = (char*)loaded_version;
245       return NULL;
246     }
247   if (version_conflict)
248     *version_conflict = NULL;
249   return typelib;
250 }
251
252 static GTypelib *
253 get_registered_status (GIRepository *repository,
254                        const char   *namespace,
255                        const char   *version,
256                        gboolean      allow_lazy,
257                        gboolean     *lazy_status,
258                        char        **version_conflict)
259 {
260   GTypelib *typelib;
261   repository = get_repository (repository);
262   if (lazy_status)
263     *lazy_status = FALSE;
264   typelib = g_hash_table_lookup (repository->priv->typelibs, namespace);
265   if (typelib)
266     return check_version_conflict (typelib, namespace, version, version_conflict);
267   typelib = g_hash_table_lookup (repository->priv->lazy_typelibs, namespace);
268   if (!typelib)
269     return NULL;
270   if (lazy_status)
271     *lazy_status = TRUE;
272   if (!allow_lazy)
273     return NULL;
274   return check_version_conflict (typelib, namespace, version, version_conflict);
275 }
276
277 static GTypelib *
278 get_registered (GIRepository *repository,
279                 const char   *namespace,
280                 const char   *version)
281 {
282   return get_registered_status (repository, namespace, version, TRUE, NULL, NULL);
283 }
284
285 static gboolean
286 load_dependencies_recurse (GIRepository *repository,
287                            GTypelib     *typelib,
288                            GError      **error)
289 {
290   char **dependencies;
291
292   dependencies = get_typelib_dependencies (typelib);
293
294   if (dependencies != NULL)
295     {
296       int i;
297
298       for (i = 0; dependencies[i]; i++)
299         {
300           char *dependency = dependencies[i];
301           const char *last_dash;
302           char *dependency_namespace;
303           const char *dependency_version;
304
305           last_dash = strrchr (dependency, '-');
306           dependency_namespace = g_strndup (dependency, last_dash - dependency);
307           dependency_version = last_dash+1;
308
309           if (!g_irepository_require (repository, dependency_namespace, dependency_version,
310                                       0, error))
311             {
312               g_free (dependency_namespace);
313               g_strfreev (dependencies);
314               return FALSE;
315             }
316           g_free (dependency_namespace);
317         }
318       g_strfreev (dependencies);
319     }
320   return TRUE;
321 }
322
323 static const char *
324 register_internal (GIRepository *repository,
325                    const char   *source,
326                    gboolean      lazy,
327                    GTypelib     *typelib,
328                    GError      **error)
329 {
330   Header *header;
331   const gchar *namespace;
332   const gchar *version;
333
334   g_return_val_if_fail (typelib != NULL, FALSE);
335
336   header = (Header *)typelib->data;
337
338   g_return_val_if_fail (header != NULL, FALSE);
339
340   namespace = g_typelib_get_string (typelib, header->namespace);
341   version = g_typelib_get_string (typelib, header->nsversion);
342
343   if (lazy)
344     {
345       g_assert (!g_hash_table_lookup (repository->priv->lazy_typelibs,
346                                       namespace));
347       g_hash_table_insert (repository->priv->lazy_typelibs,
348                            build_typelib_key (namespace, source), (void *)typelib);
349     }
350   else
351     {
352       gpointer value;
353       char *key;
354
355       /* First, try loading all the dependencies */
356       if (!load_dependencies_recurse (repository, typelib, error))
357         return NULL;
358
359       /* Check if we are transitioning from lazily loaded state */
360       if (g_hash_table_lookup_extended (repository->priv->lazy_typelibs,
361                                         namespace,
362                                         (gpointer)&key, &value))
363         g_hash_table_remove (repository->priv->lazy_typelibs, key);
364       else
365         key = build_typelib_key (namespace, source);
366
367       g_hash_table_insert (repository->priv->typelibs, key, (void *)typelib);
368     }
369
370   return namespace;
371 }
372
373 /**
374  * g_irepository_get_dependencies:
375  * @repository: A #GIRepository, may be %NULL for the default
376  * @namespace_: Namespace of interest
377  *
378  * Return an array of all (transitive) dependencies for namespace
379  * @namespace_, including version.  The returned strings are of the
380  * form <code>namespace-version</code>.
381  *
382  * Note: The namespace must have already been loaded using a function
383  * such as #g_irepository_require before calling this function.
384  *
385  * Returns: Zero-terminated string array of versioned dependencies
386  */
387 char **
388 g_irepository_get_dependencies (GIRepository *repository,
389                                 const char *namespace)
390 {
391   GTypelib *typelib;
392
393   g_return_val_if_fail (namespace != NULL, NULL);
394
395   repository = get_repository (repository);
396
397   typelib = get_registered (repository, namespace, NULL);
398   g_return_val_if_fail (typelib != NULL, NULL);
399
400   return get_typelib_dependencies (typelib);
401 }
402
403 const char *
404 g_irepository_load_typelib (GIRepository *repository,
405                             GTypelib     *typelib,
406                             GIRepositoryLoadFlags flags,
407                             GError      **error)
408 {
409   Header *header;
410   const char *namespace;
411   const char *nsversion;
412   gboolean allow_lazy = flags & G_IREPOSITORY_LOAD_FLAG_LAZY;
413   gboolean is_lazy;
414   char *version_conflict;
415
416   repository = get_repository (repository);
417
418   header = (Header *) typelib->data;
419   namespace = g_typelib_get_string (typelib, header->namespace);
420   nsversion = g_typelib_get_string (typelib, header->nsversion);
421
422   if (get_registered_status (repository, namespace, nsversion, allow_lazy,
423                              &is_lazy, &version_conflict))
424     {
425       if (version_conflict != NULL)
426         {
427           g_set_error (error, G_IREPOSITORY_ERROR,
428                        G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
429                        "Attempting to load namespace '%s', version '%s', but '%s' is already loaded",
430                        namespace, nsversion, version_conflict);
431           return NULL;
432         }
433       return namespace;
434     }
435   return register_internal (repository, "<builtin>",
436                             allow_lazy, typelib, error);
437 }
438
439 /**
440  * g_irepository_is_registered:
441  * @repository: A #GIRepository, may be %NULL for the default
442  * @namespace_: Namespace of interest
443  * @version: (allow-none): Required version, may be %NULL for latest
444  *
445  * Check whether a particular namespace (and optionally, a specific
446  * version thereof) is currently loaded.  This function is likely to
447  * only be useful in unusual circumstances; in order to act upon
448  * metadata in the namespace, you should call #g_irepository_require
449  * instead which will ensure the namespace is loaded, and return as
450  * quickly as this function will if it has already been loaded.
451  *
452  * Returns: %TRUE if namespace-version is loaded, %FALSE otherwise
453  */
454 gboolean
455 g_irepository_is_registered (GIRepository *repository,
456                              const gchar *namespace,
457                              const gchar *version)
458 {
459   repository = get_repository (repository);
460   return get_registered (repository, namespace, version) != NULL;
461 }
462
463 /**
464  * g_irepository_get_default:
465  *
466  * Returns the singleton process-global default #GIRepository.  It is
467  * not currently supported to have multiple repositories in a
468  * particular process, but this function is provided in the unlikely
469  * eventuality that it would become possible, and as a convenience for
470  * higher level language bindings to conform to the GObject method
471  * call conventions.
472
473  * All methods on #GIRepository also accept %NULL as an instance
474  * parameter to mean this default repository, which is usually more
475  * convenient for C.
476  *
477  * Returns: (transfer none): The global singleton #GIRepository
478  */
479 GIRepository *
480 g_irepository_get_default (void)
481 {
482   return get_repository (NULL);
483 }
484
485 /**
486  * g_irepository_get_n_infos:
487  * @repository: A #GIRepository, may be %NULL for the default
488  * @namespace_: Namespace to inspect
489  *
490  * This function returns the number of metadata entries in
491  * given namespace @namespace_.  The namespace must have
492  * already been loaded before calling this function.
493  *
494  * Returns: number of metadata entries
495  */
496 gint
497 g_irepository_get_n_infos (GIRepository *repository,
498                            const gchar  *namespace)
499 {
500   GTypelib *typelib;
501   gint n_interfaces = 0;
502
503   g_return_val_if_fail (namespace != NULL, -1);
504
505   repository = get_repository (repository);
506
507   typelib = get_registered (repository, namespace, NULL);
508
509   g_return_val_if_fail (typelib != NULL, -1);
510
511   n_interfaces = ((Header *)typelib->data)->n_local_entries;
512
513   return n_interfaces;
514 }
515
516 typedef struct
517 {
518   GIRepository *repo;
519   gint index;
520   const gchar *name;
521   gboolean type_firstpass;
522   const gchar *type;
523   GIBaseInfo *iface;
524 } IfaceData;
525
526 static void
527 find_interface (gpointer key,
528                 gpointer value,
529                 gpointer data)
530 {
531   gint i;
532   GTypelib *typelib = (GTypelib *)value;
533   Header *header = (Header *) typelib->data;
534   IfaceData *iface_data = (IfaceData *)data;
535   gint index;
536   gint n_entries;
537   const gchar *name;
538   const gchar *type;
539   DirEntry *entry;
540
541   index = 0;
542   n_entries = ((Header *)typelib->data)->n_local_entries;
543
544   if (iface_data->name)
545     {
546       for (i = 1; i <= n_entries; i++)
547         {
548           entry = g_typelib_get_dir_entry (typelib, i);
549           name = g_typelib_get_string (typelib, entry->name);
550           if (strcmp (name, iface_data->name) == 0)
551             {
552               index = i;
553               break;
554             }
555         }
556     }
557   else if (iface_data->type)
558     {
559       const char *c_prefix;
560       /* Inside each typelib, we include the "C prefix" which acts as
561        * a namespace mechanism.  For GtkTreeView, the C prefix is Gtk.
562        * Given the assumption that GTypes for a library also use the
563        * C prefix, we know we can skip examining a typelib if our
564        * target type does not have this typelib's C prefix.
565        *
566        * However, not every class library necessarily conforms to this,
567        * e.g. Clutter has Cogl inside it.  So, we split this into two
568        * passes.  First we try a lookup, skipping things which don't
569        * have the prefix.  If that fails then we try a global lookup,
570        * ignoring the prefix.
571        *
572        * See http://bugzilla.gnome.org/show_bug.cgi?id=564016
573        */
574       c_prefix = g_typelib_get_string (typelib, header->c_prefix);
575       if (iface_data->type_firstpass && c_prefix != NULL)
576         {
577           if (g_ascii_strncasecmp (c_prefix, iface_data->type, strlen (c_prefix)) != 0)
578             return;
579         }
580
581       for (i = 1; i <= n_entries; i++)
582         {
583           RegisteredTypeBlob *blob;
584
585           entry = g_typelib_get_dir_entry (typelib, i);
586           if (!BLOB_IS_REGISTERED_TYPE (entry))
587             continue;
588
589           blob = (RegisteredTypeBlob *)(&typelib->data[entry->offset]);
590           if (!blob->gtype_name)
591             continue;
592
593           type = g_typelib_get_string (typelib, blob->gtype_name);
594           if (strcmp (type, iface_data->type) == 0)
595             {
596               index = i;
597               break;
598             }
599         }
600     }
601   else if (iface_data->index > n_entries)
602     iface_data->index -= n_entries;
603   else if (iface_data->index > 0)
604     {
605       index = iface_data->index;
606       iface_data->index = 0;
607     }
608
609   if (index != 0)
610     {
611       entry = g_typelib_get_dir_entry (typelib, index);
612       iface_data->iface = _g_info_new_full (entry->blob_type,
613                                             iface_data->repo,
614                                             NULL, typelib, entry->offset);
615     }
616 }
617
618 /**
619  * g_irepository_get_info:
620  * @repository: A #GIRepository, may be %NULL for the default
621  * @namespace_: Namespace to inspect
622  * @index: Offset into namespace metadata for entry
623  *
624  * This function returns a particular metadata entry in the
625  * given namespace @namespace_.  The namespace must have
626  * already been loaded before calling this function.
627  *
628  * Returns: #GIBaseInfo containing metadata
629  */
630 GIBaseInfo *
631 g_irepository_get_info (GIRepository *repository,
632                         const gchar  *namespace,
633                         gint          index)
634 {
635   IfaceData data;
636   GTypelib *typelib;
637
638   g_return_val_if_fail (namespace != NULL, NULL);
639
640   repository = get_repository (repository);
641
642   data.repo = repository;
643   data.name = NULL;
644   data.type = NULL;
645   data.index = index + 1;
646   data.iface = NULL;
647
648   typelib = get_registered (repository, namespace, NULL);
649
650   g_return_val_if_fail (typelib != NULL, NULL);
651
652   find_interface ((void *)namespace, typelib, &data);
653
654   return data.iface;
655 }
656
657 /**
658  * g_irepository_find_by_gtype:
659  * @repository: A #GIRepository, may be %NULL for the default
660  * @gtype: GType to search for
661  *
662  * Searches all loaded namespaces for a particular #GType.  Note that
663  * in order to locate the metadata, the namespace corresponding to
664  * the type must first have been loaded.  There is currently no
665  * mechanism for determining the namespace which corresponds to an
666  * arbitrary GType - thus, this function will operate most reliably
667  * when you know the GType to originate from be from a loaded namespace.
668  *
669  * Returns: #GIBaseInfo representing metadata about @type, or %NULL
670  */
671 GIBaseInfo *
672 g_irepository_find_by_gtype (GIRepository *repository,
673                              GType         gtype)
674 {
675   IfaceData data;
676
677   repository = get_repository (repository);
678
679   data.iface = g_hash_table_lookup (repository->priv->info_by_gtype,
680                                     (gpointer)gtype);
681
682   if (data.iface)
683     return g_base_info_ref (data.iface);
684
685   data.repo = repository;
686   data.name = NULL;
687   data.type_firstpass = TRUE;
688   data.type = g_type_name (gtype);
689   data.index = -1;
690   data.iface = NULL;
691
692   g_hash_table_foreach (repository->priv->typelibs, find_interface, &data);
693   g_hash_table_foreach (repository->priv->lazy_typelibs, find_interface, &data);
694
695   /* We do two passes; see comment in find_interface */
696   if (!data.iface)
697     {
698       data.type_firstpass = FALSE;
699       g_hash_table_foreach (repository->priv->typelibs, find_interface, &data);
700       g_hash_table_foreach (repository->priv->lazy_typelibs, find_interface, &data);
701     }
702
703   if (data.iface)
704     g_hash_table_insert (repository->priv->info_by_gtype,
705                          (gpointer) gtype,
706                          g_base_info_ref (data.iface));
707
708   return data.iface;
709 }
710
711 /**
712  * g_irepository_find_by_name:
713  * @repository: A #GIRepository, may be %NULL for the default
714  * @namespace_: Namespace which will be searched
715  * @name: Entry name to find
716  *
717  * Searches for a particular entry in a namespace.  Before calling
718  * this function for a particular namespace, you must call
719  * #g_irepository_require once to load the namespace, or otherwise
720  * ensure the namespace has already been loaded.
721  *
722  * Returns: #GIBaseInfo representing metadata about @name, or %NULL
723  */
724 GIBaseInfo *
725 g_irepository_find_by_name (GIRepository *repository,
726                             const gchar  *namespace,
727                             const gchar  *name)
728 {
729   IfaceData data;
730   GTypelib *typelib;
731
732   g_return_val_if_fail (namespace != NULL, NULL);
733
734   repository = get_repository (repository);
735
736   data.repo = repository;
737   data.name = name;
738   data.type = NULL;
739   data.index = -1;
740   data.iface = NULL;
741
742   typelib = get_registered (repository, namespace, NULL);
743
744   g_return_val_if_fail (typelib != NULL, NULL);
745
746   find_interface ((void *)namespace, typelib, &data);
747
748   return data.iface;
749 }
750
751 static void
752 collect_namespaces (gpointer key,
753                     gpointer value,
754                     gpointer data)
755 {
756   GList **list = data;
757
758   *list = g_list_append (*list, key);
759 }
760
761 /**
762  * g_irepository_get_namespaces:
763  * @repository: A #GIRepository, may be %NULL for the default
764  *
765  * Return the list of currently loaded namespaces.
766  *
767  * Returns: (utf8) (transfer full): List of namespaces
768  */
769 gchar **
770 g_irepository_get_loaded_namespaces (GIRepository *repository)
771 {
772   GList *l, *list = NULL;
773   gchar **names;
774   gint i;
775
776   repository = get_repository (repository);
777
778   g_hash_table_foreach (repository->priv->typelibs, collect_namespaces, &list);
779   g_hash_table_foreach (repository->priv->lazy_typelibs, collect_namespaces, &list);
780
781   names = g_malloc0 (sizeof (gchar *) * (g_list_length (list) + 1));
782   i = 0;
783   for (l = list; l; l = l->next)
784     names[i++] = g_strdup (l->data);
785   g_list_free (list);
786
787   return names;
788 }
789
790 /**
791  * g_irepository_get_version:
792  * @repository: A #GIRepository, may be %NULL for the default
793  * @namespace_: Namespace to inspect
794  *
795  * This function returns the loaded version associated with the given
796  * namespace @namespace_.
797  *
798  * Note: The namespace must have already been loaded using a function
799  * such as #g_irepository_require before calling this function.
800  *
801  * Returns: Loaded version
802  */
803 const gchar *
804 g_irepository_get_version (GIRepository *repository,
805                            const gchar  *namespace)
806 {
807   GTypelib *typelib;
808   Header *header;
809
810   g_return_val_if_fail (namespace != NULL, NULL);
811
812   repository = get_repository (repository);
813
814   typelib = get_registered (repository, namespace, NULL);
815
816   g_return_val_if_fail (typelib != NULL, NULL);
817
818   header = (Header *) typelib->data;
819   return g_typelib_get_string (typelib, header->nsversion);
820 }
821
822 /**
823  * g_irepository_get_shared_library:
824  * @repository: A #GIRepository, may be %NULL for the default
825  * @namespace_: Namespace to inspect
826  *
827  * This function returns the full path to the shared C library
828  * associated with the given namespace @namespace_. There may be no
829  * shared library path associated, in which case this function will
830  * return %NULL.
831  *
832  * Note: The namespace must have already been loaded using a function
833  * such as #g_irepository_require before calling this function.
834  *
835  * Returns: Full path to shared library, or %NULL if none associated
836  */
837 const gchar *
838 g_irepository_get_shared_library (GIRepository *repository,
839                                   const gchar  *namespace)
840 {
841   GTypelib *typelib;
842   Header *header;
843
844   g_return_val_if_fail (namespace != NULL, NULL);
845
846   repository = get_repository (repository);
847
848   typelib = get_registered (repository, namespace, NULL);
849
850   g_return_val_if_fail (typelib != NULL, NULL);
851
852   header = (Header *) typelib->data;
853   if (header->shared_library)
854     return g_typelib_get_string (typelib, header->shared_library);
855   else
856     return NULL;
857 }
858
859 /**
860  * g_irepository_get_c_prefix
861  * @repository: A #GIRepository, may be %NULL for the default
862  * @namespace_: Namespace to inspect
863  *
864  * This function returns the "C prefix", or the C level namespace
865  * associated with the given introspection namespace.  Each C symbol
866  * starts with this prefix, as well each #GType in the library.
867  *
868  * Note: The namespace must have already been loaded using a function
869  * such as #g_irepository_require before calling this function.
870  *
871  * Returns: C namespace prefix, or %NULL if none associated
872  */
873 const gchar *
874 g_irepository_get_c_prefix (GIRepository *repository,
875                             const gchar  *namespace_)
876 {
877   GTypelib *typelib;
878   Header *header;
879
880   g_return_val_if_fail (namespace_ != NULL, NULL);
881
882   repository = get_repository (repository);
883
884   typelib = get_registered (repository, namespace_, NULL);
885
886   g_return_val_if_fail (typelib != NULL, NULL);
887
888   header = (Header *) typelib->data;
889   if (header->shared_library)
890     return g_typelib_get_string (typelib, header->c_prefix);
891   else
892     return NULL;
893 }
894
895 /**
896  * g_irepository_get_typelib_path
897  * @repository: Repository, may be %NULL for the default
898  * @namespace_: GI namespace to use, e.g. "Gtk"
899  *
900  * If namespace @namespace_ is loaded, return the full path to the
901  * .typelib file it was loaded from.  If the typelib for
902  * namespace @namespace_ was included in a shared library, return
903  * the special string "$lt;builtin$gt;".
904  *
905  * Returns: Filesystem path (or $lt;builtin$gt;) if successful, %NULL if namespace is not loaded
906  */
907
908 const gchar *
909 g_irepository_get_typelib_path (GIRepository *repository,
910                                 const gchar  *namespace)
911 {
912   gpointer orig_key, value;
913
914   repository = get_repository (repository);
915
916   if (!g_hash_table_lookup_extended (repository->priv->typelibs, namespace,
917                                      &orig_key, &value))
918     {
919       if (!g_hash_table_lookup_extended (repository->priv->lazy_typelibs, namespace,
920                                          &orig_key, &value))
921
922         return NULL;
923     }
924   return ((char*)orig_key) + strlen ((char *) orig_key) + 1;
925 }
926
927 /* This simple search function looks for a specified namespace-version;
928    it's faster than the full directory listing required for latest version. */
929 static GMappedFile *
930 find_namespace_version (const gchar  *namespace,
931                         const gchar  *version,
932                         GSList       *search_path,
933                         gchar       **path_ret)
934 {
935   GSList *ldir;
936   GError *error = NULL;
937   GMappedFile *mfile = NULL;
938   char *fname;
939
940   fname = g_strdup_printf ("%s-%s.typelib", namespace, version);
941
942   for (ldir = search_path; ldir; ldir = ldir->next)
943     {
944       char *path = g_build_filename (ldir->data, fname, NULL);
945
946       mfile = g_mapped_file_new (path, FALSE, &error);
947       if (error)
948         {
949           g_free (path);
950           g_clear_error (&error);
951           continue;
952         }
953       *path_ret = path;
954       break;
955     }
956   g_free (fname);
957   return mfile;
958 }
959
960 static gboolean
961 parse_version (const char *version,
962                int *major,
963                int *minor)
964 {
965   const char *dot;
966   char *end;
967
968   *major = strtol (version, &end, 10);
969   dot = strchr (version, '.');
970   if (dot == NULL)
971     {
972       *minor = 0;
973       return TRUE;
974     }
975   if (dot != end)
976     return FALSE;
977   *minor = strtol (dot+1, &end, 10);
978   if (end != (version + strlen (version)))
979     return FALSE;
980   return TRUE;
981 }
982
983 static int
984 compare_version (const char *v1,
985                  const char *v2)
986 {
987   gboolean success;
988   int v1_major, v1_minor;
989   int v2_major, v2_minor;
990
991   success = parse_version (v1, &v1_major, &v1_minor);
992   g_assert (success);
993
994   success = parse_version (v2, &v2_major, &v2_minor);
995   g_assert (success);
996
997   if (v1_major > v2_major)
998     return 1;
999   else if (v2_major > v1_major)
1000     return -1;
1001   else if (v1_minor > v2_minor)
1002     return 1;
1003   else if (v2_minor > v1_minor)
1004     return -1;
1005   return 0;
1006 }
1007
1008 struct NamespaceVersionCandidadate
1009 {
1010   GMappedFile *mfile;
1011   int path_index;
1012   char *path;
1013   char *version;
1014 };
1015
1016 static int
1017 compare_candidate_reverse (struct NamespaceVersionCandidadate *c1,
1018                            struct NamespaceVersionCandidadate *c2)
1019 {
1020   int result = compare_version (c1->version, c2->version);
1021   /* First, check the version */
1022   if (result > 0)
1023     return -1;
1024   else if (result < 0)
1025     return 1;
1026   else
1027     {
1028       /* Now check the path index, which says how early in the search path
1029        * we found it.  This ensures that of equal version targets, we
1030        * pick the earlier one.
1031        */
1032       if (c1->path_index == c2->path_index)
1033         return 0;
1034       else if (c1->path_index > c2->path_index)
1035         return 1;
1036       else
1037         return -1;
1038     }
1039 }
1040
1041 static void
1042 free_candidate (struct NamespaceVersionCandidadate *candidate)
1043 {
1044   g_mapped_file_unref (candidate->mfile);
1045   g_free (candidate->path);
1046   g_free (candidate->version);
1047   g_slice_free (struct NamespaceVersionCandidadate, candidate);
1048 }
1049
1050 static GSList *
1051 enumerate_namespace_versions (const gchar *namespace,
1052                               GSList      *search_path)
1053 {
1054   GSList *candidates = NULL;
1055   GHashTable *found_versions = g_hash_table_new (g_str_hash, g_str_equal);
1056   char *namespace_dash;
1057   char *namespace_typelib;
1058   GSList *ldir;
1059   GError *error = NULL;
1060   int index;
1061
1062   namespace_dash = g_strdup_printf ("%s-", namespace);
1063   namespace_typelib = g_strdup_printf ("%s.typelib", namespace);
1064
1065   index = 0;
1066   for (ldir = search_path; ldir; ldir = ldir->next)
1067     {
1068       GDir *dir;
1069       const char *dirname;
1070       const char *entry;
1071
1072       dirname = (const char*)ldir->data;
1073       dir = g_dir_open (dirname, 0, NULL);
1074       if (dir == NULL)
1075         continue;
1076       while ((entry = g_dir_read_name (dir)) != NULL)
1077         {
1078           GMappedFile *mfile;
1079           char *path, *version;
1080           struct NamespaceVersionCandidadate *candidate;
1081
1082           if (!g_str_has_suffix (entry, ".typelib"))
1083             continue;
1084
1085           if (g_str_has_prefix (entry, namespace_dash))
1086             {
1087               const char *last_dash;
1088               const char *name_end;
1089               int major, minor;
1090
1091               name_end = strrchr (entry, '.');
1092               last_dash = strrchr (entry, '-');
1093               version = g_strndup (last_dash+1, name_end-(last_dash+1));
1094               if (!parse_version (version, &major, &minor))
1095                 continue;
1096             }
1097           else
1098             continue;
1099
1100           if (g_hash_table_lookup (found_versions, version) != NULL)
1101             continue;
1102           g_hash_table_insert (found_versions, version, version);
1103
1104           path = g_build_filename (dirname, entry, NULL);
1105           mfile = g_mapped_file_new (path, FALSE, &error);
1106           if (mfile == NULL)
1107             {
1108               g_free (path);
1109               g_free (version);
1110               g_clear_error (&error);
1111               continue;
1112             }
1113           candidate = g_slice_new0 (struct NamespaceVersionCandidadate);
1114           candidate->mfile = mfile;
1115           candidate->path_index = index;
1116           candidate->path = path;
1117           candidate->version = version;
1118           candidates = g_slist_prepend (candidates, candidate);
1119         }
1120       g_dir_close (dir);
1121       index++;
1122     }
1123
1124   g_free (namespace_dash);
1125   g_free (namespace_typelib);
1126   g_hash_table_destroy (found_versions);
1127
1128   return candidates;
1129 }
1130
1131 static GMappedFile *
1132 find_namespace_latest (const gchar  *namespace,
1133                        GSList       *search_path,
1134                        gchar       **version_ret,
1135                        gchar       **path_ret)
1136 {
1137   GSList *candidates;
1138   GMappedFile *result = NULL;
1139
1140   *version_ret = NULL;
1141   *path_ret = NULL;
1142
1143   candidates = enumerate_namespace_versions (namespace, search_path);
1144
1145   if (candidates != NULL)
1146     {
1147       struct NamespaceVersionCandidadate *elected;
1148       candidates = g_slist_sort (candidates, (GCompareFunc) compare_candidate_reverse);
1149
1150       elected = (struct NamespaceVersionCandidadate *) candidates->data;
1151       /* Remove the elected one so we don't try to free its contents */
1152       candidates = g_slist_delete_link (candidates, candidates);
1153
1154       result = elected->mfile;
1155       *path_ret = elected->path;
1156       *version_ret = elected->version;
1157       g_slice_free (struct NamespaceVersionCandidadate, elected); /* just free the container */
1158       g_slist_foreach (candidates, (GFunc) free_candidate, NULL);
1159       g_slist_free (candidates);
1160     }
1161   return result;
1162 }
1163
1164 /**
1165  * g_irepository_enumerate_versions:
1166  * @repository: (allow-none): the repository
1167  * @namespace_: GI namespace, e.g. "Gtk"
1168  *
1169  * Obtain an unordered list of versions (either currently loaded or
1170  * available) for @namespace_ in this @repository.
1171  *
1172  * Returns: (element-type utf8) (transfer full): the array of versions.
1173  */
1174 GList *
1175 g_irepository_enumerate_versions (GIRepository *repository,
1176                          const gchar  *namespace_)
1177 {
1178   GList *ret = NULL;
1179   GSList *search_path;
1180   GSList *candidates, *link;
1181   const gchar *loaded_version;
1182
1183   search_path = build_search_path_with_overrides ();
1184   candidates = enumerate_namespace_versions (namespace_, search_path);
1185   g_slist_free (search_path);
1186
1187   for (link = candidates; link; link = link->next)
1188     {
1189       struct NamespaceVersionCandidadate *candidate = link->data;
1190       ret = g_list_prepend (ret, g_strdup (candidate->version));
1191       free_candidate (candidate);
1192     }
1193   g_slist_free (candidates);
1194
1195   /* The currently loaded version of a namespace is also part of the
1196    * available versions, as it could have been loaded using
1197    * require_private().
1198    */
1199   if (g_irepository_is_registered (repository, namespace_, NULL))
1200     {
1201       loaded_version = g_irepository_get_version (repository, namespace_);
1202       if (loaded_version && !g_list_find_custom (ret, loaded_version, g_str_equal))
1203         ret = g_list_prepend (ret, g_strdup (loaded_version));
1204     }
1205
1206   return ret;
1207 }
1208
1209 static GTypelib *
1210 require_internal (GIRepository  *repository,
1211                   const gchar   *namespace,
1212                   const gchar   *version,
1213                   GIRepositoryLoadFlags flags,
1214                   GSList        *search_path,
1215                   GError       **error)
1216 {
1217   GMappedFile *mfile;
1218   GTypelib *ret = NULL;
1219   Header *header;
1220   GTypelib *typelib = NULL;
1221   const gchar *typelib_namespace, *typelib_version;
1222   gboolean allow_lazy = (flags & G_IREPOSITORY_LOAD_FLAG_LAZY) > 0;
1223   gboolean is_lazy;
1224   char *version_conflict = NULL;
1225   char *path = NULL;
1226   char *tmp_version = NULL;
1227
1228   g_return_val_if_fail (namespace != NULL, FALSE);
1229
1230   repository = get_repository (repository);
1231
1232   typelib = get_registered_status (repository, namespace, version, allow_lazy,
1233                                    &is_lazy, &version_conflict);
1234   if (typelib)
1235     return typelib;
1236
1237   if (version_conflict != NULL)
1238     {
1239       g_set_error (error, G_IREPOSITORY_ERROR,
1240                    G_IREPOSITORY_ERROR_NAMESPACE_VERSION_CONFLICT,
1241                    "Requiring namespace '%s' version '%s', but '%s' is already loaded",
1242                    namespace, version, version_conflict);
1243       return NULL;
1244     }
1245
1246   if (version != NULL)
1247     {
1248       mfile = find_namespace_version (namespace, version,
1249                                       search_path, &path);
1250       tmp_version = g_strdup (version);
1251     }
1252   else
1253     {
1254       mfile = find_namespace_latest (namespace, search_path,
1255                                      &tmp_version, &path);
1256     }
1257
1258   if (mfile == NULL)
1259     {
1260       if (version != NULL)
1261         g_set_error (error, G_IREPOSITORY_ERROR,
1262                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1263                      "Typelib file for namespace '%s', version '%s' not found",
1264                      namespace, version);
1265       else
1266         g_set_error (error, G_IREPOSITORY_ERROR,
1267                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1268                      "Typelib file for namespace '%s' (any version) not found",
1269                      namespace);
1270       goto out;
1271     }
1272
1273   {
1274     GError *temp_error = NULL;
1275     typelib = g_typelib_new_from_mapped_file (mfile, &temp_error);
1276     if (!typelib)
1277       {
1278         g_set_error (error, G_IREPOSITORY_ERROR,
1279                      G_IREPOSITORY_ERROR_TYPELIB_NOT_FOUND,
1280                      "Failed to load typelib file '%s' for namespace '%s': %s",
1281                      path, namespace, temp_error->message);
1282         g_clear_error (&temp_error);
1283         goto out;
1284       }
1285   }
1286   header = (Header *) typelib->data;
1287   typelib_namespace = g_typelib_get_string (typelib, header->namespace);
1288   typelib_version = g_typelib_get_string (typelib, header->nsversion);
1289
1290   if (strcmp (typelib_namespace, namespace) != 0)
1291     {
1292       g_set_error (error, G_IREPOSITORY_ERROR,
1293                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
1294                    "Typelib file %s for namespace '%s' contains "
1295                    "namespace '%s' which doesn't match the file name",
1296                    path, namespace, typelib_namespace);
1297       g_typelib_free (typelib);
1298       goto out;
1299     }
1300   if (version != NULL && strcmp (typelib_version, version) != 0)
1301     {
1302       g_set_error (error, G_IREPOSITORY_ERROR,
1303                    G_IREPOSITORY_ERROR_NAMESPACE_MISMATCH,
1304                    "Typelib file %s for namespace '%s' contains "
1305                    "version '%s' which doesn't match the expected version '%s'",
1306                    path, namespace, typelib_version, version);
1307       g_typelib_free (typelib);
1308       goto out;
1309     }
1310
1311   if (!register_internal (repository, path, allow_lazy,
1312                           typelib, error))
1313     {
1314       g_typelib_free (typelib);
1315       goto out;
1316     }
1317   ret = typelib;
1318  out:
1319   g_free (tmp_version);
1320   g_free (path);
1321   return ret;
1322 }
1323
1324 /**
1325  * g_irepository_require:
1326  * @repository: (allow-none): Repository, may be %NULL for the default
1327  * @namespace_: GI namespace to use, e.g. "Gtk"
1328  * @version: (allow-none): Version of namespace, may be %NULL for latest
1329  * @flags: Set of %GIRepositoryLoadFlags, may be 0
1330  * @error: a #GError.
1331  *
1332  * Force the namespace @namespace_ to be loaded if it isn't already.
1333  * If @namespace_ is not loaded, this function will search for a
1334  * ".typelib" file using the repository search path.  In addition, a
1335  * version @version of namespace may be specified.  If @version is
1336  * not specified, the latest will be used.
1337  *
1338  * Returns: a pointer to the #GTypelib if successful, %NULL otherwise
1339  */
1340 GTypelib *
1341 g_irepository_require (GIRepository  *repository,
1342                        const gchar   *namespace,
1343                        const gchar   *version,
1344                        GIRepositoryLoadFlags flags,
1345                        GError       **error)
1346 {
1347   GSList *search_path;
1348   GTypelib *typelib;
1349
1350   search_path = build_search_path_with_overrides ();
1351   typelib = require_internal (repository, namespace, version, flags,
1352                               search_path, error);
1353   g_slist_free (search_path);
1354
1355   return typelib;
1356 }
1357
1358 /**
1359  * g_irepository_require_private:
1360  * @repository: (allow-none): Repository, may be %NULL for the default
1361  * @typelib_dir: Private directory where to find the requested typelib
1362  * @namespace_: GI namespace to use, e.g. "Gtk"
1363  * @version: (allow-none): Version of namespace, may be %NULL for latest
1364  * @flags: Set of %GIRepositoryLoadFlags, may be 0
1365  * @error: a #GError.
1366  *
1367  * Force the namespace @namespace_ to be loaded if it isn't already.
1368  * If @namespace_ is not loaded, this function will search for a
1369  * ".typelib" file within the private directory only. In addition, a
1370  * version @version of namespace should be specified.  If @version is
1371  * not specified, the latest will be used.
1372  *
1373  * Returns: a pointer to the #GTypelib if successful, %NULL otherwise
1374  */
1375 GTypelib *
1376 g_irepository_require_private (GIRepository  *repository,
1377                                const gchar   *typelib_dir,
1378                                const gchar   *namespace,
1379                                const gchar   *version,
1380                                GIRepositoryLoadFlags flags,
1381                                GError       **error)
1382 {
1383   GSList search_path = { (gpointer) typelib_dir, NULL };
1384
1385   return require_internal (repository, namespace, version, flags,
1386                            &search_path, error);
1387 }
1388
1389 static gboolean
1390 g_irepository_introspect_cb (const char *option_name,
1391                              const char *value,
1392                              gpointer data,
1393                              GError **error)
1394 {
1395   GError *tmp_error = NULL;
1396   gboolean ret = g_irepository_dump (value, &tmp_error);
1397   if (!ret)
1398     {
1399       g_error ("Failed to extract GType data: %s",
1400                tmp_error->message);
1401       exit (1);
1402     }
1403   exit (0);
1404 }
1405
1406 static const GOptionEntry introspection_args[] = {
1407   { "introspect-dump", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK,
1408     g_irepository_introspect_cb, "Dump introspection information",
1409     "infile.txt,outfile.xml" },
1410   { NULL }
1411 };
1412
1413 GOptionGroup *
1414 g_irepository_get_option_group (void)
1415 {
1416   GOptionGroup *group;
1417   group = g_option_group_new ("girepository", "Introspection Options", "Show Introspection Options", NULL, NULL);
1418
1419   g_option_group_add_entries (group, introspection_args);
1420   return group;
1421 }
1422
1423 GQuark
1424 g_irepository_error_quark (void)
1425 {
1426   static GQuark quark = 0;
1427   if (quark == 0)
1428     quark = g_quark_from_static_string ("g-irepository-error-quark");
1429   return quark;
1430 }
1431
1432 /**
1433  * g_type_tag_to_string:
1434  * @type: the type_tag
1435  *
1436  * Obtain a string representation of @type
1437  *
1438  * Returns: the string
1439  */
1440 const gchar*
1441 g_type_tag_to_string (GITypeTag type)
1442 {
1443   switch (type)
1444     {
1445     case GI_TYPE_TAG_VOID:
1446       return "void";
1447     case GI_TYPE_TAG_BOOLEAN:
1448       return "gboolean";
1449     case GI_TYPE_TAG_INT8:
1450       return "gint8";
1451     case GI_TYPE_TAG_UINT8:
1452       return "guint8";
1453     case GI_TYPE_TAG_INT16:
1454       return "gint16";
1455     case GI_TYPE_TAG_UINT16:
1456       return "guint16";
1457     case GI_TYPE_TAG_INT32:
1458       return "gint32";
1459     case GI_TYPE_TAG_UINT32:
1460       return "guint32";
1461     case GI_TYPE_TAG_INT64:
1462       return "gint64";
1463     case GI_TYPE_TAG_UINT64:
1464       return "guint64";
1465     case GI_TYPE_TAG_FLOAT:
1466       return "gfloat";
1467     case GI_TYPE_TAG_DOUBLE:
1468       return "gdouble";
1469     case GI_TYPE_TAG_GTYPE:
1470       return "GType";
1471     case GI_TYPE_TAG_UTF8:
1472       return "utf8";
1473     case GI_TYPE_TAG_FILENAME:
1474       return "filename";
1475     case GI_TYPE_TAG_ARRAY:
1476       return "array";
1477     case GI_TYPE_TAG_INTERFACE:
1478       return "interface";
1479     case GI_TYPE_TAG_GLIST:
1480       return "glist";
1481     case GI_TYPE_TAG_GSLIST:
1482       return "gslist";
1483     case GI_TYPE_TAG_GHASH:
1484       return "ghash";
1485     case GI_TYPE_TAG_ERROR:
1486       return "error";
1487     default:
1488       return "unknown";
1489     }
1490 }
1491
1492 /**
1493  * g_info_type_to_string:
1494  * @type: the info type
1495  *
1496  * Obtain a string representation of @type
1497  *
1498  * Returns: the string
1499  */
1500 const gchar*
1501 g_info_type_to_string (GIInfoType type)
1502 {
1503   switch (type)
1504     {
1505     case GI_INFO_TYPE_INVALID:
1506       return "invalid";
1507     case GI_INFO_TYPE_FUNCTION:
1508       return "function";
1509     case GI_INFO_TYPE_CALLBACK:
1510       return "callback";
1511     case GI_INFO_TYPE_STRUCT:
1512       return "struct";
1513     case GI_INFO_TYPE_BOXED:
1514       return "boxed";
1515     case GI_INFO_TYPE_ENUM:
1516       return "enum";
1517     case GI_INFO_TYPE_FLAGS:
1518       return "flags";
1519     case GI_INFO_TYPE_OBJECT:
1520       return "object";
1521     case GI_INFO_TYPE_INTERFACE:
1522       return "interface";
1523     case GI_INFO_TYPE_CONSTANT:
1524       return "constant";
1525     case GI_INFO_TYPE_ERROR_DOMAIN:
1526       return "error domain";
1527     case GI_INFO_TYPE_UNION:
1528       return "union";
1529     case GI_INFO_TYPE_VALUE:
1530       return "value";
1531     case GI_INFO_TYPE_SIGNAL:
1532       return "signal";
1533     case GI_INFO_TYPE_VFUNC:
1534       return "vfunc";
1535     case GI_INFO_TYPE_PROPERTY:
1536       return "property";
1537     case GI_INFO_TYPE_FIELD:
1538       return "field";
1539     case GI_INFO_TYPE_ARG:
1540       return "arg";
1541     case GI_INFO_TYPE_TYPE:
1542       return "type";
1543     case GI_INFO_TYPE_UNRESOLVED:
1544       return "unresolved";
1545     default:
1546       return "unknown";
1547   }
1548 }