Release 0.41.92
[vala-gnome.git] / codegen / valagirwriter.vala
blob7c2ebc7f0cecd5559ef76aba01b3328097578451
1 /* valagirwriter.vala
3 * Copyright (C) 2008-2012 Jürg Billeter
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.
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.
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
19 * Author:
20 * Jürg Billeter <j@bitron.ch>
23 using GLib;
25 /**
26 * Code visitor generating .gir file for the public interface.
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;
35 protected virtual string? get_interface_comment (Interface iface) {
36 return null;
39 protected virtual string? get_struct_comment (Struct st) {
40 return null;
43 protected virtual string? get_enum_comment (Enum en) {
44 return null;
47 protected virtual string? get_class_comment (Class c) {
48 return null;
51 protected virtual string? get_error_code_comment (ErrorCode ecode) {
52 return null;
55 protected virtual string? get_enum_value_comment (EnumValue ev) {
56 return null;
59 protected virtual string? get_constant_comment (Constant c) {
60 return null;
63 protected virtual string? get_error_domain_comment (ErrorDomain edomain) {
64 return null;
67 protected virtual string? get_field_comment (Field f) {
68 return null;
71 protected virtual string? get_delegate_comment (Delegate cb) {
72 return null;
75 protected virtual string? get_method_comment (Method m) {
76 return null;
79 protected virtual string? get_property_comment (Property prop) {
80 return null;
83 protected virtual string? get_delegate_return_comment (Delegate cb) {
84 return null;
87 protected virtual string? get_signal_return_comment (Signal sig) {
88 return null;
91 protected virtual string? get_method_return_comment (Method m) {
92 return null;
95 protected virtual string? get_signal_comment (Signal sig) {
96 return null;
99 protected virtual string? get_parameter_comment (Parameter param) {
100 return null;
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>();
110 int indent;
112 private TypeSymbol gobject_type;
113 private TypeSymbol ginitiallyunowned_type;
115 private struct GIRNamespace {
116 public GIRNamespace (string ns, string version) {
117 this.ns = ns; this.version = version;
119 public string ns;
120 public string version;
121 public bool equal (GIRNamespace g) {
122 return ((ns == g.ns) && (version == g.version));
126 private ArrayList<GIRNamespace?> externals = new ArrayList<GIRNamespace?> ((EqualFunc<GIRNamespace>) GIRNamespace.equal);
128 public void write_includes() {
129 foreach (var i in externals) {
130 if (i.ns != this.gir_namespace) {
131 write_indent_stream ();
132 stream.printf ("<include name=\"%s\" version=\"%s\"/>\n", i.ns, i.version);
139 * Writes the public interface of the specified code context into the
140 * specified file.
142 * @param context a code context
143 * @param gir_filename a relative or absolute filename
145 public void write_file (CodeContext context, string directory, string gir_filename, string gir_namespace, string gir_version, string package, string? gir_shared_library = null) {
146 this.context = context;
147 this.directory = directory;
148 this.gir_namespace = gir_namespace;
149 this.gir_version = gir_version;
150 this.gir_shared_library = gir_shared_library;
152 var root_symbol = context.root;
153 var glib_ns = root_symbol.scope.lookup ("GLib");
154 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
155 ginitiallyunowned_type = (TypeSymbol) glib_ns.scope.lookup ("InitiallyUnowned");
157 write_package (package);
159 context.accept (this);
161 indent--;
162 buffer.append_printf ("</repository>\n");
164 string filename = "%s%c%s".printf (directory, Path.DIR_SEPARATOR, gir_filename);
165 stream = FileStream.open (filename, "w");
166 if (stream == null) {
167 Report.error (null, "unable to open `%s' for writing".printf (filename));
168 this.context = null;
169 return;
172 stream.printf ("<?xml version=\"1.0\"?>\n");
174 stream.printf ("<repository version=\"1.2\"");
175 stream.printf (" xmlns=\"http://www.gtk.org/introspection/core/1.0\"");
176 stream.printf (" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"");
177 stream.printf (" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
178 stream.printf (">\n");
179 indent++;
181 write_includes();
182 indent--;
184 stream.puts (buffer.str);
185 stream = null;
187 foreach (var ns in unannotated_namespaces) {
188 if (!our_namespaces.contains(ns)) {
189 Report.warning (ns.source_reference, "Namespace %s does not have a GIR namespace and version annotation".printf (ns.name));
192 foreach (var ns in our_namespaces) {
193 ns.source_reference.file.gir_namespace = gir_namespace;
194 ns.source_reference.file.gir_version = gir_version;
197 if (our_namespaces.size == 0) {
198 Report.error (null, "No suitable namespace found to export for GIR");
201 this.context = null;
204 private void write_doc (string? comment) {
205 if (comment != null) {
206 write_indent ();
207 buffer.append ("<doc xml:whitespace=\"preserve\">");
208 buffer.append (comment);
209 buffer.append ("</doc>\n");
213 private void write_package (string package) {
214 write_indent ();
215 buffer.append_printf ("<package name=\"%s\"/>\n", package);
218 private void write_c_includes (Namespace ns) {
219 // Collect C header filenames
220 Set<string> header_filenames = new HashSet<string> (str_hash, str_equal);
221 foreach (unowned string c_header_filename in get_ccode_header_filenames (ns).split (",")) {
222 header_filenames.add (c_header_filename);
224 foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) {
225 foreach (unowned string c_header_filename in get_ccode_header_filenames (symbol).split (",")) {
226 header_filenames.add (c_header_filename);
230 // Generate c:include tags
231 foreach (string c_header_filename in header_filenames) {
232 write_c_include (c_header_filename);
236 private void write_c_include (string name) {
237 write_indent ();
238 buffer.append_printf ("<c:include name=\"%s\"/>\n", name);
241 public override void visit_namespace (Namespace ns) {
242 if (ns.external_package) {
243 return;
246 if (!is_visibility (ns)) {
247 return;
250 if (ns.name == null) {
251 // global namespace
252 hierarchy.insert (0, ns);
253 ns.accept_children (this);
254 hierarchy.remove_at (0);
255 return;
258 if (ns.parent_symbol.name != null) {
259 ns.accept_children (this);
260 return;
263 write_c_includes (ns);
265 write_indent ();
266 buffer.append_printf ("<namespace name=\"%s\" version=\"%s\"", gir_namespace, gir_version);
267 string? cprefix = get_ccode_prefix (ns);
268 if (gir_shared_library != null) {
269 buffer.append_printf(" shared-library=\"%s\"", gir_shared_library);
271 if (cprefix != null) {
272 buffer.append_printf (" c:prefix=\"%s\"", cprefix);
274 buffer.append_printf (">\n");
275 indent++;
277 hierarchy.insert (0, ns);
278 ns.accept_children (this);
279 hierarchy.remove_at (0);
281 indent--;
282 write_indent ();
283 buffer.append_printf ("</namespace>\n");
284 our_namespaces.add(ns);
286 visit_deferred ();
289 private void write_symbol_attributes (Symbol symbol) {
290 if (!is_introspectable (symbol)) {
291 buffer.append_printf (" introspectable=\"0\"");
293 if (symbol.version.deprecated) {
294 buffer.append_printf (" deprecated=\"1\"");
295 if (symbol.version.deprecated_since != null) {
296 buffer.append_printf (" deprecated-version=\"%s\"", symbol.version.deprecated_since);
299 if (symbol.version.since != null) {
300 buffer.append_printf (" version=\"%s\"", symbol.version.since);
304 public override void visit_class (Class cl) {
305 if (cl.external_package) {
306 return;
309 if (!check_accessibility (cl)) {
310 return;
313 if (!(hierarchy[0] is Namespace)) {
314 deferred.add (cl);
315 return;
318 if (cl.is_subtype_of (gobject_type)) {
319 string gtype_struct_name = get_gir_name (cl) + "Class";
321 write_indent ();
322 buffer.append_printf ("<class name=\"%s\"", get_gir_name (cl));
323 write_gtype_attributes (cl);
324 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
325 buffer.append_printf (" parent=\"%s\"", gi_type_name (cl.base_class));
326 if (cl.is_abstract) {
327 buffer.append_printf (" abstract=\"1\"");
329 write_symbol_attributes (cl);
330 buffer.append_printf (">\n");
331 indent++;
333 write_doc (get_class_comment (cl));
335 // write implemented interfaces
336 foreach (DataType base_type in cl.get_base_types ()) {
337 var object_type = (ObjectType) base_type;
338 if (object_type.type_symbol is Interface) {
339 write_indent ();
340 buffer.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
344 write_indent ();
345 buffer.append_printf ("<field name=\"parent_instance\">\n");
346 indent++;
347 write_indent ();
348 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
349 indent--;
350 write_indent ();
351 buffer.append_printf("</field>\n");
353 write_indent ();
354 buffer.append_printf ("<field name=\"priv\">\n");
355 indent++;
356 write_indent ();
357 buffer.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", get_gir_name (cl), get_ccode_name (cl));
358 indent--;
359 write_indent ();
360 buffer.append_printf("</field>\n");
362 hierarchy.insert (0, cl);
363 cl.accept_children (this);
364 hierarchy.remove_at (0);
366 indent--;
367 write_indent ();
368 buffer.append_printf ("</class>\n");
370 write_indent ();
371 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
372 write_ctype_attributes (cl, "Class");
373 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", cl.name);
374 buffer.append_printf (">\n");
375 indent++;
377 write_indent ();
378 buffer.append_printf ("<field name=\"parent_class\">\n");
379 indent++;
380 write_indent ();
381 buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
382 indent--;
383 write_indent ();
384 buffer.append_printf ("</field>\n");
386 foreach (Method m in cl.get_methods ()) {
387 if (m.is_abstract || m.is_virtual) {
388 if (m.coroutine) {
389 string finish_name = m.name;
390 if (finish_name.has_suffix ("_async")) {
391 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
393 finish_name += "_finish";
395 write_indent ();
396 buffer.append_printf("<field name=\"%s\">\n", m.name);
397 indent++;
398 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false);
399 indent--;
400 write_indent ();
401 buffer.append_printf ("</field>\n");
403 write_indent ();
404 buffer.append_printf("<field name=\"%s\">\n", finish_name);
405 indent++;
406 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);
407 indent--;
408 write_indent ();
409 buffer.append_printf ("</field>\n");
410 } else {
411 write_indent ();
412 buffer.append_printf("<field name=\"%s\">\n", m.name);
413 indent++;
414 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
415 indent--;
416 write_indent ();
417 buffer.append_printf ("</field>\n");
422 foreach (Signal sig in cl.get_signals ()) {
423 if (sig.default_handler != null) {
424 write_indent ();
425 buffer.append_printf ("<field name=\"%s\">\n", get_ccode_lower_case_name (sig));
426 indent++;
427 write_signature (sig.default_handler, "callback", false, true);
428 indent--;
429 write_indent ();
430 buffer.append_printf ("</field>\n");
434 indent--;
435 write_indent ();
436 buffer.append_printf ("</record>\n");
438 write_indent ();
439 buffer.append_printf ("<record name=\"%sPrivate\" c:type=\"%sPrivate\" disguised=\"1\"/>\n", get_gir_name (cl), get_ccode_name (cl));
440 } else {
441 write_indent ();
442 buffer.append_printf ("<record name=\"%s\"", get_gir_name (cl));
443 write_symbol_attributes (cl);
444 buffer.append_printf (">\n");
445 indent++;
447 write_doc (get_class_comment (cl));
449 hierarchy.insert (0, cl);
450 cl.accept_children (this);
451 hierarchy.remove_at (0);
453 indent--;
454 write_indent ();
455 buffer.append_printf ("</record>\n");
458 visit_deferred ();
461 public override void visit_struct (Struct st) {
462 if (st.external_package) {
463 return;
466 if (!check_accessibility (st)) {
467 return;
470 if (!(hierarchy[0] is Namespace)) {
471 deferred.add (st);
472 return;
475 write_indent ();
476 buffer.append_printf ("<record name=\"%s\"", get_gir_name (st));
477 write_symbol_attributes (st);
478 buffer.append_printf (">\n");
479 indent++;
481 write_doc (get_struct_comment (st));
483 hierarchy.insert (0, st);
484 st.accept_children (this);
485 hierarchy.remove_at (0);
487 indent--;
488 write_indent ();
489 buffer.append_printf ("</record>\n");
491 visit_deferred ();
494 public override void visit_interface (Interface iface) {
495 if (iface.external_package) {
496 return;
499 if (!check_accessibility (iface)) {
500 return;
503 if (!(hierarchy[0] is Namespace)) {
504 deferred.add (iface);
505 return;
508 string gtype_struct_name = iface.name + "Iface";
510 write_indent ();
511 buffer.append_printf ("<interface name=\"%s\"", get_gir_name (iface));
512 write_gtype_attributes (iface);
513 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
514 write_symbol_attributes (iface);
515 buffer.append_printf (">\n");
516 indent++;
518 write_doc (get_interface_comment (iface));
520 // write prerequisites
521 if (iface.get_prerequisites ().size > 0) {
522 foreach (DataType base_type in iface.get_prerequisites ()) {
523 write_indent ();
524 buffer.append_printf ("<prerequisite name=\"%s\"/>\n", gi_type_name (((ObjectType) base_type).type_symbol));
528 hierarchy.insert (0, iface);
529 iface.accept_children (this);
530 hierarchy.remove_at (0);
532 indent--;
533 write_indent ();
534 buffer.append_printf ("</interface>\n");
536 write_indent ();
537 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
538 write_ctype_attributes (iface, "Iface");
539 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", iface.name);
540 buffer.append_printf (">\n");
541 indent++;
543 write_indent ();
544 buffer.append_printf ("<field name=\"parent_iface\">\n");
545 indent++;
546 write_indent ();
547 buffer.append_printf ("<type name=\"GObject.TypeInterface\" c:type=\"GTypeInterface\"/>\n");
548 indent--;
549 write_indent ();
550 buffer.append_printf ("</field>\n");
552 foreach (Method m in iface.get_methods ()) {
553 if (m.is_abstract || m.is_virtual) {
554 if (m.coroutine) {
555 string finish_name = m.name;
556 if (finish_name.has_suffix ("_async")) {
557 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
559 finish_name += "_finish";
561 write_indent ();
562 buffer.append_printf("<field name=\"%s\">\n", m.name);
563 indent++;
564 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false);
565 indent--;
566 write_indent ();
567 buffer.append_printf ("</field>\n");
569 write_indent ();
570 buffer.append_printf("<field name=\"%s\">\n", finish_name);
571 indent++;
572 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);
573 indent--;
574 write_indent ();
575 buffer.append_printf ("</field>\n");
576 } else {
577 write_indent ();
578 buffer.append_printf("<field name=\"%s\">\n", m.name);
579 indent++;
580 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
581 indent--;
582 write_indent ();
583 buffer.append_printf ("</field>\n");
588 foreach (var prop in iface.get_properties ()) {
589 if (prop.is_abstract || prop.is_virtual) {
590 if (prop.get_accessor != null) {
591 var m = prop.get_accessor.get_method ();
592 write_indent ();
593 buffer.append_printf("<field name=\"%s\">\n", m.name);
594 indent++;
595 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
596 indent--;
597 write_indent ();
598 buffer.append_printf ("</field>\n");
601 if (prop.set_accessor != null && prop.set_accessor.writable) {
602 var m = prop.set_accessor.get_method ();
603 write_indent ();
604 buffer.append_printf("<field name=\"%s\">\n", m.name);
605 indent++;
606 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
607 indent--;
608 write_indent ();
609 buffer.append_printf ("</field>\n");
614 indent--;
615 write_indent ();
616 buffer.append_printf ("</record>\n");
618 visit_deferred ();
621 private void visit_deferred () {
622 var nodes = this.deferred;
623 this.deferred = new Vala.ArrayList<Vala.CodeNode>();
625 foreach (var node in nodes) {
626 node.accept (this);
630 private string? get_gir_name (Symbol symbol) {
631 string? gir_name = null;
632 var h0 = hierarchy[0];
634 for (Symbol? cur_sym = symbol ; cur_sym != null ; cur_sym = cur_sym.parent_symbol) {
635 if (cur_sym == h0) {
636 break;
639 var cur_name = cur_sym.get_attribute_string ("GIR", "name");
640 if (cur_name == null) {
641 cur_name = cur_sym.name;
643 gir_name = cur_name.concat (gir_name);
646 return gir_name;
649 public override void visit_enum (Enum en) {
650 if (en.external_package) {
651 return;
654 if (!check_accessibility (en)) {
655 return;
658 if (!(hierarchy[0] is Namespace)) {
659 deferred.add (en);
660 return;
663 string element_name = (en.is_flags) ? "bitfield" : "enumeration";
665 write_indent ();
666 buffer.append_printf ("<%s name=\"%s\"", element_name, get_gir_name (en));
667 write_gtype_attributes (en);
668 write_symbol_attributes (en);
669 buffer.append_printf (">\n");
670 indent++;
672 write_doc (get_enum_comment (en));
674 enum_value = 0;
675 hierarchy.insert (0, en);
676 en.accept_children (this);
677 hierarchy.remove_at (0);
679 indent--;
680 write_indent ();
681 buffer.append_printf ("</%s>\n", element_name);
683 visit_deferred ();
686 private int enum_value;
688 public override void visit_enum_value (EnumValue ev) {
689 write_indent ();
690 var en = (Enum) hierarchy[0];
691 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev.name.down (), get_ccode_name (ev));
692 if (ev.value != null) {
693 string value = literal_expression_to_value_string (ev.value);
694 buffer.append_printf (" value=\"%s\"", value);
695 } else {
696 if (en.is_flags) {
697 buffer.append_printf (" value=\"%d\"", 1 << enum_value++);
698 } else {
699 buffer.append_printf (" value=\"%d\"", enum_value++);
702 write_symbol_attributes (ev);
704 string? comment = get_enum_value_comment (ev);
705 if (comment == null) {
706 buffer.append_printf ("/>\n");
707 } else {
708 buffer.append_printf (">\n");
709 indent++;
711 write_doc (comment);
713 indent--;
714 write_indent ();
715 buffer.append_printf ("</member>\n");
719 public override void visit_error_domain (ErrorDomain edomain) {
720 if (edomain.external_package) {
721 return;
724 if (!check_accessibility (edomain)) {
725 return;
728 write_indent ();
729 buffer.append_printf ("<enumeration name=\"%s\"", edomain.name);
730 write_ctype_attributes (edomain);
731 buffer.append_printf (" glib:error-domain=\"%s\"", get_ccode_quark_name (edomain));
732 write_symbol_attributes (edomain);
733 buffer.append_printf (">\n");
734 indent++;
736 write_doc (get_error_domain_comment (edomain));
738 enum_value = 0;
739 hierarchy.insert (0, edomain);
740 edomain.accept_children (this);
741 hierarchy.remove_at (0);
743 indent--;
744 write_indent ();
745 buffer.append_printf ("</enumeration>\n");
747 visit_deferred ();
750 public override void visit_error_code (ErrorCode ecode) {
751 write_indent ();
752 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode.name.down (), get_ccode_name (ecode));
753 if (ecode.value != null) {
754 string value = literal_expression_to_value_string (ecode.value);
755 buffer.append_printf (" value=\"%s\"", value);
756 } else {
757 buffer.append_printf (" value=\"%d\"", enum_value++);
759 write_symbol_attributes (ecode);
761 string? comment = get_error_code_comment (ecode);
762 if (comment == null) {
763 buffer.append_printf ("/>\n");
764 } else {
765 buffer.append_printf (">\n");
766 indent++;
768 write_doc (comment);
770 indent--;
771 write_indent ();
772 buffer.append_printf ("</member>\n");
776 public override void visit_constant (Constant c) {
777 if (c.external_package) {
778 return;
781 if (!check_accessibility (c)) {
782 return;
785 //TODO Add better constant evaluation
786 var initializer = c.value;
787 string value = literal_expression_to_value_string (initializer);
789 write_indent ();
790 buffer.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", c.name, get_ccode_name (c));
791 buffer.append_printf (" value=\"%s\"", value);
792 write_symbol_attributes (c);
793 buffer.append_printf (">\n");
794 indent++;
796 write_doc (get_constant_comment (c));
798 write_type (initializer.value_type);
800 indent--;
801 write_indent ();
802 buffer.append_printf ("</constant>\n");
805 public override void visit_field (Field f) {
806 if (f.external_package) {
807 return;
810 if (!check_accessibility (f)) {
811 return;
814 write_indent ();
815 buffer.append_printf ("<field name=\"%s\"", get_ccode_name (f));
816 if (f.variable_type.nullable) {
817 buffer.append_printf (" allow-none=\"1\"");
819 write_symbol_attributes (f);
820 buffer.append_printf (">\n");
821 indent++;
823 write_doc (get_field_comment (f));
825 write_type (f.variable_type);
827 indent--;
828 write_indent ();
829 buffer.append_printf ("</field>\n");
832 private void write_implicit_params (DataType? type, ref int index, bool has_array_length, string? name, ParameterDirection direction) {
833 if (type is ArrayType && has_array_length) {
834 var int_type = new IntegerType (context.root.scope.lookup ("int") as Struct);
835 for (var i = 0; i < ((ArrayType) type).rank; i++) {
836 write_param_or_return (int_type, true, ref index, has_array_length, "%s_length%i".printf (name, i + 1), null, direction);
838 } else if (type is DelegateType) {
839 var deleg_type = (DelegateType) type;
840 if (deleg_type.delegate_symbol.has_target) {
841 var data_type = new PointerType (new VoidType ());
842 write_param_or_return (data_type, true, ref index, false, "%s_target".printf (name), null, direction);
843 if (deleg_type.is_disposable ()) {
844 var notify_type = new DelegateType (context.root.scope.lookup ("GLib").scope.lookup ("DestroyNotify") as Delegate);
845 write_param_or_return (notify_type, true, ref index, false, "%s_target_destroy_notify".printf (name), null, direction);
851 void skip_implicit_params (DataType? type, ref int index, bool has_array_length) {
852 if (type is ArrayType && has_array_length) {
853 index += ((ArrayType) type).rank;
854 } else if (type is DelegateType) {
855 index++;
856 var deleg_type = (DelegateType) type;
857 if (deleg_type.is_disposable ()) {
858 index++;
863 private void write_params_and_return (List<Parameter> params, DataType? return_type, bool return_array_length, string? return_comment = null, bool constructor = false, DataType? instance_type = null, bool user_data = false) {
864 int last_index = 0;
865 bool ret_is_struct = return_type != null && return_type.is_real_non_null_struct_type ();
867 if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
868 int index = 0;
870 if (instance_type != null) {
871 index++;
874 foreach (Parameter param in params) {
875 index++;
877 skip_implicit_params (param.variable_type, ref index, get_ccode_array_length (param));
880 if (ret_is_struct) {
881 index++;
882 } else {
883 skip_implicit_params (return_type, ref index, return_array_length);
884 if (return_type is ArrayType && return_array_length) {
885 index -= ((ArrayType) return_type).rank - 1;
889 last_index = index - 1;
892 if (return_type != null && !ret_is_struct) {
893 write_param_or_return (return_type, false, ref last_index, return_array_length, null, return_comment, ParameterDirection.IN, constructor);
894 } else if (ret_is_struct) {
895 write_param_or_return (new VoidType (), false, ref last_index, false, null, return_comment, ParameterDirection.IN);
898 if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
899 write_indent ();
900 buffer.append_printf ("<parameters>\n");
901 indent++;
902 int index = 0;
904 if (instance_type != null) {
905 write_param_or_return (instance_type, true, ref index, false, "self");
908 foreach (Parameter param in params) {
909 write_param_or_return (param.variable_type, true, ref index, get_ccode_array_length (param), param.name, get_parameter_comment (param), param.direction, false, false, param.ellipsis);
911 write_implicit_params (param.variable_type, ref index, get_ccode_array_length (param), param.name, param.direction);
914 if (ret_is_struct) {
915 // struct returns are converted to parameters
916 write_param_or_return (return_type, true, ref index, false, "result", return_comment, ParameterDirection.OUT, constructor, true);
917 } else {
918 write_implicit_params (return_type, ref index, return_array_length, "result", ParameterDirection.OUT);
921 if (user_data) {
922 write_indent ();
923 buffer.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index);
924 indent++;
925 write_indent ();
926 buffer.append_printf ("<type name=\"gpointer\" c:type=\"void*\"/>\n");
927 indent--;
928 write_indent ();
929 buffer.append_printf ("</parameter>\n");
932 indent--;
933 write_indent ();
934 buffer.append_printf ("</parameters>\n");
938 public override void visit_delegate (Delegate cb) {
939 if (cb.external_package) {
940 return;
943 if (!check_accessibility (cb)) {
944 return;
947 write_indent ();
948 buffer.append_printf ("<callback name=\"%s\"", cb.name);
949 buffer.append_printf (" c:type=\"%s\"", get_ccode_name (cb));
950 if (cb.tree_can_fail) {
951 buffer.append_printf (" throws=\"1\"");
953 write_symbol_attributes (cb);
954 buffer.append_printf (">\n");
955 indent++;
957 write_doc (get_delegate_comment (cb));
959 write_params_and_return (cb.get_parameters (), cb.return_type, get_ccode_array_length (cb), get_delegate_return_comment (cb), false, null, cb.has_target);
961 indent--;
962 write_indent ();
963 buffer.append_printf ("</callback>\n");
966 public override void visit_method (Method m) {
967 if (m.external_package) {
968 return;
971 // don't write interface implementation unless it's an abstract or virtual method
972 if (!check_accessibility (m) || m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
973 return;
976 string tag_name = "method";
977 var parent = this.hierarchy.get (0);
978 if (parent is Enum) {
979 deferred.add (m);
980 return;
983 if (parent is Namespace || m.binding == MemberBinding.STATIC || parent != m.parent_symbol) {
984 tag_name = "function";
987 write_signature (m, tag_name, true);
989 if (m.is_abstract || m.is_virtual) {
990 write_signature (m, "virtual-method", true, false);
994 bool is_type_introspectable (DataType type) {
995 // gobject-introspection does not currently support va_list parameters
996 if (get_ccode_name (type) == "va_list") {
997 return false;
1000 return true;
1003 bool is_method_introspectable (Method m) {
1004 if (!is_type_introspectable (m.return_type)) {
1005 return false;
1007 foreach (var param in m.get_parameters ()) {
1008 if (param.ellipsis || !is_type_introspectable (param.variable_type)) {
1009 return false;
1012 return true;
1015 bool is_introspectable (Symbol sym) {
1016 if (sym is Method && !is_method_introspectable ((Method) sym)) {
1017 return false;
1020 return is_visibility (sym);
1023 private void write_signature (Method m, string tag_name, bool write_doc, bool instance = false) {
1024 var parent = this.hierarchy.get (0);
1025 string name;
1026 if (m.parent_symbol != parent) {
1027 instance = false;
1028 name = get_ccode_name (m);
1029 var parent_prefix = get_ccode_lower_case_prefix (parent);
1030 if (name.has_prefix (parent_prefix)) {
1031 name = name.substring (parent_prefix.length);
1033 } else {
1034 name = m.name;
1037 if (m.coroutine) {
1038 string finish_name = name;
1039 if (finish_name.has_suffix ("_async")) {
1040 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
1042 finish_name += "_finish";
1043 do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, true);
1044 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);
1045 } else {
1046 do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, true);
1050 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) {
1051 write_indent ();
1052 buffer.append_printf ("<%s name=\"%s\"", tag_name, name);
1053 if (tag_name == "virtual-method") {
1054 buffer.append_printf (" invoker=\"%s\"", name);
1055 } else if (tag_name == "callback") {
1056 /* this is only used for vfuncs */
1057 buffer.append_printf (" c:type=\"%s\"", name);
1058 } else {
1059 buffer.append_printf (" c:identifier=\"%s\"", cname);
1061 if (can_fail) {
1062 buffer.append_printf (" throws=\"1\"");
1064 write_symbol_attributes (m);
1065 buffer.append_printf (">\n");
1066 indent++;
1068 string? return_comment = null;
1069 if (write_comment) {
1070 return_comment = get_method_return_comment (m);
1071 write_doc (get_method_comment (m));
1074 DataType instance_type = null;
1075 if (instance) {
1076 instance_type = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
1079 write_params_and_return (params, return_type, get_ccode_array_length (m), return_comment, false, instance_type);
1081 indent--;
1082 write_indent ();
1083 buffer.append_printf ("</%s>\n", tag_name);
1086 public override void visit_creation_method (CreationMethod m) {
1087 if (m.external_package) {
1088 return;
1091 if (!check_accessibility (m)) {
1092 return;
1095 if (m.parent_symbol is Class && ((Class) m.parent_symbol).is_abstract) {
1096 return;
1099 write_indent ();
1101 bool is_struct = m.parent_symbol is Struct;
1102 // GI doesn't like constructors that return void type
1103 string tag_name = is_struct ? "function" : "constructor";
1105 if (m.parent_symbol is Class && m == ((Class)m.parent_symbol).default_construction_method ||
1106 m.parent_symbol is Struct && m == ((Struct)m.parent_symbol).default_construction_method) {
1107 string m_name = is_struct ? "init" : "new";
1108 buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m_name, get_ccode_name (m));
1109 } else {
1110 buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m.name, get_ccode_name (m));
1113 if (m.tree_can_fail) {
1114 buffer.append_printf (" throws=\"1\"");
1116 write_symbol_attributes (m);
1117 buffer.append_printf (">\n");
1118 indent++;
1120 write_doc (get_method_comment (m));
1122 var datatype = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
1123 write_params_and_return (m.get_parameters (), datatype, false, get_method_return_comment (m), true);
1125 indent--;
1126 write_indent ();
1127 buffer.append_printf ("</%s>\n", tag_name);
1130 public override void visit_property (Property prop) {
1131 if (!check_accessibility (prop) || prop.overrides || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
1132 return;
1135 write_indent ();
1136 buffer.append_printf ("<property name=\"%s\"", prop.name.replace ("_", "-"));
1137 if (prop.get_accessor == null) {
1138 buffer.append_printf (" readable=\"0\"");
1140 if (prop.set_accessor != null) {
1141 buffer.append_printf (" writable=\"1\"");
1142 if (prop.set_accessor.construction) {
1143 if (!prop.set_accessor.writable) {
1144 buffer.append_printf (" construct-only=\"1\"");
1145 } else {
1146 buffer.append_printf (" construct=\"1\"");
1150 write_symbol_attributes (prop);
1151 buffer.append_printf (">\n");
1152 indent++;
1154 write_doc (get_property_comment (prop));
1156 write_type (prop.property_type);
1158 indent--;
1159 write_indent ();
1160 buffer.append_printf ("</property>\n");
1162 if (prop.get_accessor != null) {
1163 var m = prop.get_accessor.get_method ();
1164 if (m != null) {
1165 visit_method (m);
1169 if (prop.set_accessor != null) {
1170 var m = prop.set_accessor.get_method ();
1171 if (m != null) {
1172 visit_method (m);
1177 public override void visit_signal (Signal sig) {
1178 if (!check_accessibility (sig)) {
1179 return;
1182 if (sig.emitter != null) {
1183 sig.emitter.accept (this);
1186 write_indent ();
1187 buffer.append_printf ("<glib:signal name=\"%s\"", get_ccode_name (sig));
1188 write_symbol_attributes (sig);
1189 buffer.append_printf (">\n");
1190 indent++;
1192 write_doc (get_signal_comment (sig));
1194 write_params_and_return (sig.get_parameters (), sig.return_type, false, get_signal_return_comment (sig));
1196 indent--;
1197 write_indent ();
1198 buffer.append_printf ("</glib:signal>\n");
1201 private void write_indent () {
1202 int i;
1204 for (i = 0; i < indent; i++) {
1205 buffer.append_c ('\t');
1209 private void write_indent_stream () {
1210 int i;
1212 for (i = 0; i < indent; i++) {
1213 stream.putc ('\t');
1218 private void write_param_or_return (DataType? type, bool is_parameter, 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) {
1219 write_indent ();
1220 string tag = is_parameter ? "parameter" : "return-value";
1221 buffer.append_printf ("<%s", tag);
1222 if (ellipsis) {
1223 name = "...";
1225 if (name != null) {
1226 buffer.append_printf (" name=\"%s\"", name);
1228 if (direction == ParameterDirection.REF) {
1229 buffer.append_printf (" direction=\"inout\"");
1230 } else if (direction == ParameterDirection.OUT) {
1231 buffer.append_printf (" direction=\"out\"");
1234 DelegateType delegate_type = type as DelegateType;
1236 if (type != null && ((type.value_owned && delegate_type == null) || (constructor && !type.data_type.is_subtype_of (ginitiallyunowned_type)))) {
1237 var any_owned = false;
1238 foreach (var generic_arg in type.get_type_arguments ()) {
1239 any_owned |= generic_arg.value_owned;
1241 if (type.has_type_arguments () && !any_owned) {
1242 buffer.append_printf (" transfer-ownership=\"container\"");
1243 } else {
1244 buffer.append_printf (" transfer-ownership=\"full\"");
1246 } else {
1247 buffer.append_printf (" transfer-ownership=\"none\"");
1249 if (caller_allocates) {
1250 buffer.append_printf (" caller-allocates=\"1\"");
1252 if (type != null && type.nullable) {
1253 buffer.append_printf (" allow-none=\"1\"");
1256 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
1257 int closure_index = is_parameter ?
1258 index + 1 : (type.value_owned ? index - 1 : index);
1259 buffer.append_printf (" closure=\"%i\"", closure_index);
1260 if (delegate_type.is_called_once) {
1261 buffer.append (" scope=\"async\"");
1262 } else if (type.value_owned) {
1263 buffer.append_printf (" scope=\"notified\" destroy=\"%i\"", closure_index + 1);
1264 } else {
1265 buffer.append (" scope=\"call\"");
1267 } else if (delegate_type != null) {
1268 buffer.append (" scope=\"call\"");
1271 buffer.append_printf (">\n");
1272 indent++;
1274 write_doc (comment);
1276 if (ellipsis) {
1277 write_indent ();
1278 buffer.append ("<varargs/>\n");
1279 } else if (type != null) {
1280 int length_param_index = -1;
1281 if (has_array_length) {
1282 length_param_index = is_parameter ? index + 1 : index;
1284 write_type (type, length_param_index, direction);
1287 indent--;
1288 write_indent ();
1289 buffer.append_printf ("</%s>\n", tag);
1290 index++;
1293 private void write_ctype_attributes (TypeSymbol symbol, string suffix = "") {
1294 buffer.append_printf (" c:type=\"%s%s\"", get_ccode_name (symbol), suffix);
1297 private void write_gtype_attributes (TypeSymbol symbol) {
1298 write_ctype_attributes(symbol);
1299 buffer.append_printf (" glib:type-name=\"%s\"", get_ccode_name (symbol));
1300 buffer.append_printf (" glib:get-type=\"%sget_type\"", get_ccode_lower_case_prefix (symbol));
1303 private void write_type (DataType type, int index = -1, ParameterDirection direction = ParameterDirection.IN) {
1304 if (type is ArrayType) {
1305 var array_type = (ArrayType) type;
1307 write_indent ();
1308 buffer.append_printf ("<array");
1309 if (array_type.fixed_length && array_type.length is IntegerLiteral) {
1310 var lit = (IntegerLiteral) array_type.length;
1311 buffer.append_printf (" fixed-size=\"%i\"", int.parse (lit.value));
1312 } else if (index != -1) {
1313 buffer.append_printf (" length=\"%i\"", index);
1315 buffer.append_printf (">\n");
1316 indent++;
1318 write_type (array_type.element_type);
1320 indent--;
1321 write_indent ();
1322 buffer.append_printf ("</array>\n");
1323 } else if (type is VoidType) {
1324 write_indent ();
1325 buffer.append_printf ("<type name=\"none\"/>\n");
1326 } else if (type is PointerType) {
1327 write_indent ();
1328 buffer.append_printf ("<type name=\"gpointer\" c:type=\"%s\"/>\n", get_ccode_name (type));
1329 } else if (type.data_type != null) {
1330 write_indent ();
1331 string type_name = gi_type_name (type.data_type);
1332 bool is_array = false;
1333 if ((type_name == "GLib.Array") || (type_name == "GLib.PtrArray")) {
1334 is_array = true;
1336 buffer.append_printf ("<%s name=\"%s\" c:type=\"%s%s\"", is_array ? "array" : "type", gi_type_name (type.data_type), get_ccode_name (type), direction == ParameterDirection.IN ? "" : "*");
1338 List<DataType> type_arguments = type.get_type_arguments ();
1339 if (type_arguments.size == 0) {
1340 buffer.append_printf ("/>\n");
1341 } else {
1342 buffer.append_printf (">\n");
1343 indent++;
1345 foreach (DataType type_argument in type_arguments) {
1346 write_type (type_argument);
1349 indent--;
1350 write_indent ();
1351 buffer.append_printf ("</%s>\n", is_array ? "array" : "type");
1353 } else if (type is DelegateType) {
1354 var deleg_type = (DelegateType) type;
1355 write_indent ();
1356 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (deleg_type.delegate_symbol), get_ccode_name (type));
1357 } else if (type is GenericType) {
1358 // generic type parameters not supported in GIR
1359 write_indent ();
1360 buffer.append ("<type name=\"gpointer\" c:type=\"gpointer\"/>\n");
1361 } else {
1362 write_indent ();
1363 buffer.append_printf ("<type name=\"%s\"/>\n", type.to_string ());
1367 private string? get_full_gir_name (Symbol sym) {
1368 string? gir_fullname = sym.get_attribute_string ("GIR", "fullname");
1369 if (gir_fullname != null) {
1370 return gir_fullname;
1373 string? gir_name = sym.get_attribute_string ("GIR", "name");
1375 if (gir_name == null && sym is Namespace) {
1376 gir_name = sym.get_attribute_string ("CCode", "gir_namespace");
1378 if (gir_name == null) {
1379 gir_name = sym.name;
1382 if (sym.parent_symbol == null) {
1383 return gir_name;
1386 if (sym.name == null) {
1387 return get_full_gir_name (sym.parent_symbol);
1390 string parent_gir_name = get_full_gir_name (sym.parent_symbol);
1391 if (parent_gir_name == null) {
1392 return gir_name;
1395 string self_gir_name = gir_name.has_prefix (".") ? gir_name.substring (1) : gir_name;
1396 if ("." in parent_gir_name) {
1397 return "%s%s".printf (parent_gir_name, self_gir_name);
1398 } else {
1399 return "%s.%s".printf (parent_gir_name, self_gir_name);
1403 private string gi_type_name (TypeSymbol type_symbol) {
1404 Symbol parent = type_symbol.parent_symbol;
1405 if (parent is Namespace) {
1406 Namespace ns = parent as Namespace;
1407 var ns_gir_name = ns.get_attribute_string ("GIR", "name") ?? ns.name;
1408 if (ns_gir_name != null) {
1409 if (type_symbol.source_reference.file.gir_namespace != null) {
1410 GIRNamespace external = GIRNamespace (type_symbol.source_reference.file.gir_namespace, type_symbol.source_reference.file.gir_version);
1411 if (!externals.contains (external)) {
1412 externals.add (external);
1414 string? gir_fullname = type_symbol.get_attribute_string ("GIR", "fullname");
1415 if (gir_fullname != null) {
1416 return gir_fullname;
1418 var type_name = type_symbol.get_attribute_string ("GIR", "name") ?? type_symbol.name;
1419 return "%s.%s".printf (type_symbol.source_reference.file.gir_namespace, type_name);
1420 } else {
1421 unannotated_namespaces.add(ns);
1426 return get_full_gir_name (type_symbol);
1429 private string? literal_expression_to_value_string (Expression literal) {
1430 if (literal is StringLiteral) {
1431 var lit = literal as StringLiteral;
1432 if (lit != null) {
1433 return Markup.escape_text (lit.eval ());
1435 } else if (literal is CharacterLiteral) {
1436 return "%c".printf ((char) ((CharacterLiteral) literal).get_char ());
1437 } else if (literal is BooleanLiteral) {
1438 return ((BooleanLiteral) literal).value ? "true" : "false";
1439 } else if (literal is RealLiteral) {
1440 return ((RealLiteral) literal).value;
1441 } else if (literal is IntegerLiteral) {
1442 return ((IntegerLiteral) literal).value;
1443 } else if (literal is UnaryExpression) {
1444 var unary = (UnaryExpression) literal;
1445 if (unary.operator == UnaryOperator.MINUS) {
1446 if (unary.inner is RealLiteral) {
1447 return "-" + ((RealLiteral) unary.inner).value;
1448 } else if (unary.inner is IntegerLiteral) {
1449 return "-" + ((IntegerLiteral) unary.inner).value;
1453 return null;
1456 private bool check_accessibility (Symbol sym) {
1457 if (sym.access == SymbolAccessibility.PUBLIC ||
1458 sym.access == SymbolAccessibility.PROTECTED) {
1459 return true;
1462 return false;
1465 private bool is_visibility (Symbol sym) {
1466 return sym.get_attribute_bool ("GIR", "visible", true);