change run to use meson/ninja and then exec. - remove libvala code from application...
[roobuilder] / src / codegen / valagirwriter.vala
1 /* valagirwriter.vala
2  *
3  * Copyright (C) 2008-2012  Jürg Billeter
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18  *
19  * Author:
20  *      Jürg Billeter <j@bitron.ch>
21  */
22
23 using GLib;
24
25 /**
26  * Code visitor generating .gir file for the public interface.
27  */
28 public class Vala.GIRWriter : CodeVisitor {
29         private CodeContext context;
30         private string directory;
31         private string gir_namespace;
32         private string gir_version;
33         private string gir_shared_library;
34
35         protected virtual string? get_interface_comment (Interface iface) {
36                 return null;
37         }
38
39         protected virtual string? get_struct_comment (Struct st) {
40                 return null;
41         }
42
43         protected virtual string? get_enum_comment (Enum en) {
44                 return null;
45         }
46
47         protected virtual string? get_class_comment (Class c) {
48                 return null;
49         }
50
51         protected virtual string? get_error_code_comment (ErrorCode ecode) {
52                 return null;
53         }
54
55         protected virtual string? get_enum_value_comment (EnumValue ev) {
56                 return null;
57         }
58
59         protected virtual string? get_constant_comment (Constant c) {
60                 return null;
61         }
62
63         protected virtual string? get_error_domain_comment (ErrorDomain edomain) {
64                 return null;
65         }
66
67         protected virtual string? get_field_comment (Field f) {
68                 return null;
69         }
70
71         protected virtual string? get_delegate_comment (Delegate cb) {
72                 return null;
73         }
74
75         protected virtual string? get_method_comment (Method m) {
76                 return null;
77         }
78
79         protected virtual string? get_property_comment (Property prop) {
80                 return null;
81         }
82
83         protected virtual string? get_delegate_return_comment (Delegate cb) {
84                 return null;
85         }
86
87         protected virtual string? get_signal_return_comment (Signal sig) {
88                 return null;
89         }
90
91         protected virtual string? get_method_return_comment (Method m) {
92                 return null;
93         }
94
95         protected virtual string? get_signal_comment (Signal sig) {
96                 return null;
97         }
98
99         protected virtual string? get_parameter_comment (Parameter param) {
100                 return null;
101         }
102
103         StringBuilder buffer = new StringBuilder();
104         FileStream stream;
105         Vala.HashSet<Namespace> unannotated_namespaces = new Vala.HashSet<Namespace>();
106         Vala.HashSet<Namespace> our_namespaces = new Vala.HashSet<Namespace>();
107         Vala.ArrayList<Vala.Symbol> hierarchy = new Vala.ArrayList<Vala.Symbol>();
108         Vala.ArrayList<Vala.CodeNode> deferred = new Vala.ArrayList<Vala.CodeNode>();
109
110         int indent;
111
112         private TypeSymbol gobject_type;
113         private TypeSymbol ginitiallyunowned_type;
114         private TypeSymbol gtypeinterface_type;
115         private TypeSymbol gtypeinstance_type;
116         private TypeSymbol gtype_type;
117
118         private struct GIRNamespace {
119                 public GIRNamespace (string ns, string version) {
120                         this.ns = ns; this.version = version;
121                 }
122                 public string ns;
123                 public string version;
124                 public bool equal (GIRNamespace g) {
125                         return ((ns == g.ns) && (version == g.version));
126                 }
127
128                 public static GIRNamespace for_symbol (Symbol sym) {
129                         while (sym.parent_symbol != null && sym.parent_symbol.name != null) {
130                                 sym = sym.parent_symbol;
131                         }
132                         assert (sym is Namespace);
133                         string gir_namespace = sym.get_attribute_string ("CCode", "gir_namespace");
134                         string gir_version = sym.get_attribute_string ("CCode", "gir_version");
135                         return GIRNamespace (gir_namespace, gir_version);
136                 }
137         }
138
139         private ArrayList<GIRNamespace?> externals = new ArrayList<GIRNamespace?> ((EqualFunc<GIRNamespace>) GIRNamespace.equal);
140
141         public void write_includes() {
142                 foreach (var i in externals) {
143                         if (i.ns != this.gir_namespace) {
144                                 write_indent_stream ();
145                                 stream.printf ("<include name=\"%s\" version=\"%s\"/>\n", i.ns, i.version);
146                         }
147                 }
148         }
149
150
151         /**
152          * Writes the public interface of the specified code context into the
153          * specified file.
154          *
155          * @param context      a code context
156          * @param gir_filename a relative or absolute filename
157          */
158         public void write_file (CodeContext context, string directory, string gir_filename, string gir_namespace, string gir_version, string package, string? gir_shared_library = null) {
159                 this.context = context;
160                 this.directory = directory;
161                 this.gir_namespace = gir_namespace;
162                 this.gir_version = gir_version;
163                 this.gir_shared_library = gir_shared_library;
164
165                 var root_symbol = context.root;
166                 var glib_ns = root_symbol.scope.lookup ("GLib");
167                 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
168                 ginitiallyunowned_type = (TypeSymbol) glib_ns.scope.lookup ("InitiallyUnowned");
169                 gtypeinterface_type = (TypeSymbol) glib_ns.scope.lookup ("TypeInterface");
170                 gtypeinstance_type = (TypeSymbol) glib_ns.scope.lookup ("TypeInstance");
171                 gtype_type = (TypeSymbol) glib_ns.scope.lookup ("Type");
172
173                 write_package (package);
174
175                 // Make sure to initialize external files with their gir_namespace/version
176                 foreach (var file in context.get_source_files ()) {
177                         file.accept (this);
178                 }
179
180                 context.accept (this);
181
182                 indent--;
183                 buffer.append_printf ("</repository>\n");
184
185                 string filename = "%s%c%s".printf (directory, Path.DIR_SEPARATOR, gir_filename);
186                 var file_exists = FileUtils.test (filename, FileTest.EXISTS);
187                 var temp_filename = "%s.valatmp".printf (filename);
188
189                 if (file_exists) {
190                         stream = FileStream.open (temp_filename, "w");
191                 } else {
192                         stream = FileStream.open (filename, "w");
193                 }
194
195                 if (stream == null) {
196                         Report.error (null, "unable to open `%s' for writing", filename);
197                         this.context = null;
198                         return;
199                 }
200
201                 stream.printf ("<?xml version=\"1.0\"?>\n");
202
203                 var header = context.version_header ?
204                         "<!-- %s generated by %s %s, do not modify. -->".printf (Path.get_basename (filename), Environment.get_prgname (), Vala.BUILD_VERSION) :
205                         "<!-- %s generated by %s, do not modify. -->".printf (Path.get_basename (filename), Environment.get_prgname ());
206                 stream.printf ("%s\n", header);
207
208                 stream.printf ("<repository version=\"1.2\"");
209                 stream.printf (" xmlns=\"http://www.gtk.org/introspection/core/1.0\"");
210                 stream.printf (" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"");
211                 stream.printf (" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
212                 stream.printf (">\n");
213                 indent++;
214
215                 write_includes();
216                 indent--;
217
218                 stream.puts (buffer.str);
219                 stream = null;
220
221                 if (file_exists) {
222                         var changed = true;
223
224                         try {
225                                 var old_file = new MappedFile (filename, false);
226                                 var new_file = new MappedFile (temp_filename, false);
227                                 var len = old_file.get_length ();
228                                 if (len == new_file.get_length ()) {
229                                         if (Memory.cmp (old_file.get_contents (), new_file.get_contents (), len) == 0) {
230                                                 changed = false;
231                                         }
232                                 }
233                                 old_file = null;
234                                 new_file = null;
235                         } catch (FileError e) {
236                                 // assume changed if mmap comparison doesn't work
237                         }
238
239                         if (changed) {
240                                 FileUtils.rename (temp_filename, filename);
241                         } else {
242                                 FileUtils.unlink (temp_filename);
243                         }
244                 }
245
246                 foreach (var ns in unannotated_namespaces) {
247                         if (!our_namespaces.contains(ns)) {
248                                 Report.warning (ns.source_reference, "Namespace `%s' does not have a GIR namespace and version annotation", ns.name);
249                         }
250                 }
251                 foreach (var ns in our_namespaces) {
252                         ns.source_reference.file.gir_namespace = gir_namespace;
253                         ns.source_reference.file.gir_version = gir_version;
254                 }
255
256                 if (our_namespaces.size == 0) {
257                         Report.error (null, "No suitable namespace found to export for GIR");
258                 }
259
260                 this.context = null;
261         }
262
263         private void write_doc (string? comment) {
264                 if (comment != null) {
265                         write_indent ();
266                         buffer.append ("<doc xml:whitespace=\"preserve\">");
267                         buffer.append (comment);
268                         buffer.append ("</doc>\n");
269                 }
270         }
271
272         private void write_package (string package) {
273                 write_indent ();
274                 buffer.append_printf ("<package name=\"%s\"/>\n", package);
275         }
276
277         private void write_c_includes (Namespace ns) {
278                 // Collect C header filenames
279                 Set<string> header_filenames = new HashSet<string> (str_hash, str_equal);
280                 foreach (unowned string c_header_filename in get_ccode_header_filenames (ns).split (",")) {
281                         header_filenames.add (c_header_filename);
282                 }
283                 foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) {
284                         if (symbol.external_package) {
285                                 continue;
286                         }
287                         foreach (unowned string c_header_filename in get_ccode_header_filenames (symbol).split (",")) {
288                                 header_filenames.add (c_header_filename);
289                         }
290                 }
291
292                 // Generate c:include tags
293                 foreach (string c_header_filename in header_filenames) {
294                         write_c_include (c_header_filename);
295                 }
296         }
297
298         private void write_c_include (string name) {
299                 write_indent ();
300                 buffer.append_printf ("<c:include name=\"%s\"/>\n", name);
301         }
302
303         public override void visit_source_file (SourceFile source_file) {
304                 if (source_file.file_type != SourceFileType.PACKAGE) {
305                         return;
306                 }
307
308                 // Populate gir_namespace/version of source-file like in Namespace.check()
309                 foreach (var node in source_file.get_nodes ()) {
310                         if (node is Namespace && ((Namespace) node).parent_symbol == context.root) {
311                                 var a = node.get_attribute ("CCode");
312                                 if (a != null && a.has_argument ("gir_namespace")) {
313                                         var new_gir = a.get_string ("gir_namespace");
314                                         var old_gir = source_file.gir_namespace;
315                                         if (old_gir != null && old_gir != new_gir) {
316                                                 source_file.gir_ambiguous = true;
317                                         }
318                                         source_file.gir_namespace = new_gir;
319                                 }
320                                 if (a != null && a.has_argument ("gir_version")) {
321                                         source_file.gir_version = a.get_string ("gir_version");
322                                 }
323                                 break;
324                         }
325                 }
326         }
327
328         public override void visit_namespace (Namespace ns) {
329                 if (ns.external_package) {
330                         return;
331                 }
332
333                 if (!is_visibility (ns)) {
334                         return;
335                 }
336
337                 if (ns.name == null)  {
338                         // global namespace
339                         hierarchy.insert (0, ns);
340                         ns.accept_children (this);
341                         hierarchy.remove_at (0);
342                         return;
343                 }
344
345                 if (ns.parent_symbol.name != null) {
346                         ns.accept_children (this);
347                         return;
348                 }
349
350                 if (our_namespaces.size > 0) {
351                         Report.error (ns.source_reference, "Secondary top-level namespace `%s' is not supported by GIR format", ns.name);
352                         return;
353                 }
354
355                 // Use given gir_namespace and gir_version for our top-level namespace
356                 var old_gir_namespace = ns.get_attribute_string ("CCode", "gir_namespace");
357                 var old_gir_version = ns.get_attribute_string ("CCode", "gir_version");
358                 if ((old_gir_namespace != null && old_gir_namespace != gir_namespace)
359                     || (old_gir_version != null && old_gir_version != gir_version)) {
360                         Report.warning (ns.source_reference, "Replace conflicting CCode.gir_* attributes for namespace `%s'".printf (ns.name));
361                 }
362                 ns.set_attribute_string ("CCode", "gir_namespace", gir_namespace);
363                 ns.set_attribute_string ("CCode", "gir_version", gir_version);
364
365                 write_c_includes (ns);
366
367                 write_indent ();
368                 buffer.append_printf ("<namespace name=\"%s\" version=\"%s\"", gir_namespace, gir_version);
369                 string? cprefix = get_ccode_prefix (ns);
370                 if (gir_shared_library != null) {
371                         buffer.append_printf(" shared-library=\"%s\"", gir_shared_library);
372                 }
373                 if (cprefix != null) {
374                         buffer.append_printf (" c:prefix=\"%s\"", cprefix);
375                         buffer.append_printf (" c:identifier-prefixes=\"%s\"", cprefix);
376                 }
377                 string? csymbol_prefix = get_ccode_lower_case_suffix (ns);
378                 if (csymbol_prefix != null) {
379                         buffer.append_printf (" c:symbol-prefixes=\"%s\"", csymbol_prefix);
380                 }
381                 buffer.append_printf (">\n");
382                 indent++;
383
384                 hierarchy.insert (0, ns);
385                 ns.accept_children (this);
386                 hierarchy.remove_at (0);
387
388                 indent--;
389                 write_indent ();
390                 buffer.append_printf ("</namespace>\n");
391                 our_namespaces.add(ns);
392
393                 visit_deferred ();
394         }
395
396         private void write_symbol_attributes (Symbol symbol) {
397                 if (!is_introspectable (symbol)) {
398                         buffer.append_printf (" introspectable=\"0\"");
399                 }
400                 if (symbol.version.deprecated) {
401                         buffer.append_printf (" deprecated=\"1\"");
402                         if (symbol.version.deprecated_since != null) {
403                                 buffer.append_printf (" deprecated-version=\"%s\"", symbol.version.deprecated_since);
404                         }
405                 }
406                 if (symbol.version.since != null) {
407                         buffer.append_printf (" version=\"%s\"", symbol.version.since);
408                 }
409         }
410
411         public override void visit_class (Class cl) {
412                 if (cl.external_package) {
413                         return;
414                 }
415
416                 if (!check_accessibility (cl)) {
417                         return;
418                 }
419
420                 if (!has_namespace (cl)) {
421                         return;
422                 }
423
424                 if (!(hierarchy[0] is Namespace)) {
425                         deferred.add (cl);
426                         return;
427                 }
428
429                 if (!cl.is_compact) {
430                         string gtype_struct_name = get_gir_name (cl) + "Class";
431
432                         write_indent ();
433                         buffer.append_printf ("<class name=\"%s\"", get_gir_name (cl));
434                         write_gtype_attributes (cl, true);
435                         buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
436                         if (cl.base_class == null) {
437                                 buffer.append_printf (" glib:fundamental=\"1\"");
438                                 buffer.append_printf (" glib:ref-func=\"%s\"", get_ccode_ref_function (cl));
439                                 buffer.append_printf (" glib:unref-func=\"%s\"", get_ccode_unref_function (cl));
440                                 buffer.append_printf (" glib:set-value-func=\"%s\"", get_ccode_set_value_function (cl));
441                                 buffer.append_printf (" glib:get-value-func=\"%s\"", get_ccode_get_value_function (cl));
442                         } else {
443                                 buffer.append_printf (" parent=\"%s\"", gi_type_name (cl.base_class));
444                         }
445                         if (cl.is_abstract) {
446                                 buffer.append_printf (" abstract=\"1\"");
447                         }
448                         if (cl.is_sealed) {
449                                 buffer.append_printf (" final=\"1\"");
450                         }
451                         write_symbol_attributes (cl);
452                         buffer.append_printf (">\n");
453                         indent++;
454
455                         write_doc (get_class_comment (cl));
456
457                         // write implemented interfaces
458                         foreach (DataType base_type in cl.get_base_types ()) {
459                                 var object_type = (ObjectType) base_type;
460                                 if (object_type.type_symbol is Interface) {
461                                         write_indent ();
462                                         buffer.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
463                                 }
464                         }
465
466                         write_indent ();
467                         buffer.append_printf ("<field name=\"parent_instance\" readable=\"0\" private=\"1\">\n");
468                         indent++;
469                         write_indent ();
470                         if (cl.base_class == null) {
471                                 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (gtypeinstance_type), get_ccode_name (gtypeinstance_type));
472                         } else {
473                                 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
474                         }
475                         indent--;
476                         write_indent ();
477                         buffer.append_printf("</field>\n");
478
479                         if (cl.base_class == null) {
480                                 write_indent ();
481                                 buffer.append_printf ("<field name=\"ref_count\">\n");
482                                 indent++;
483                                 write_indent ();
484                                 buffer.append_printf ("<type name=\"gint\" c:type=\"volatile int\"/>\n");
485                                 indent--;
486                                 write_indent ();
487                                 buffer.append_printf("</field>\n");
488                         }
489
490                         if (!context.abi_stability) {
491                                 write_indent ();
492                                 buffer.append_printf ("<field name=\"priv\" readable=\"0\" private=\"1\">\n");
493                                 indent++;
494                                 write_indent ();
495                                 buffer.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", get_gir_name (cl), get_ccode_name (cl));
496                                 indent--;
497                                 write_indent ();
498                                 buffer.append_printf("</field>\n");
499                         }
500
501                         if (cl.base_class != null && cl.base_class.is_subtype_of (gobject_type)) {
502                                 foreach (var p in cl.get_type_parameters ()) {
503                                         write_type_parameter (p, "property");
504                                 }
505                         }
506
507                         hierarchy.insert (0, cl);
508                         cl.accept_children (this);
509                         hierarchy.remove_at (0);
510
511                         if (context.abi_stability) {
512                                 write_indent ();
513                                 buffer.append_printf ("<field name=\"priv\" readable=\"0\" private=\"1\">\n");
514                                 indent++;
515                                 write_indent ();
516                                 buffer.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", get_gir_name (cl), get_ccode_name (cl));
517                                 indent--;
518                                 write_indent ();
519                                 buffer.append_printf("</field>\n");
520                         }
521
522                         indent--;
523                         write_indent ();
524                         buffer.append_printf ("</class>\n");
525
526                         write_indent ();
527                         buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
528                         write_ctype_attributes (cl, "Class");
529                         buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", get_gir_name (cl));
530                         buffer.append_printf (">\n");
531                         indent++;
532
533                         write_indent ();
534                         buffer.append_printf ("<field name=\"parent_class\" readable=\"0\" private=\"1\">\n");
535                         indent++;
536                         write_indent ();
537                         if (cl.base_class == null) {
538                                 //FIXME GObject.TypeClass vs GType
539                                 buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", "GObject.Type", get_ccode_name (gtype_type));
540                         } else {
541                                 buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
542                         }
543                         indent--;
544                         write_indent ();
545                         buffer.append_printf ("</field>\n");
546
547                         foreach (Method m in cl.get_methods ()) {
548                                 if (m.is_abstract || m.is_virtual) {
549                                         if (m.coroutine) {
550                                                 string finish_name = m.name;
551                                                 if (finish_name.has_suffix ("_async")) {
552                                                         finish_name = finish_name.substring (0, finish_name.length - "_async".length);
553                                                 }
554                                                 finish_name += "_finish";
555
556                                                 write_indent ();
557                                                 buffer.append_printf("<field name=\"%s\"", m.name);
558                                                 write_symbol_attributes (m);
559                                                 buffer.append_printf (">\n");
560                                                 indent++;
561                                                 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false, false);
562                                                 indent--;
563                                                 write_indent ();
564                                                 buffer.append_printf ("</field>\n");
565
566                                                 write_indent ();
567                                                 buffer.append_printf("<field name=\"%s\"", finish_name);
568                                                 write_symbol_attributes (m);
569                                                 buffer.append_printf (">\n");
570                                                 indent++;
571                                                 do_write_signature (m, "callback", true, finish_name, get_ccode_finish_name (m), m.get_async_end_parameters (), m.return_type, m.tree_can_fail, false, false);
572                                                 indent--;
573                                                 write_indent ();
574                                                 buffer.append_printf ("</field>\n");
575                                         } else {
576                                                 write_indent ();
577                                                 buffer.append_printf("<field name=\"%s\"", m.name);
578                                                 write_symbol_attributes (m);
579                                                 buffer.append_printf (">\n");
580                                                 indent++;
581                                                 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false, false);
582                                                 indent--;
583                                                 write_indent ();
584                                                 buffer.append_printf ("</field>\n");
585                                         }
586                                 }
587                         }
588
589                         foreach (Signal sig in cl.get_signals ()) {
590                                 if (sig.default_handler != null) {
591                                         write_indent ();
592                                         buffer.append_printf ("<field name=\"%s\"", get_ccode_lower_case_name (sig));
593                                         write_symbol_attributes (sig);
594                                         buffer.append_printf (">\n");
595                                         indent++;
596                                         write_signature (sig.default_handler, "callback", false, true, false);
597                                         indent--;
598                                         write_indent ();
599                                         buffer.append_printf ("</field>\n");
600                                 }
601                         }
602
603                         indent--;
604                         write_indent ();
605                         buffer.append_printf ("</record>\n");
606
607                         write_indent ();
608                         buffer.append_printf ("<record name=\"%sPrivate\" c:type=\"%sPrivate\" disguised=\"1\"/>\n", get_gir_name (cl), get_ccode_name (cl));
609                 } else {
610                         write_indent ();
611                         buffer.append_printf ("<record name=\"%s\"", get_gir_name (cl));
612                         write_ctype_attributes (cl);
613                         write_symbol_attributes (cl);
614                         buffer.append_printf (">\n");
615                         indent++;
616
617                         write_doc (get_class_comment (cl));
618
619                         hierarchy.insert (0, cl);
620                         cl.accept_children (this);
621                         hierarchy.remove_at (0);
622
623                         indent--;
624                         write_indent ();
625                         buffer.append_printf ("</record>\n");
626                 }
627
628                 visit_deferred ();
629         }
630
631         public override void visit_struct (Struct st) {
632                 if (st.external_package) {
633                         return;
634                 }
635
636                 if (!check_accessibility (st)) {
637                         return;
638                 }
639
640                 if (!has_namespace (st)) {
641                         return;
642                 }
643
644                 if (!(hierarchy[0] is Namespace)) {
645                         deferred.add (st);
646                         return;
647                 }
648
649                 write_indent ();
650                 buffer.append_printf ("<record name=\"%s\"", get_gir_name (st));
651                 if (get_ccode_has_type_id (st)) {
652                         write_gtype_attributes (st, true);
653                 } else {
654                         write_ctype_attributes (st, "", true);
655                 }
656                 write_symbol_attributes (st);
657                 buffer.append_printf (">\n");
658                 indent++;
659
660                 write_doc (get_struct_comment (st));
661
662                 hierarchy.insert (0, st);
663                 st.accept_children (this);
664                 hierarchy.remove_at (0);
665
666                 indent--;
667                 write_indent ();
668                 buffer.append_printf ("</record>\n");
669
670                 visit_deferred ();
671         }
672
673         public override void visit_interface (Interface iface) {
674                 if (iface.external_package) {
675                         return;
676                 }
677
678                 if (!check_accessibility (iface)) {
679                         return;
680                 }
681
682                 if (!has_namespace (iface)) {
683                         return;
684                 }
685
686                 if (!(hierarchy[0] is Namespace)) {
687                         deferred.add (iface);
688                         return;
689                 }
690
691                 string gtype_struct_name = get_gir_name (iface) + "Iface";
692
693                 write_indent ();
694                 buffer.append_printf ("<interface name=\"%s\"", get_gir_name (iface));
695                 write_gtype_attributes (iface, true);
696                 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
697                 write_symbol_attributes (iface);
698                 buffer.append_printf (">\n");
699                 indent++;
700
701                 write_doc (get_interface_comment (iface));
702
703                 // write prerequisites
704                 if (iface.get_prerequisites ().size > 0) {
705                         foreach (DataType base_type in iface.get_prerequisites ()) {
706                                 write_indent ();
707                                 buffer.append_printf ("<prerequisite name=\"%s\"/>\n", gi_type_name (((ObjectType) base_type).type_symbol));
708                         }
709                 }
710
711                 hierarchy.insert (0, iface);
712                 iface.accept_children (this);
713                 hierarchy.remove_at (0);
714
715                 indent--;
716                 write_indent ();
717                 buffer.append_printf ("</interface>\n");
718
719                 write_indent ();
720                 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
721                 write_ctype_attributes (iface, "Iface");
722                 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", get_gir_name (iface));
723                 buffer.append_printf (">\n");
724                 indent++;
725
726                 write_indent ();
727                 buffer.append_printf ("<field name=\"parent_iface\" readable=\"0\" private=\"1\">\n");
728                 indent++;
729                 write_indent ();
730                 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (gtypeinterface_type), get_ccode_name (gtypeinterface_type));
731                 indent--;
732                 write_indent ();
733                 buffer.append_printf ("</field>\n");
734
735                 foreach (Method m in iface.get_methods ()) {
736                         if (m.is_abstract || m.is_virtual) {
737                                 if (m.coroutine) {
738                                         string finish_name = m.name;
739                                         if (finish_name.has_suffix ("_async")) {
740                                                 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
741                                         }
742                                         finish_name += "_finish";
743
744                                         write_indent ();
745                                         buffer.append_printf("<field name=\"%s\"", m.name);
746                                         write_symbol_attributes (m);
747                                         buffer.append_printf (">\n");
748                                         indent++;
749                                         do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false, false);
750                                         indent--;
751                                         write_indent ();
752                                         buffer.append_printf ("</field>\n");
753
754                                         write_indent ();
755                                         buffer.append_printf("<field name=\"%s\"", finish_name);
756                                         write_symbol_attributes (m);
757                                         buffer.append_printf (">\n");
758                                         indent++;
759                                         do_write_signature (m, "callback", true, finish_name, get_ccode_finish_name (m), m.get_async_end_parameters (), m.return_type, m.tree_can_fail, false, false);
760                                         indent--;
761                                         write_indent ();
762                                         buffer.append_printf ("</field>\n");
763                                 } else {
764                                         write_indent ();
765                                         buffer.append_printf("<field name=\"%s\"", m.name);
766                                         write_symbol_attributes (m);
767                                         buffer.append_printf (">\n");
768                                         indent++;
769                                         do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false, false);
770                                         indent--;
771                                         write_indent ();
772                                         buffer.append_printf ("</field>\n");
773                                 }
774                         }
775                 }
776
777                 foreach (var prop in iface.get_properties ()) {
778                         if (prop.is_abstract || prop.is_virtual) {
779                                 if (prop.get_accessor != null) {
780                                         var m = prop.get_accessor.get_method ();
781                                         write_indent ();
782                                         buffer.append_printf("<field name=\"%s\"", m.name);
783                                         write_symbol_attributes (m);
784                                         buffer.append_printf (">\n");
785                                         indent++;
786                                         do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false, false);
787                                         indent--;
788                                         write_indent ();
789                                         buffer.append_printf ("</field>\n");
790                                 }
791
792                                 if (prop.set_accessor != null && prop.set_accessor.writable) {
793                                         var m = prop.set_accessor.get_method ();
794                                         write_indent ();
795                                         buffer.append_printf("<field name=\"%s\"", m.name);
796                                         write_symbol_attributes (m);
797                                         buffer.append_printf (">\n");
798                                         indent++;
799                                         do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false, false);
800                                         indent--;
801                                         write_indent ();
802                                         buffer.append_printf ("</field>\n");
803                                 }
804                         }
805                 }
806
807                 indent--;
808                 write_indent ();
809                 buffer.append_printf ("</record>\n");
810
811                 visit_deferred ();
812         }
813
814         private void visit_deferred () {
815                 var nodes = this.deferred;
816                 this.deferred = new Vala.ArrayList<Vala.CodeNode>();
817
818                 foreach (var node in nodes) {
819                         node.accept (this);
820                 }
821         }
822
823         private string? get_gir_name (Symbol symbol) {
824                 string? gir_name = null;
825                 var h0 = hierarchy[0];
826
827                 for (Symbol? cur_sym = symbol ; cur_sym != null ; cur_sym = cur_sym.parent_symbol) {
828                         if (cur_sym == h0) {
829                                 break;
830                         }
831
832                         var cur_name = cur_sym.get_attribute_string ("GIR", "name");
833                         if (cur_name == null) {
834                                 cur_name = cur_sym.name;
835                         }
836                         gir_name = cur_name.concat (gir_name);
837                 }
838
839                 return gir_name;
840         }
841
842         public override void visit_enum (Enum en) {
843                 if (en.external_package) {
844                         return;
845                 }
846
847                 if (!check_accessibility (en)) {
848                         return;
849                 }
850
851                 if (!has_namespace (en)) {
852                         return;
853                 }
854
855                 if (!(hierarchy[0] is Namespace)) {
856                         deferred.add (en);
857                         return;
858                 }
859
860                 string element_name = (en.is_flags) ? "bitfield" : "enumeration";
861
862                 write_indent ();
863                 buffer.append_printf ("<%s name=\"%s\"", element_name, get_gir_name (en));
864                 if (get_ccode_has_type_id (en)) {
865                         write_gtype_attributes (en);
866                 } else {
867                         write_ctype_attributes (en);
868                 }
869                 write_symbol_attributes (en);
870                 buffer.append_printf (">\n");
871                 indent++;
872
873                 write_doc (get_enum_comment (en));
874
875                 enum_value = 0;
876                 hierarchy.insert (0, en);
877                 en.accept_children (this);
878                 hierarchy.remove_at (0);
879
880                 indent--;
881                 write_indent ();
882                 buffer.append_printf ("</%s>\n", element_name);
883
884                 visit_deferred ();
885         }
886
887         private int enum_value;
888
889         public override void visit_enum_value (EnumValue ev) {
890                 write_indent ();
891                 var en = (Enum) hierarchy[0];
892                 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev.name.ascii_down (), get_ccode_name (ev));
893                 if (ev.value != null) {
894                         string value = literal_expression_to_value_string (ev.value);
895                         buffer.append_printf (" value=\"%s\"", value);
896                 } else {
897                         if (en.is_flags) {
898                                 buffer.append_printf (" value=\"%d\"", 1 << enum_value++);
899                         } else {
900                                 buffer.append_printf (" value=\"%d\"", enum_value++);
901                         }
902                 }
903                 write_symbol_attributes (ev);
904
905                 string? comment = get_enum_value_comment (ev);
906                 if (comment == null) {
907                         buffer.append_printf ("/>\n");
908                 } else {
909                         buffer.append_printf (">\n");
910                         indent++;
911
912                         write_doc (comment);
913
914                         indent--;
915                         write_indent ();
916                         buffer.append_printf ("</member>\n");
917                 }
918         }
919
920         public override void visit_error_domain (ErrorDomain edomain) {
921                 if (edomain.external_package) {
922                         return;
923                 }
924
925                 if (!check_accessibility (edomain)) {
926                         return;
927                 }
928
929                 if (!has_namespace (edomain)) {
930                         return;
931                 }
932
933                 write_indent ();
934                 buffer.append_printf ("<enumeration name=\"%s\"", get_gir_name (edomain));
935                 write_ctype_attributes (edomain);
936                 buffer.append_printf (" glib:error-domain=\"%s\"", get_ccode_quark_name (edomain));
937                 write_symbol_attributes (edomain);
938                 buffer.append_printf (">\n");
939                 indent++;
940
941                 write_doc (get_error_domain_comment (edomain));
942
943                 enum_value = 0;
944                 hierarchy.insert (0, edomain);
945                 edomain.accept_children (this);
946                 hierarchy.remove_at (0);
947
948                 indent--;
949                 write_indent ();
950                 buffer.append_printf ("</enumeration>\n");
951
952                 visit_deferred ();
953         }
954
955         public override void visit_error_code (ErrorCode ecode) {
956                 write_indent ();
957                 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode.name.ascii_down (), get_ccode_name (ecode));
958                 if (ecode.value != null) {
959                         string value = literal_expression_to_value_string (ecode.value);
960                         buffer.append_printf (" value=\"%s\"", value);
961                 } else {
962                         buffer.append_printf (" value=\"%d\"", enum_value++);
963                 }
964                 write_symbol_attributes (ecode);
965
966                 string? comment = get_error_code_comment (ecode);
967                 if (comment == null) {
968                         buffer.append_printf ("/>\n");
969                 } else {
970                         buffer.append_printf (">\n");
971                         indent++;
972
973                         write_doc (comment);
974
975                         indent--;
976                         write_indent ();
977                         buffer.append_printf ("</member>\n");
978                 }
979         }
980
981         public override void visit_constant (Constant c) {
982                 if (c.external_package) {
983                         return;
984                 }
985
986                 if (!check_accessibility (c)) {
987                         return;
988                 }
989
990                 if (!has_namespace (c)) {
991                         return;
992                 }
993
994                 //TODO Add better constant evaluation
995                 var initializer = c.value;
996                 string value = literal_expression_to_value_string (initializer);
997
998                 write_indent ();
999                 buffer.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", get_gir_name (c), get_ccode_name (c));
1000                 buffer.append_printf (" value=\"%s\"", value);
1001                 write_symbol_attributes (c);
1002                 buffer.append_printf (">\n");
1003                 indent++;
1004
1005                 write_doc (get_constant_comment (c));
1006
1007                 write_type (initializer.value_type);
1008
1009                 indent--;
1010                 write_indent ();
1011                 buffer.append_printf ("</constant>\n");
1012         }
1013
1014         public override void visit_field (Field f) {
1015                 if (f.external_package) {
1016                         return;
1017                 }
1018
1019                 if (!check_accessibility (f)) {
1020                         return;
1021                 }
1022
1023                 if (!has_namespace (f)) {
1024                         return;
1025                 }
1026
1027                 write_indent ();
1028                 buffer.append_printf ("<field name=\"%s\" writable=\"1\"", get_ccode_name (f));
1029                 if (f.variable_type.nullable) {
1030                         buffer.append_printf (" nullable=\"1\"");
1031                 }
1032                 write_symbol_attributes (f);
1033                 buffer.append_printf (">\n");
1034                 indent++;
1035
1036                 write_doc (get_field_comment (f));
1037
1038                 write_type (f.variable_type);
1039
1040                 indent--;
1041                 write_indent ();
1042                 buffer.append_printf ("</field>\n");
1043
1044                 if (f.variable_type is ArrayType && get_ccode_array_length (f)) {
1045                         var array_type = (ArrayType) f.variable_type;
1046                         if (!array_type.fixed_length) {
1047                                 for (var i = 0; i < array_type.rank; i++) {
1048                                         write_indent ();
1049                                         buffer.append_printf ("<field name=\"%s_length%i\"", get_ccode_name (f), i + 1);
1050                                         write_symbol_attributes (f);
1051                                         buffer.append_printf (">\n");
1052                                         indent++;
1053                                         write_type (array_type.length_type);
1054                                         indent--;
1055                                         write_indent ();
1056                                         buffer.append_printf ("</field>\n");
1057                                 }
1058                         }
1059                 } else if (f.variable_type is DelegateType) {
1060                         var deleg_type = (DelegateType) f.variable_type;
1061                         if (deleg_type.delegate_symbol.has_target) {
1062                                 write_indent ();
1063                                 buffer.append_printf ("<field name=\"%s\"", get_ccode_delegate_target_name (f));
1064                                 write_symbol_attributes (f);
1065                                 buffer.append_printf (">\n");
1066                                 indent++;
1067                                 write_indent ();
1068                                 buffer.append_printf ("<type name=\"gpointer\" c:type=\"gpointer\"/>\n");
1069                                 indent--;
1070                                 write_indent ();
1071                                 buffer.append_printf ("</field>\n");
1072                                 if (deleg_type.is_disposable ()) {
1073                                         write_indent ();
1074                                         buffer.append_printf ("<field name=\"%s\"", get_ccode_delegate_target_destroy_notify_name (f));
1075                                         write_symbol_attributes (f);
1076                                         buffer.append_printf (">\n");
1077                                         indent++;
1078                                         write_indent ();
1079                                         buffer.append_printf ("<type name=\"GLib.DestroyNotify\" c:type=\"GDestroyNotify\"/>\n");
1080                                         indent--;
1081                                         write_indent ();
1082                                         buffer.append_printf ("</field>\n");
1083                                 }
1084                         }
1085                 }
1086         }
1087
1088         private void write_implicit_params (DataType? type, ref int index, bool has_array_length, string? name, ParameterDirection direction) {
1089                 if (type is ArrayType && has_array_length) {
1090                         for (var i = 0; i < ((ArrayType) type).rank; i++) {
1091                                 write_param_or_return (((ArrayType) type).length_type, "parameter", ref index, has_array_length, "%s_length%i".printf (name, i + 1), null, direction);
1092                         }
1093                 } else if (type is DelegateType) {
1094                         var deleg_type = (DelegateType) type;
1095                         if (deleg_type.delegate_symbol.has_target) {
1096                                 var data_type = new PointerType (new VoidType ());
1097                                 write_param_or_return (data_type, "parameter", ref index, false, "%s_target".printf (name), null, direction);
1098                                 if (deleg_type.is_disposable ()) {
1099                                         var notify_type = new DelegateType (context.root.scope.lookup ("GLib").scope.lookup ("DestroyNotify") as Delegate);
1100                                         write_param_or_return (notify_type, "parameter", ref index, false, "%s_target_destroy_notify".printf (name), null, direction);
1101                                 }
1102                         }
1103                 }
1104         }
1105
1106         void skip_implicit_params (DataType? type, ref int index, bool has_array_length) {
1107                 if (type is ArrayType && has_array_length) {
1108                         index += ((ArrayType) type).rank;
1109                 } else if (type is DelegateType) {
1110                         index++;
1111                         var deleg_type = (DelegateType) type;
1112                         if (deleg_type.is_disposable ()) {
1113                                 index++;
1114                         }
1115                 }
1116         }
1117
1118         private void write_type_parameter (TypeParameter type_parameter, string tag_type) {
1119                 write_indent ();
1120                 if (tag_type == "property") {
1121                         buffer.append_printf ("<%s name=\"%s\" writable=\"1\" construct-only=\"1\">\n", tag_type, get_ccode_type_id (type_parameter).replace ("_", "-"));
1122                 } else {
1123                         buffer.append_printf ("<%s name=\"%s\" transfer-ownership=\"none\">\n", tag_type, get_ccode_type_id (type_parameter));
1124                 }
1125                 indent++;
1126                 write_indent ();
1127                 buffer.append_printf ("<type name=\"GType\" c:type=\"GType\"/>\n");
1128                 indent--;
1129                 write_indent ();
1130                 buffer.append_printf ("</%s>\n", tag_type);
1131                 write_indent ();
1132                 if (tag_type == "property") {
1133                         buffer.append_printf ("<%s name=\"%s\" writable=\"1\" construct-only=\"1\">\n", tag_type, get_ccode_copy_function (type_parameter).replace ("_", "-"));
1134                 } else {
1135                         buffer.append_printf ("<%s name=\"%s\" transfer-ownership=\"none\">\n", tag_type, get_ccode_copy_function (type_parameter));
1136                 }
1137                 indent++;
1138                 write_indent ();
1139                 buffer.append_printf ("<type name=\"GObject.BoxedCopyFunc\" c:type=\"GBoxedCopyFunc\"/>\n");
1140                 indent--;
1141                 write_indent ();
1142                 buffer.append_printf ("</%s>\n", tag_type);
1143                 write_indent ();
1144                 if (tag_type == "property") {
1145                         buffer.append_printf ("<%s name=\"%s\" writable=\"1\" construct-only=\"1\">\n", tag_type, get_ccode_destroy_function (type_parameter).replace ("_", "-"));
1146                 } else {
1147                         buffer.append_printf ("<%s name=\"%s\" transfer-ownership=\"none\">\n", tag_type, get_ccode_destroy_function (type_parameter));
1148                 }
1149                 indent++;
1150                 write_indent ();
1151                 buffer.append_printf ("<type name=\"GLib.DestroyNotify\" c:type=\"GDestroyNotify\"/>\n");
1152                 indent--;
1153                 write_indent ();
1154                 buffer.append_printf ("</%s>\n", tag_type);
1155         }
1156
1157         private void write_params_and_return (string tag_name, List<Parameter> params, List<TypeParameter>? type_params, DataType? return_type, bool return_array_length, string? return_comment = null, bool constructor = false, Parameter? instance_param = null, bool user_data = false) {
1158                 int last_index = 0;
1159                 bool ret_is_struct = return_type != null && return_type.is_real_non_null_struct_type ();
1160
1161                 if (params.size != 0 || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
1162                         int index = 0;
1163
1164                         foreach (Parameter param in params) {
1165                                 index++;
1166
1167                                 skip_implicit_params (param.variable_type, ref index, get_ccode_array_length (param));
1168                         }
1169
1170                         if (ret_is_struct) {
1171                                 index++;
1172                         } else {
1173                                 skip_implicit_params (return_type, ref index, return_array_length);
1174                                 if (return_type is ArrayType && return_array_length) {
1175                                         index -= ((ArrayType) return_type).rank - 1;
1176                                 }
1177                         }
1178
1179                         last_index = index - 1;
1180                 }
1181
1182                 if (return_type != null && !ret_is_struct) {
1183                         write_param_or_return (return_type, "return-value", ref last_index, return_array_length, null, return_comment, ParameterDirection.IN, constructor);
1184                 } else if (ret_is_struct) {
1185                         write_param_or_return (new VoidType (), "return-value", ref last_index, false, null, return_comment, ParameterDirection.IN);
1186                 }
1187
1188                 if (params.size != 0 || (type_params != null && type_params.size > 0) || instance_param != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
1189                         write_indent ();
1190                         buffer.append_printf ("<parameters>\n");
1191                         indent++;
1192                         int index = 0;
1193
1194                         if (instance_param != null) {
1195                                 var type = instance_param.variable_type.copy ();
1196                                 unowned Struct? st = type.type_symbol as Struct;
1197                                 if (st != null && !st.is_simple_type ()) {
1198                                         type.nullable = true;
1199                                 }
1200                                 int skip = 0;
1201                                 if (tag_name == "callback") {
1202                                         write_param_or_return (type, "parameter", ref skip, false, "self");
1203                                         index++;
1204                                 } else {
1205                                         write_param_or_return (type, "instance-parameter", ref skip, false, "self");
1206                                 }
1207                         }
1208
1209                         if (constructor && ret_is_struct) {
1210                                 // struct constructor has a caller-allocates / out-parameter as instance
1211                                 write_param_or_return (return_type, "instance-parameter", ref index, false, "self", return_comment, ParameterDirection.OUT, constructor, true);
1212                         }
1213
1214                         if (type_params != null) {
1215                                 foreach (var p in type_params) {
1216                                         write_type_parameter (p, "parameter");
1217                                         index += 3;
1218                                 }
1219                         }
1220
1221                         foreach (Parameter param in params) {
1222                                 write_param_or_return (param.variable_type, "parameter", ref index, get_ccode_array_length (param), get_ccode_name (param), get_parameter_comment (param), param.direction, false, false, param.ellipsis || param.params_array);
1223
1224                                 write_implicit_params (param.variable_type, ref index, get_ccode_array_length (param), get_ccode_name (param), param.direction);
1225                         }
1226
1227                         if (!constructor && ret_is_struct) {
1228                                 // struct returns are converted to parameters
1229                                 write_param_or_return (return_type, "parameter", ref index, false, "result", return_comment, ParameterDirection.OUT, constructor, true);
1230                         } else if (!constructor) {
1231                                 write_implicit_params (return_type, ref index, return_array_length, "result", ParameterDirection.OUT);
1232                         }
1233
1234                         if (user_data) {
1235                                 write_indent ();
1236                                 buffer.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index);
1237                                 indent++;
1238                                 write_indent ();
1239                                 buffer.append_printf ("<type name=\"gpointer\" c:type=\"void*\"/>\n");
1240                                 indent--;
1241                                 write_indent ();
1242                                 buffer.append_printf ("</parameter>\n");
1243                         }
1244
1245                         indent--;
1246                         write_indent ();
1247                         buffer.append_printf ("</parameters>\n");
1248                 }
1249         }
1250
1251         public override void visit_delegate (Delegate cb) {
1252                 if (cb.external_package) {
1253                         return;
1254                 }
1255
1256                 if (!check_accessibility (cb)) {
1257                         return;
1258                 }
1259
1260                 if (!has_namespace (cb)) {
1261                         return;
1262                 }
1263
1264                 write_indent ();
1265                 buffer.append_printf ("<callback name=\"%s\"", get_gir_name (cb));
1266                 buffer.append_printf (" c:type=\"%s\"", get_ccode_name (cb));
1267                 if (cb.tree_can_fail) {
1268                         buffer.append_printf (" throws=\"1\"");
1269                 }
1270                 write_symbol_attributes (cb);
1271                 buffer.append_printf (">\n");
1272                 indent++;
1273
1274                 write_doc (get_delegate_comment (cb));
1275
1276                 write_params_and_return ("callback", cb.get_parameters (), cb.get_type_parameters (), cb.return_type, get_ccode_array_length (cb), get_delegate_return_comment (cb), false, null, cb.has_target);
1277
1278                 indent--;
1279                 write_indent ();
1280                 buffer.append_printf ("</callback>\n");
1281         }
1282
1283         public override void visit_method (Method m) {
1284                 if (m.external_package) {
1285                         return;
1286                 }
1287
1288                 // don't write interface implementation unless it's an abstract or virtual method
1289                 if (!check_accessibility (m) || m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
1290                         return;
1291                 }
1292
1293                 if (!has_namespace (m)) {
1294                         return;
1295                 }
1296
1297                 string tag_name = "method";
1298                 var parent = this.hierarchy.get (0);
1299                 if (parent is Enum) {
1300                         deferred.add (m);
1301                         return;
1302                 }
1303
1304                 if (parent is Namespace || m.binding == MemberBinding.STATIC || parent != m.parent_symbol) {
1305                         tag_name = "function";
1306                 }
1307
1308                 if (!get_ccode_no_wrapper (m) && m.signal_reference == null) {
1309                         write_signature (m, tag_name, true);
1310                 }
1311
1312                 if (m.is_abstract || m.is_virtual) {
1313                         write_signature (m, "virtual-method", true, false);
1314                 }
1315         }
1316
1317         bool is_type_introspectable (DataType type) {
1318                 // gobject-introspection does not currently support va_list parameters
1319                 if (get_ccode_name (type) == "va_list") {
1320                         return false;
1321                 }
1322
1323                 return true;
1324         }
1325
1326         bool is_method_introspectable (Method m) {
1327                 if (!is_type_introspectable (m.return_type)) {
1328                         return false;
1329                 }
1330                 foreach (var param in m.get_parameters ()) {
1331                         if (param.ellipsis || param.params_array || !is_type_introspectable (param.variable_type)) {
1332                                 return false;
1333                         }
1334                 }
1335                 return true;
1336         }
1337
1338         bool is_introspectable (Symbol sym) {
1339                 if (sym is Method && !is_method_introspectable ((Method) sym)) {
1340                         return false;
1341                 }
1342
1343                 return is_visibility (sym);
1344         }
1345
1346         private void write_signature (Method m, string tag_name, bool write_doc, bool instance = false, bool write_attributes = true) {
1347                 var parent = this.hierarchy.get (0);
1348                 string name;
1349                 if (m.parent_symbol != parent) {
1350                         instance = false;
1351                         name = get_ccode_name (m);
1352                         var parent_prefix = get_ccode_lower_case_prefix (parent);
1353                         if (name.has_prefix (parent_prefix)) {
1354                                 name = name.substring (parent_prefix.length);
1355                         }
1356                 } else {
1357                         name = m.name;
1358                 }
1359
1360                 if (m.coroutine) {
1361                         string finish_name = name;
1362                         if (finish_name.has_suffix ("_async")) {
1363                                 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
1364                         }
1365                         finish_name += "_finish";
1366                         do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, true, write_attributes);
1367                         do_write_signature (m, tag_name, instance, finish_name, get_ccode_finish_name (m), m.get_async_end_parameters (), m.return_type, m.tree_can_fail, false, write_attributes);
1368                 } else {
1369                         do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, true, write_attributes);
1370                 }
1371         }
1372
1373         private void do_write_signature (Method m, string tag_name, bool instance, string name, string cname, List<Vala.Parameter> params, DataType return_type, bool can_fail, bool write_comment, bool write_attributes = true) {
1374                 write_indent ();
1375                 buffer.append_printf ("<%s name=\"%s\"", tag_name, name);
1376                 if (tag_name == "virtual-method") {
1377                         if (!get_ccode_no_wrapper (m)) {
1378                                 buffer.append_printf (" invoker=\"%s\"", name);
1379                         }
1380                 } else if (tag_name == "callback") {
1381                         /* this is only used for vfuncs */
1382                         buffer.append_printf (" c:type=\"%s\"", name);
1383                 } else {
1384                         buffer.append_printf (" c:identifier=\"%s\"", cname);
1385                 }
1386                 if (can_fail) {
1387                         buffer.append_printf (" throws=\"1\"");
1388                 }
1389                 if (write_attributes) {
1390                         write_symbol_attributes (m);
1391                 }
1392                 buffer.append_printf (">\n");
1393                 indent++;
1394
1395                 string? return_comment = null;
1396                 if (write_comment) {
1397                         return_comment = get_method_return_comment (m);
1398                         write_doc (get_method_comment (m));
1399                 }
1400
1401                 write_params_and_return (tag_name, params, m.get_type_parameters (), return_type, get_ccode_array_length (m), return_comment, false, m.this_parameter);
1402
1403                 indent--;
1404                 write_indent ();
1405                 buffer.append_printf ("</%s>\n", tag_name);
1406         }
1407
1408         public override void visit_creation_method (CreationMethod m) {
1409                 if (m.external_package) {
1410                         return;
1411                 }
1412
1413                 if (!check_accessibility (m)) {
1414                         return;
1415                 }
1416
1417                 if (m.parent_symbol is Class && ((Class) m.parent_symbol).is_abstract) {
1418                         return;
1419                 }
1420
1421                 write_indent ();
1422
1423                 bool is_struct = m.parent_symbol is Struct;
1424                 // GI doesn't like constructors that return void type
1425                 string tag_name = is_struct ? "method" : "constructor";
1426
1427                 if (m.parent_symbol is Class && m == ((Class)m.parent_symbol).default_construction_method ||
1428                         m.parent_symbol is Struct && m == ((Struct)m.parent_symbol).default_construction_method) {
1429                         string m_name = is_struct ? "init" : "new";
1430                         buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m_name, get_ccode_name (m));
1431                 } else if (is_struct) {
1432                         buffer.append_printf ("<%s name=\"init_%s\" c:identifier=\"%s\"", tag_name, m.name, get_ccode_name (m));
1433                 } else {
1434                         buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m.name, get_ccode_name (m));
1435                 }
1436
1437                 if (m.tree_can_fail) {
1438                         buffer.append_printf (" throws=\"1\"");
1439                 }
1440                 write_symbol_attributes (m);
1441                 buffer.append_printf (">\n");
1442                 indent++;
1443
1444                 write_doc (get_method_comment (m));
1445
1446                 var datatype = SemanticAnalyzer.get_data_type_for_symbol (m.parent_symbol);
1447                 List<TypeParameter>? type_params = null;
1448                 if (m.parent_symbol is Class) {
1449                         type_params = ((Class) m.parent_symbol).get_type_parameters ();
1450                 }
1451                 write_params_and_return (tag_name, m.get_parameters (), type_params, datatype, false, get_method_return_comment (m), true);
1452
1453                 indent--;
1454                 write_indent ();
1455                 buffer.append_printf ("</%s>\n", tag_name);
1456         }
1457
1458         public override void visit_property (Property prop) {
1459                 if (!check_accessibility (prop) || prop.overrides || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
1460                         return;
1461                 }
1462
1463                 if (context.analyzer.is_gobject_property (prop)) {
1464                         write_indent ();
1465                         buffer.append_printf ("<property name=\"%s\"", get_ccode_name (prop));
1466                         if (prop.get_accessor == null) {
1467                                 buffer.append_printf (" readable=\"0\"");
1468                         }
1469                         if (prop.set_accessor != null) {
1470                                 buffer.append_printf (" writable=\"1\"");
1471                                 if (prop.set_accessor.construction) {
1472                                         if (!prop.set_accessor.writable) {
1473                                                 buffer.append_printf (" construct-only=\"1\"");
1474                                         } else {
1475                                                 buffer.append_printf (" construct=\"1\"");
1476                                         }
1477                                 }
1478                         }
1479                         write_symbol_attributes (prop);
1480                         buffer.append_printf (">\n");
1481                         indent++;
1482
1483                         write_doc (get_property_comment (prop));
1484
1485                         write_type (prop.property_type);
1486
1487                         indent--;
1488                         write_indent ();
1489                         buffer.append_printf ("</property>\n");
1490                 }
1491
1492                 if (prop.get_accessor != null) {
1493                         var m = prop.get_accessor.get_method ();
1494                         if (m != null) {
1495                                 visit_method (m);
1496                         }
1497                 }
1498
1499                 if (prop.set_accessor != null) {
1500                         var m = prop.set_accessor.get_method ();
1501                         if (m != null) {
1502                                 visit_method (m);
1503                         }
1504                 }
1505         }
1506
1507         public override void visit_signal (Signal sig) {
1508                 if (!check_accessibility (sig)) {
1509                         return;
1510                 }
1511
1512                 if (sig.emitter != null) {
1513                         sig.emitter.accept (this);
1514                 }
1515
1516                 if (sig.default_handler != null) {
1517                         sig.default_handler.accept (this);
1518                 }
1519
1520                 write_indent ();
1521                 buffer.append_printf ("<glib:signal name=\"%s\"", get_ccode_name (sig));
1522                 write_symbol_attributes (sig);
1523                 buffer.append_printf (">\n");
1524                 indent++;
1525
1526                 write_doc (get_signal_comment (sig));
1527
1528                 write_params_and_return ("glib:signal", sig.get_parameters (), null, sig.return_type, false, get_signal_return_comment (sig));
1529
1530                 indent--;
1531                 write_indent ();
1532                 buffer.append_printf ("</glib:signal>\n");
1533         }
1534
1535         private void write_indent () {
1536                 int i;
1537
1538                 for (i = 0; i < indent; i++) {
1539                         buffer.append_c ('\t');
1540                 }
1541         }
1542
1543         private void write_indent_stream () {
1544                 int i;
1545
1546                 for (i = 0; i < indent; i++) {
1547                         stream.putc ('\t');
1548                 }
1549         }
1550
1551
1552         private void write_param_or_return (DataType? type, string tag, ref int index, bool has_array_length, string? name = null, string? comment = null, ParameterDirection direction = ParameterDirection.IN, bool constructor = false, bool caller_allocates = false, bool ellipsis = false) {
1553                 write_indent ();
1554                 buffer.append_printf ("<%s", tag);
1555                 if (ellipsis) {
1556                         name = "...";
1557                 }
1558                 if (name != null) {
1559                         buffer.append_printf (" name=\"%s\"", name);
1560                 }
1561                 if (direction == ParameterDirection.REF) {
1562                         buffer.append_printf (" direction=\"inout\"");
1563                 } else if (direction == ParameterDirection.OUT) {
1564                         buffer.append_printf (" direction=\"out\"");
1565                 }
1566
1567                 unowned DelegateType? delegate_type = type as DelegateType;
1568                 unowned ArrayType? array_type = type as ArrayType;
1569
1570                 if (type != null && ((type.value_owned && delegate_type == null) || (constructor
1571                     && !(type.type_symbol is Struct || type.type_symbol.is_subtype_of (ginitiallyunowned_type))))) {
1572                         var any_owned = false;
1573                         foreach (var generic_arg in type.get_type_arguments ()) {
1574                                 any_owned |= generic_arg.value_owned;
1575                         }
1576                         if (type.has_type_arguments () && !any_owned) {
1577                                 buffer.append_printf (" transfer-ownership=\"container\"");
1578                         } else if (array_type != null && !array_type.element_type.value_owned) {
1579                                 buffer.append_printf (" transfer-ownership=\"container\"");
1580                         } else {
1581                                 buffer.append_printf (" transfer-ownership=\"full\"");
1582                         }
1583                 } else {
1584                         buffer.append_printf (" transfer-ownership=\"none\"");
1585                 }
1586                 if (caller_allocates) {
1587                         buffer.append_printf (" caller-allocates=\"1\"");
1588                 }
1589                 if (type != null && type.nullable) {
1590                         if (tag == "parameter"
1591                             && (direction == ParameterDirection.OUT || direction == ParameterDirection.REF)) {
1592                                 buffer.append_printf (" optional=\"1\"");
1593                         } else {
1594                                 buffer.append_printf (" nullable=\"1\"");
1595                         }
1596                 }
1597
1598                 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
1599                         int closure_index = tag == "parameter" ?
1600                                 index + 1 : (type.value_owned ? index - 1 : index);
1601                         buffer.append_printf (" closure=\"%i\"", closure_index);
1602                         if (delegate_type.is_called_once) {
1603                                 buffer.append (" scope=\"async\"");
1604                         } else if (type.value_owned) {
1605                                 buffer.append_printf (" scope=\"notified\" destroy=\"%i\"", closure_index + 1);
1606                         } else {
1607                                 buffer.append (" scope=\"call\"");
1608                         }
1609                 } else if (delegate_type != null) {
1610                         buffer.append (" scope=\"call\"");
1611                 }
1612
1613                 buffer.append_printf (">\n");
1614                 indent++;
1615
1616                 write_doc (comment);
1617
1618                 if (ellipsis) {
1619                         write_indent ();
1620                         buffer.append ("<varargs/>\n");
1621                 } else if (type != null) {
1622                         int length_param_index = -1;
1623                         if (has_array_length) {
1624                                 length_param_index = tag == "parameter" ? index + 1 : index;
1625                         }
1626                         write_type (type, length_param_index, direction);
1627                 }
1628
1629                 indent--;
1630                 write_indent ();
1631                 buffer.append_printf ("</%s>\n", tag);
1632                 index++;
1633         }
1634
1635         private void write_ctype_attributes (TypeSymbol symbol, string suffix = "", bool symbol_prefix = false) {
1636                 buffer.append_printf (" c:type=\"%s%s\"", get_ccode_name (symbol), suffix);
1637                 if (symbol_prefix) {
1638                         buffer.append_printf (" c:symbol-prefix=\"%s\"", get_ccode_lower_case_suffix (symbol));
1639                 }
1640         }
1641
1642         private void write_gtype_attributes (TypeSymbol symbol, bool symbol_prefix = false) {
1643                 write_ctype_attributes(symbol, "", symbol_prefix);
1644                 buffer.append_printf (" glib:type-name=\"%s\"", get_ccode_name (symbol));
1645                 buffer.append_printf (" glib:get-type=\"%sget_type\"", get_ccode_lower_case_prefix (symbol));
1646         }
1647
1648         private void write_type (DataType type, int index = -1, ParameterDirection direction = ParameterDirection.IN) {
1649                 if (type is ArrayType) {
1650                         var array_type = (ArrayType) type;
1651
1652                         write_indent ();
1653                         buffer.append_printf ("<array");
1654                         if (array_type.fixed_length && array_type.length is IntegerLiteral) {
1655                                 var lit = (IntegerLiteral) array_type.length;
1656                                 buffer.append_printf (" fixed-size=\"%i\"", int.parse (lit.value));
1657                         } else if (index != -1) {
1658                                 buffer.append_printf (" length=\"%i\"", index);
1659                         }
1660                         buffer.append_printf (" c:type=\"%s%s\"", get_ccode_name (array_type.element_type), direction == ParameterDirection.IN ? "*" : "**");
1661                         buffer.append_printf (">\n");
1662                         indent++;
1663
1664                         write_type (array_type.element_type);
1665
1666                         indent--;
1667                         write_indent ();
1668                         buffer.append_printf ("</array>\n");
1669                 } else if (type is VoidType) {
1670                         write_indent ();
1671                         buffer.append_printf ("<type name=\"none\" c:type=\"void\"/>\n");
1672                 } else if (type is PointerType) {
1673                         write_indent ();
1674                         buffer.append_printf ("<type name=\"gpointer\" c:type=\"%s%s\"/>\n", get_ccode_name (type), direction == ParameterDirection.IN ? "" : "*");
1675                 } else if (type is GenericType) {
1676                         // generic type parameters not supported in GIR
1677                         write_indent ();
1678                         buffer.append ("<type name=\"gpointer\" c:type=\"gpointer\"/>\n");
1679                 } else if (type is DelegateType) {
1680                         var deleg_type = (DelegateType) type;
1681                         write_indent ();
1682                         buffer.append_printf ("<type name=\"%s\" c:type=\"%s%s\"/>\n", gi_type_name (deleg_type.delegate_symbol), get_ccode_name (type), direction == ParameterDirection.IN ? "" : "*");
1683                 } else if (type.type_symbol != null) {
1684                         write_indent ();
1685                         string type_name = gi_type_name (type.type_symbol);
1686                         bool is_array = false;
1687                         if ((type_name == "GLib.Array") || (type_name == "GLib.PtrArray")) {
1688                                 is_array = true;
1689                         }
1690                         buffer.append_printf ("<%s name=\"%s\" c:type=\"%s%s\"", is_array ? "array" : "type", gi_type_name (type.type_symbol), get_ccode_name (type), direction == ParameterDirection.IN ? "" : "*");
1691
1692                         List<DataType> type_arguments = type.get_type_arguments ();
1693                         if (type_arguments.size == 0) {
1694                                 buffer.append_printf ("/>\n");
1695                         } else {
1696                                 buffer.append_printf (">\n");
1697                                 indent++;
1698
1699                                 foreach (DataType type_argument in type_arguments) {
1700                                         write_type (type_argument);
1701                                 }
1702
1703                                 indent--;
1704                                 write_indent ();
1705                                 buffer.append_printf ("</%s>\n", is_array ? "array" : "type");
1706                         }
1707                 } else {
1708                         write_indent ();
1709                         buffer.append_printf ("<type name=\"%s\"/>\n", type.to_string ());
1710                 }
1711         }
1712
1713         private string? get_full_gir_name (Symbol sym) {
1714                 string? gir_fullname = sym.get_attribute_string ("GIR", "fullname");
1715                 if (gir_fullname != null) {
1716                         return gir_fullname;
1717                 }
1718
1719                 string? gir_name = sym.get_attribute_string ("GIR", "name");
1720
1721                 if (gir_name == null && sym is Namespace) {
1722                         gir_name = sym.get_attribute_string ("CCode", "gir_namespace");
1723                 }
1724                 if (gir_name == null) {
1725                         gir_name = sym.name;
1726                 }
1727
1728                 if (sym.parent_symbol == null) {
1729                         return gir_name;
1730                 }
1731
1732                 if (sym.name == null) {
1733                         return get_full_gir_name (sym.parent_symbol);
1734                 }
1735
1736                 string parent_gir_name = get_full_gir_name (sym.parent_symbol);
1737                 if (parent_gir_name == null) {
1738                         return gir_name;
1739                 }
1740
1741                 string self_gir_name = gir_name.has_prefix (".") ? gir_name.substring (1) : gir_name;
1742                 if ("." in parent_gir_name) {
1743                         return "%s%s".printf (parent_gir_name, self_gir_name);
1744                 } else {
1745                         return "%s.%s".printf (parent_gir_name, self_gir_name);
1746                 }
1747         }
1748
1749         private string gi_type_name (TypeSymbol type_symbol) {
1750                 Symbol parent = type_symbol.parent_symbol;
1751                 if (parent is Namespace) {
1752                         Namespace ns = parent as Namespace;
1753                         var ns_gir_name = ns.get_attribute_string ("GIR", "name") ?? ns.name;
1754                         if (ns_gir_name != null) {
1755                                 unowned SourceFile source_file = type_symbol.source_reference.file;
1756                                 if (source_file.gir_namespace != null) {
1757                                         GIRNamespace external;
1758                                         if (source_file.gir_ambiguous) {
1759                                                 external = GIRNamespace.for_symbol (type_symbol);
1760                                         } else {
1761                                                 external = GIRNamespace (source_file.gir_namespace, source_file.gir_version);
1762                                         }
1763                                         if (!externals.contains (external)) {
1764                                                 externals.add (external);
1765                                         }
1766                                         string? gir_fullname = type_symbol.get_attribute_string ("GIR", "fullname");
1767                                         if (gir_fullname != null) {
1768                                                 return gir_fullname;
1769                                         }
1770                                         var type_name = type_symbol.get_attribute_string ("GIR", "name") ?? type_symbol.name;
1771                                         return "%s.%s".printf (external.ns, type_name);
1772                                 } else {
1773                                         unannotated_namespaces.add(ns);
1774                                 }
1775                         }
1776                 }
1777
1778                 return get_full_gir_name (type_symbol);
1779         }
1780
1781         private string? literal_expression_to_value_string (Expression literal) {
1782                 if (literal is StringLiteral) {
1783                         var lit = literal as StringLiteral;
1784                         if (lit != null) {
1785                                 return Markup.escape_text (lit.eval ());
1786                         }
1787                 } else if (literal is CharacterLiteral) {
1788                         return "%c".printf ((char) ((CharacterLiteral) literal).get_char ());
1789                 } else if (literal is BooleanLiteral) {
1790                         return ((BooleanLiteral) literal).value ? "true" : "false";
1791                 } else if (literal is RealLiteral) {
1792                         return ((RealLiteral) literal).value;
1793                 } else if (literal is IntegerLiteral) {
1794                         return ((IntegerLiteral) literal).value;
1795                 } else if (literal is UnaryExpression) {
1796                         var unary = (UnaryExpression) literal;
1797                         if (unary.operator == UnaryOperator.MINUS) {
1798                                 if (unary.inner is RealLiteral) {
1799                                         return "-" + ((RealLiteral) unary.inner).value;
1800                                 } else if (unary.inner is IntegerLiteral) {
1801                                         return "-" + ((IntegerLiteral) unary.inner).value;
1802                                 }
1803                         }
1804                 }
1805                 return null;
1806         }
1807
1808         private bool check_accessibility (Symbol sym) {
1809                 if (sym.access == SymbolAccessibility.PUBLIC ||
1810                     sym.access == SymbolAccessibility.PROTECTED) {
1811                         return true;
1812                 }
1813
1814                 // internal fields and function pointers in classes/interfaces are public API
1815                 if (sym.access == SymbolAccessibility.INTERNAL) {
1816                         unowned Symbol? parent = sym.parent_symbol;
1817                         if (parent != null
1818                             && (parent is Class || parent is Interface)
1819                             && ((sym is Field && ((Field) sym).binding == MemberBinding.INSTANCE)
1820                             || (sym is Method && ((Method) sym).binding == MemberBinding.INSTANCE && (((Method) sym).is_abstract || ((Method) sym).is_virtual)))) {
1821                                 return true;
1822                         }
1823                 }
1824
1825                 return false;
1826         }
1827
1828         private bool is_visibility (Symbol sym) {
1829                 return sym.get_attribute_bool ("GIR", "visible", true);
1830         }
1831
1832         bool has_namespace (Symbol sym) {
1833                 if (!(sym.parent_symbol is Namespace) || sym.parent_symbol.name != null) {
1834                         return true;
1835                 }
1836
1837                 Report.warning (sym.source_reference, "`%s' must be part of namespace to be included in GIR", sym.name);
1838                 return false;
1839         }
1840 }