codegen: Infer error parameter from abstract/virtual method implementations
[vala-gnome.git] / codegen / valagirwriter.vala
bloba115852e1ee42ceac87b221b679afc3281e6b0bb
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 return;
171 stream.printf ("<?xml version=\"1.0\"?>\n");
173 stream.printf ("<repository version=\"1.2\"");
174 stream.printf (" xmlns=\"http://www.gtk.org/introspection/core/1.0\"");
175 stream.printf (" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"");
176 stream.printf (" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
177 stream.printf (">\n");
178 indent++;
180 write_includes();
181 indent--;
183 stream.puts (buffer.str);
184 stream = null;
186 foreach (var ns in unannotated_namespaces) {
187 if (!our_namespaces.contains(ns)) {
188 Report.warning (ns.source_reference, "Namespace %s does not have a GIR namespace and version annotation".printf (ns.name));
191 foreach (var ns in our_namespaces) {
192 ns.source_reference.file.gir_namespace = gir_namespace;
193 ns.source_reference.file.gir_version = gir_version;
196 if (our_namespaces.size == 0) {
197 Report.error (null, "No suitable namespace found to export for GIR");
201 private void write_doc (string? comment) {
202 if (comment != null) {
203 write_indent ();
204 buffer.append ("<doc xml:whitespace=\"preserve\">");
205 buffer.append (comment);
206 buffer.append ("</doc>\n");
210 private void write_package (string package) {
211 write_indent ();
212 buffer.append_printf ("<package name=\"%s\"/>\n", package);
215 private void write_c_includes (Namespace ns) {
216 // Collect C header filenames
217 Set<string> header_filenames = new HashSet<string> (str_hash, str_equal);
218 foreach (unowned string c_header_filename in get_ccode_header_filenames (ns).split (",")) {
219 header_filenames.add (c_header_filename);
221 foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) {
222 foreach (unowned string c_header_filename in get_ccode_header_filenames (symbol).split (",")) {
223 header_filenames.add (c_header_filename);
227 // Generate c:include tags
228 foreach (string c_header_filename in header_filenames) {
229 write_c_include (c_header_filename);
233 private void write_c_include (string name) {
234 write_indent ();
235 buffer.append_printf ("<c:include name=\"%s\"/>\n", name);
238 public override void visit_namespace (Namespace ns) {
239 if (ns.external_package) {
240 return;
243 if (ns.name == null) {
244 // global namespace
245 hierarchy.insert (0, ns);
246 ns.accept_children (this);
247 hierarchy.remove_at (0);
248 return;
251 if (ns.parent_symbol.name != null) {
252 ns.accept_children (this);
253 return;
256 write_c_includes (ns);
258 write_indent ();
259 buffer.append_printf ("<namespace name=\"%s\" version=\"%s\"", gir_namespace, gir_version);
260 string? cprefix = get_ccode_prefix (ns);
261 if (gir_shared_library != null) {
262 buffer.append_printf(" shared-library=\"%s\"", gir_shared_library);
264 if (cprefix != null) {
265 buffer.append_printf (" c:prefix=\"%s\"", cprefix);
267 buffer.append_printf (">\n");
268 indent++;
270 write_annotations (ns);
272 hierarchy.insert (0, ns);
273 ns.accept_children (this);
274 hierarchy.remove_at (0);
276 indent--;
277 write_indent ();
278 buffer.append_printf ("</namespace>\n");
279 our_namespaces.add(ns);
281 visit_deferred ();
284 private void write_symbol_attributes (Symbol symbol) {
285 if (symbol.version.deprecated) {
286 buffer.append_printf (" deprecated=\"%s\"", (symbol.version.replacement == null) ? "" : "Use %s".printf (symbol.version.replacement));
287 if (symbol.version.deprecated_since != null) {
288 buffer.append_printf (" deprecated-version=\"%s\"", symbol.version.deprecated_since);
291 if (symbol.version.since != null) {
292 buffer.append_printf (" version=\"%s\"", symbol.version.since);
296 public override void visit_class (Class cl) {
297 if (cl.external_package) {
298 return;
301 if (!check_accessibility (cl)) {
302 return;
305 if (!(hierarchy[0] is Namespace)) {
306 deferred.add (cl);
307 return;
310 if (cl.is_subtype_of (gobject_type)) {
311 string gtype_struct_name = get_gir_name (cl) + "Class";
313 write_indent ();
314 buffer.append_printf ("<class name=\"%s\"", get_gir_name (cl));
315 write_gtype_attributes (cl);
316 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
317 buffer.append_printf (" parent=\"%s\"", gi_type_name (cl.base_class));
318 if (cl.is_abstract) {
319 buffer.append_printf (" abstract=\"1\"");
321 write_symbol_attributes (cl);
322 buffer.append_printf (">\n");
323 indent++;
325 write_doc (get_class_comment (cl));
327 // write implemented interfaces
328 foreach (DataType base_type in cl.get_base_types ()) {
329 var object_type = (ObjectType) base_type;
330 if (object_type.type_symbol is Interface) {
331 write_indent ();
332 buffer.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
336 write_annotations (cl);
338 write_indent ();
339 buffer.append_printf ("<field name=\"parent_instance\">\n");
340 indent++;
341 write_indent ();
342 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
343 indent--;
344 write_indent ();
345 buffer.append_printf("</field>\n");
347 write_indent ();
348 buffer.append_printf ("<field name=\"priv\">\n");
349 indent++;
350 write_indent ();
351 buffer.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", get_gir_name (cl), get_ccode_name (cl));
352 indent--;
353 write_indent ();
354 buffer.append_printf("</field>\n");
356 hierarchy.insert (0, cl);
357 cl.accept_children (this);
358 hierarchy.remove_at (0);
360 indent--;
361 write_indent ();
362 buffer.append_printf ("</class>\n");
364 write_indent ();
365 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
366 write_ctype_attributes (cl, "Class");
367 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", cl.name);
368 buffer.append_printf (">\n");
369 indent++;
371 write_indent ();
372 buffer.append_printf ("<field name=\"parent_class\">\n");
373 indent++;
374 write_indent ();
375 buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl.base_class), get_ccode_name (cl.base_class));
376 indent--;
377 write_indent ();
378 buffer.append_printf ("</field>\n");
380 foreach (Method m in cl.get_methods ()) {
381 if (m.is_abstract || m.is_virtual) {
382 write_indent ();
383 if (m.coroutine) {
384 string finish_name = m.name;
385 if (finish_name.has_suffix ("_async")) {
386 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
388 finish_name += "_finish";
390 write_indent ();
391 buffer.append_printf("<field name=\"%s\">\n", m.name);
392 indent++;
393 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false);
394 indent--;
395 write_indent ();
396 buffer.append_printf ("</field>\n");
398 write_indent ();
399 buffer.append_printf("<field name=\"%s\">\n", finish_name);
400 indent++;
401 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);
402 indent--;
403 write_indent ();
404 buffer.append_printf ("</field>\n");
405 } else {
406 write_indent ();
407 buffer.append_printf("<field name=\"%s\">\n", m.name);
408 indent++;
409 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
410 indent--;
411 write_indent ();
412 buffer.append_printf ("</field>\n");
417 foreach (Signal sig in cl.get_signals ()) {
418 if (sig.default_handler != null) {
419 write_indent ();
420 buffer.append_printf ("<field name=\"%s\">\n", get_ccode_lower_case_name (sig));
421 indent++;
422 write_signature (sig.default_handler, "callback", false, true);
423 indent--;
424 write_indent ();
425 buffer.append_printf ("</field>\n");
429 indent--;
430 write_indent ();
431 buffer.append_printf ("</record>\n");
433 write_indent ();
434 buffer.append_printf ("<record name=\"%sPrivate\" c:type=\"%sPrivate\" disguised=\"1\"/>\n", get_gir_name (cl), get_ccode_name (cl));
435 } else {
436 write_indent ();
437 buffer.append_printf ("<record name=\"%s\"", get_gir_name (cl));
438 write_symbol_attributes (cl);
439 buffer.append_printf (">\n");
440 indent++;
442 write_doc (get_class_comment (cl));
444 write_annotations (cl);
446 hierarchy.insert (0, cl);
447 cl.accept_children (this);
448 hierarchy.remove_at (0);
450 indent--;
451 write_indent ();
452 buffer.append_printf ("</record>\n");
455 visit_deferred ();
458 public override void visit_struct (Struct st) {
459 if (st.external_package) {
460 return;
463 if (!check_accessibility (st)) {
464 return;
467 if (!(hierarchy[0] is Namespace)) {
468 deferred.add (st);
469 return;
472 write_indent ();
473 buffer.append_printf ("<record name=\"%s\"", get_gir_name (st));
474 write_symbol_attributes (st);
475 buffer.append_printf (">\n");
476 indent++;
478 write_doc (get_struct_comment (st));
480 write_annotations (st);
482 hierarchy.insert (0, st);
483 st.accept_children (this);
484 hierarchy.remove_at (0);
486 indent--;
487 write_indent ();
488 buffer.append_printf ("</record>\n");
490 visit_deferred ();
493 public override void visit_interface (Interface iface) {
494 if (iface.external_package) {
495 return;
498 if (!check_accessibility (iface)) {
499 return;
502 if (!(hierarchy[0] is Namespace)) {
503 deferred.add (iface);
504 return;
507 string gtype_struct_name = iface.name + "Iface";
509 write_indent ();
510 buffer.append_printf ("<interface name=\"%s\"", get_gir_name (iface));
511 write_gtype_attributes (iface);
512 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
513 write_symbol_attributes (iface);
514 buffer.append_printf (">\n");
515 indent++;
517 write_doc (get_interface_comment (iface));
519 // write prerequisites
520 if (iface.get_prerequisites ().size > 0) {
521 foreach (DataType base_type in iface.get_prerequisites ()) {
522 write_indent ();
523 buffer.append_printf ("<prerequisite name=\"%s\"/>\n", gi_type_name (((ObjectType) base_type).type_symbol));
527 write_annotations (iface);
529 hierarchy.insert (0, iface);
530 iface.accept_children (this);
531 hierarchy.remove_at (0);
533 indent--;
534 write_indent ();
535 buffer.append_printf ("</interface>\n");
537 write_indent ();
538 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
539 write_ctype_attributes (iface, "Iface");
540 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", iface.name);
541 buffer.append_printf (">\n");
542 indent++;
544 write_indent ();
545 buffer.append_printf ("<field name=\"parent_iface\">\n");
546 indent++;
547 write_indent ();
548 buffer.append_printf ("<type name=\"GObject.TypeInterface\" c:type=\"GTypeInterface\"/>\n");
549 indent--;
550 write_indent ();
551 buffer.append_printf ("</field>\n");
553 foreach (Method m in iface.get_methods ()) {
554 if (m.is_abstract || m.is_virtual) {
555 if (m.coroutine) {
556 string finish_name = m.name;
557 if (finish_name.has_suffix ("_async")) {
558 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
560 finish_name += "_finish";
562 write_indent ();
563 buffer.append_printf("<field name=\"%s\">\n", m.name);
564 indent++;
565 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, false);
566 indent--;
567 write_indent ();
568 buffer.append_printf ("</field>\n");
570 write_indent ();
571 buffer.append_printf("<field name=\"%s\">\n", finish_name);
572 indent++;
573 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);
574 indent--;
575 write_indent ();
576 buffer.append_printf ("</field>\n");
577 } else {
578 write_indent ();
579 buffer.append_printf("<field name=\"%s\">\n", m.name);
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);
582 indent--;
583 write_indent ();
584 buffer.append_printf ("</field>\n");
589 foreach (var prop in iface.get_properties ()) {
590 if (prop.is_abstract || prop.is_virtual) {
591 if (prop.get_accessor != null) {
592 var m = prop.get_accessor.get_method ();
593 write_indent ();
594 buffer.append_printf("<field name=\"%s\">\n", m.name);
595 indent++;
596 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
597 indent--;
598 write_indent ();
599 buffer.append_printf ("</field>\n");
602 if (prop.set_accessor != null) {
603 var m = prop.set_accessor.get_method ();
604 write_indent ();
605 buffer.append_printf("<field name=\"%s\">\n", m.name);
606 indent++;
607 do_write_signature (m, "callback", true, m.name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, false);
608 indent--;
609 write_indent ();
610 buffer.append_printf ("</field>\n");
615 indent--;
616 write_indent ();
617 buffer.append_printf ("</record>\n");
619 visit_deferred ();
622 private void visit_deferred () {
623 var nodes = this.deferred;
624 this.deferred = new Vala.ArrayList<Vala.CodeNode>();
626 foreach (var node in nodes) {
627 node.accept (this);
631 private string? get_gir_name (Symbol symbol) {
632 string? gir_name = null;
633 var h0 = hierarchy[0];
635 for (Symbol? cur_sym = symbol ; cur_sym != null ; cur_sym = cur_sym.parent_symbol) {
636 if (cur_sym == h0) {
637 break;
640 var cur_name = cur_sym.get_attribute_string ("GIR", "name");
641 if (cur_name == null) {
642 cur_name = cur_sym.name;
644 gir_name = cur_name.concat (gir_name);
647 return gir_name;
650 public override void visit_enum (Enum en) {
651 if (en.external_package) {
652 return;
655 if (!check_accessibility (en)) {
656 return;
659 if (!(hierarchy[0] is Namespace)) {
660 deferred.add (en);
661 return;
664 string element_name = (en.is_flags) ? "bitfield" : "enumeration";
666 write_indent ();
667 buffer.append_printf ("<%s name=\"%s\"", element_name, get_gir_name (en));
668 write_gtype_attributes (en);
669 write_symbol_attributes (en);
670 buffer.append_printf (">\n");
671 indent++;
673 write_doc (get_enum_comment (en));
675 write_annotations (en);
677 enum_value = 0;
678 hierarchy.insert (0, en);
679 en.accept_children (this);
680 hierarchy.remove_at (0);
682 indent--;
683 write_indent ();
684 buffer.append_printf ("</%s>\n", element_name);
686 visit_deferred ();
689 private int enum_value;
691 public override void visit_enum_value (EnumValue ev) {
692 write_indent ();
693 var en = (Enum) hierarchy[0];
694 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev.name.down (), get_ccode_name (ev));
695 if (ev.value != null) {
696 string value = literal_expression_to_value_string (ev.value);
697 buffer.append_printf (" value=\"%s\"", value);
698 } else {
699 if (en.is_flags) {
700 buffer.append_printf (" value=\"%d\"", 1 << enum_value++);
701 } else {
702 buffer.append_printf (" value=\"%d\"", enum_value++);
705 write_symbol_attributes (ev);
707 string? comment = get_enum_value_comment (ev);
708 if (comment == null) {
709 buffer.append_printf ("/>\n");
710 } else {
711 buffer.append_printf (">\n");
712 indent++;
714 write_doc (comment);
716 indent--;
717 write_indent ();
718 buffer.append_printf ("</member>\n");
722 public override void visit_error_domain (ErrorDomain edomain) {
723 if (edomain.external_package) {
724 return;
727 if (!check_accessibility (edomain)) {
728 return;
731 write_indent ();
732 buffer.append_printf ("<enumeration name=\"%s\"", edomain.name);
733 write_ctype_attributes (edomain);
734 buffer.append_printf (" glib:error-domain=\"%s\"", get_ccode_quark_name (edomain));
735 buffer.append_printf (">\n");
736 indent++;
738 write_doc (get_error_domain_comment (edomain));
740 enum_value = 0;
741 hierarchy.insert (0, edomain);
742 edomain.accept_children (this);
743 hierarchy.remove_at (0);
745 indent--;
746 write_indent ();
747 buffer.append_printf ("</enumeration>\n");
749 visit_deferred ();
752 public override void visit_error_code (ErrorCode ecode) {
753 write_indent ();
754 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode.name.down (), get_ccode_name (ecode));
755 if (ecode.value != null) {
756 string value = literal_expression_to_value_string (ecode.value);
757 buffer.append_printf (" value=\"%s\"", value);
758 } else {
759 buffer.append_printf (" value=\"%d\"", enum_value++);
761 write_symbol_attributes (ecode);
763 string? comment = get_error_code_comment (ecode);
764 if (comment == null) {
765 buffer.append_printf ("/>\n");
766 } else {
767 buffer.append_printf (">\n");
768 indent++;
770 write_doc (comment);
772 indent--;
773 write_indent ();
774 buffer.append_printf ("</member>\n");
778 public override void visit_constant (Constant c) {
779 if (c.external_package) {
780 return;
783 if (!check_accessibility (c)) {
784 return;
787 //TODO Add better constant evaluation
788 var initializer = c.value;
789 string value = literal_expression_to_value_string (initializer);
791 write_indent ();
792 buffer.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", c.name, get_ccode_name (c));
793 buffer.append_printf (" value=\"%s\"", value);
794 write_symbol_attributes (c);
795 buffer.append_printf (">\n");
796 indent++;
798 write_doc (get_constant_comment (c));
800 write_type (initializer.value_type);
802 indent--;
803 write_indent ();
804 buffer.append_printf ("</constant>\n");
807 public override void visit_field (Field f) {
808 if (f.external_package) {
809 return;
812 if (!check_accessibility (f)) {
813 return;
816 write_indent ();
817 buffer.append_printf ("<field name=\"%s\"", get_ccode_name (f));
818 if (f.variable_type.nullable) {
819 buffer.append_printf (" allow-none=\"1\"");
821 write_symbol_attributes (f);
822 buffer.append_printf (">\n");
823 indent++;
825 write_doc (get_field_comment (f));
827 write_annotations (f);
829 write_type (f.variable_type);
831 indent--;
832 write_indent ();
833 buffer.append_printf ("</field>\n");
836 private void write_implicit_params (DataType type, ref int index, bool has_array_length, string name, ParameterDirection direction) {
837 if (type is ArrayType && has_array_length) {
838 var int_type = new IntegerType (CodeContext.get ().root.scope.lookup ("int") as Struct);
839 for (var i = 0; i < ((ArrayType) type).rank; i++) {
840 write_param_or_return (int_type, true, ref index, has_array_length, "%s_length%i".printf (name, i + 1), null, direction);
842 } else if (type is DelegateType) {
843 var deleg_type = (DelegateType) type;
844 if (deleg_type.delegate_symbol.has_target) {
845 var data_type = new PointerType (new VoidType ());
846 write_param_or_return (data_type, true, ref index, false, "%s_target".printf (name), null, direction);
847 if (deleg_type.is_disposable ()) {
848 var notify_type = new DelegateType (CodeContext.get ().root.scope.lookup ("GLib").scope.lookup ("DestroyNotify") as Delegate);
849 write_param_or_return (notify_type, true, ref index, false, "%s_target_destroy_notify".printf (name), null, direction);
855 void skip_implicit_params (DataType type, ref int index, bool has_array_length) {
856 if (type is ArrayType && has_array_length) {
857 index += ((ArrayType) type).rank;
858 } else if (type is DelegateType) {
859 index++;
860 var deleg_type = (DelegateType) type;
861 if (deleg_type.is_disposable ()) {
862 index++;
867 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) {
868 int last_index = 0;
869 bool ret_is_struct = return_type != null && return_type.is_real_non_null_struct_type ();
871 if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
872 int index = 0;
874 if (instance_type != null) {
875 index++;
878 foreach (Parameter param in params) {
879 index++;
881 skip_implicit_params (param.variable_type, ref index, get_ccode_array_length (param));
884 if (ret_is_struct) {
885 index++;
886 } else {
887 skip_implicit_params (return_type, ref index, return_array_length);
888 if (return_type is ArrayType && return_array_length) {
889 index -= ((ArrayType) return_type).rank - 1;
893 last_index = index - 1;
896 if (return_type != null && !ret_is_struct) {
897 write_param_or_return (return_type, false, ref last_index, return_array_length, null, return_comment, ParameterDirection.IN, constructor);
898 } else if (ret_is_struct) {
899 write_param_or_return (new VoidType (), false, ref last_index, false, null, return_comment, ParameterDirection.IN);
902 if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
903 write_indent ();
904 buffer.append_printf ("<parameters>\n");
905 indent++;
906 int index = 0;
908 if (instance_type != null) {
909 write_param_or_return (instance_type, true, ref index, false, "self");
912 foreach (Parameter param in params) {
913 write_param_or_return (param.variable_type, true, ref index, get_ccode_array_length (param), param.name, get_parameter_comment (param), param.direction);
915 write_implicit_params (param.variable_type, ref index, get_ccode_array_length (param), param.name, param.direction);
918 if (ret_is_struct) {
919 // struct returns are converted to parameters
920 write_param_or_return (return_type, true, ref index, false, "result", return_comment, ParameterDirection.OUT, constructor, true);
921 } else {
922 write_implicit_params (return_type, ref index, return_array_length, "result", ParameterDirection.OUT);
925 if (user_data) {
926 write_indent ();
927 buffer.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index);
928 indent++;
929 write_indent ();
930 buffer.append_printf ("<type name=\"gpointer\" c:type=\"void*\"/>\n");
931 indent--;
932 write_indent ();
933 buffer.append_printf ("</parameter>\n");
936 indent--;
937 write_indent ();
938 buffer.append_printf ("</parameters>\n");
942 public override void visit_delegate (Delegate cb) {
943 if (cb.external_package) {
944 return;
947 if (!check_accessibility (cb)) {
948 return;
951 write_indent ();
952 buffer.append_printf ("<callback name=\"%s\"", cb.name);
953 buffer.append_printf (" c:type=\"%s\"", get_ccode_name (cb));
954 if (cb.tree_can_fail) {
955 buffer.append_printf (" throws=\"1\"");
957 write_symbol_attributes (cb);
958 buffer.append_printf (">\n");
959 indent++;
961 write_doc (get_delegate_comment (cb));
963 write_annotations (cb);
965 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);
967 indent--;
968 write_indent ();
969 buffer.append_printf ("</callback>\n");
972 public override void visit_method (Method m) {
973 if (m.external_package) {
974 return;
977 // don't write interface implementation unless it's an abstract or virtual method
978 if (!check_accessibility (m) || m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
979 return;
982 // check for unsupported types
983 if (!check_signature (m)) {
984 return;
987 string tag_name = "method";
988 var parent = this.hierarchy.get (0);
989 if (parent is Enum) {
990 deferred.add (m);
991 return;
994 if (parent is Namespace || m.binding == MemberBinding.STATIC || parent != m.parent_symbol) {
995 tag_name = "function";
998 write_signature (m, tag_name, true);
1000 if (m.is_abstract || m.is_virtual) {
1001 write_signature (m, "virtual-method", true, false);
1005 bool check_type (DataType type) {
1006 // gobject-introspection does not currently support va_list parameters
1007 if (get_ccode_name (type) == "va_list") {
1008 return false;
1011 return true;
1014 bool check_signature (Method m) {
1015 if (!check_type (m.return_type)) {
1016 return false;
1018 foreach (var param in m.get_parameters ()) {
1019 if (param.variable_type == null || !check_type (param.variable_type)) {
1020 return false;
1023 return true;
1026 private void write_signature (Method m, string tag_name, bool write_doc, bool instance = false) {
1027 var parent = this.hierarchy.get (0);
1028 string name;
1029 if (m.parent_symbol != parent) {
1030 instance = false;
1031 name = get_ccode_name (m);
1032 var parent_prefix = get_ccode_lower_case_prefix (parent);
1033 if (name.has_prefix (parent_prefix)) {
1034 name = name.substring (parent_prefix.length);
1036 } else {
1037 name = m.name;
1040 if (m.coroutine) {
1041 string finish_name = name;
1042 if (finish_name.has_suffix ("_async")) {
1043 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
1045 finish_name += "_finish";
1046 do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_async_begin_parameters (), new VoidType (), false, true);
1047 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);
1048 } else {
1049 do_write_signature (m, tag_name, instance, name, get_ccode_name (m), m.get_parameters (), m.return_type, m.tree_can_fail, true);
1053 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) {
1054 write_indent ();
1055 buffer.append_printf ("<%s name=\"%s\"", tag_name, name);
1056 if (tag_name == "virtual-method") {
1057 buffer.append_printf (" invoker=\"%s\"", name);
1058 } else if (tag_name == "callback") {
1059 /* this is only used for vfuncs */
1060 buffer.append_printf (" c:type=\"%s\"", name);
1061 } else {
1062 buffer.append_printf (" c:identifier=\"%s\"", cname);
1064 if (can_fail) {
1065 buffer.append_printf (" throws=\"1\"");
1067 write_symbol_attributes (m);
1068 buffer.append_printf (">\n");
1069 indent++;
1071 string? return_comment = null;
1072 if (write_comment) {
1073 return_comment = get_method_return_comment (m);
1074 write_doc (get_method_comment (m));
1077 write_annotations (m);
1079 DataType instance_type = null;
1080 if (instance) {
1081 instance_type = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
1084 write_params_and_return (params, return_type, get_ccode_array_length (m), return_comment, false, instance_type);
1086 indent--;
1087 write_indent ();
1088 buffer.append_printf ("</%s>\n", tag_name);
1091 public override void visit_creation_method (CreationMethod m) {
1092 if (m.external_package) {
1093 return;
1096 if (!check_accessibility (m)) {
1097 return;
1100 if (m.parent_symbol is Class && ((Class) m.parent_symbol).is_abstract) {
1101 return;
1104 write_indent ();
1106 bool is_struct = m.parent_symbol is Struct;
1107 // GI doesn't like constructors that return void type
1108 string tag_name = is_struct ? "function" : "constructor";
1110 if (m.parent_symbol is Class && m == ((Class)m.parent_symbol).default_construction_method ||
1111 m.parent_symbol is Struct && m == ((Struct)m.parent_symbol).default_construction_method) {
1112 string m_name = is_struct ? "init" : "new";
1113 buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m_name, get_ccode_name (m));
1114 } else {
1115 buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m.name, get_ccode_name (m));
1118 if (m.tree_can_fail) {
1119 buffer.append_printf (" throws=\"1\"");
1121 buffer.append_printf (">\n");
1122 indent++;
1124 write_doc (get_method_comment (m));
1126 write_annotations (m);
1129 var datatype = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
1130 write_params_and_return (m.get_parameters (), datatype, false, get_method_return_comment (m), true);
1132 indent--;
1133 write_indent ();
1134 buffer.append_printf ("</%s>\n", tag_name);
1137 public override void visit_property (Property prop) {
1138 if (!check_accessibility (prop) || prop.overrides || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
1139 return;
1142 write_indent ();
1143 buffer.append_printf ("<property name=\"%s\"", prop.name.replace ("_", "-"));
1144 if (prop.get_accessor == null) {
1145 buffer.append_printf (" readable=\"0\"");
1147 if (prop.set_accessor != null) {
1148 buffer.append_printf (" writable=\"1\"");
1149 if (prop.set_accessor.construction) {
1150 if (!prop.set_accessor.writable) {
1151 buffer.append_printf (" construct-only=\"1\"");
1152 } else {
1153 buffer.append_printf (" construct=\"1\"");
1157 write_symbol_attributes (prop);
1158 buffer.append_printf (">\n");
1159 indent++;
1161 write_doc (get_property_comment (prop));
1163 write_annotations (prop);
1165 write_type (prop.property_type);
1167 indent--;
1168 write_indent ();
1169 buffer.append_printf ("</property>\n");
1171 if (prop.get_accessor != null) {
1172 var m = prop.get_accessor.get_method ();
1173 if (m != null) {
1174 visit_method (m);
1178 if (prop.set_accessor != null) {
1179 var m = prop.set_accessor.get_method ();
1180 if (m != null) {
1181 visit_method (m);
1186 public override void visit_signal (Signal sig) {
1187 if (!check_accessibility (sig)) {
1188 return;
1191 if (sig.emitter != null) {
1192 sig.emitter.accept (this);
1195 write_indent ();
1196 buffer.append_printf ("<glib:signal name=\"%s\"", get_ccode_name (sig));
1197 write_symbol_attributes (sig);
1198 buffer.append_printf (">\n");
1199 indent++;
1201 write_doc (get_signal_comment (sig));
1203 write_annotations (sig);
1205 write_params_and_return (sig.get_parameters (), sig.return_type, false, get_signal_return_comment (sig));
1207 indent--;
1208 write_indent ();
1209 buffer.append_printf ("</glib:signal>\n");
1212 private void write_indent () {
1213 int i;
1215 for (i = 0; i < indent; i++) {
1216 buffer.append_c ('\t');
1220 private void write_indent_stream () {
1221 int i;
1223 for (i = 0; i < indent; i++) {
1224 stream.putc ('\t');
1229 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) {
1230 write_indent ();
1231 string tag = is_parameter ? "parameter" : "return-value";
1232 buffer.append_printf ("<%s", tag);
1233 if (name != null) {
1234 buffer.append_printf (" name=\"%s\"", name);
1236 if (direction == ParameterDirection.REF) {
1237 buffer.append_printf (" direction=\"inout\"");
1238 } else if (direction == ParameterDirection.OUT) {
1239 buffer.append_printf (" direction=\"out\"");
1242 DelegateType delegate_type = type as DelegateType;
1244 if ((type.value_owned && delegate_type == null) || (constructor && !type.data_type.is_subtype_of (ginitiallyunowned_type))) {
1245 var any_owned = false;
1246 foreach (var generic_arg in type.get_type_arguments ()) {
1247 any_owned |= generic_arg.value_owned;
1249 if (type.has_type_arguments () && !any_owned) {
1250 buffer.append_printf (" transfer-ownership=\"container\"");
1251 } else {
1252 buffer.append_printf (" transfer-ownership=\"full\"");
1254 } else {
1255 buffer.append_printf (" transfer-ownership=\"none\"");
1257 if (caller_allocates) {
1258 buffer.append_printf (" caller-allocates=\"1\"");
1260 if (type.nullable) {
1261 buffer.append_printf (" allow-none=\"1\"");
1264 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
1265 int closure_index = is_parameter ?
1266 index + 1 : (type.value_owned ? index - 1 : index);
1267 buffer.append_printf (" closure=\"%i\"", closure_index);
1268 if (delegate_type.is_called_once) {
1269 buffer.append (" scope=\"async\"");
1270 } else if (type.value_owned) {
1271 buffer.append_printf (" scope=\"notified\" destroy=\"%i\"", closure_index + 1);
1272 } else {
1273 buffer.append (" scope=\"call\"");
1275 } else if (delegate_type != null) {
1276 buffer.append (" scope=\"call\"");
1279 buffer.append_printf (">\n");
1280 indent++;
1282 write_doc (comment);
1284 int length_param_index = -1;
1285 if (has_array_length) {
1286 length_param_index = is_parameter ? index + 1 : index;
1288 write_type (type, length_param_index, direction);
1290 indent--;
1291 write_indent ();
1292 buffer.append_printf ("</%s>\n", tag);
1293 index++;
1296 private void write_ctype_attributes (TypeSymbol symbol, string suffix = "") {
1297 buffer.append_printf (" c:type=\"%s%s\"", get_ccode_name (symbol), suffix);
1300 private void write_gtype_attributes (TypeSymbol symbol) {
1301 write_ctype_attributes(symbol);
1302 buffer.append_printf (" glib:type-name=\"%s\"", get_ccode_name (symbol));
1303 buffer.append_printf (" glib:get-type=\"%sget_type\"", get_ccode_lower_case_prefix (symbol));
1306 private void write_type (DataType type, int index = -1, ParameterDirection direction = ParameterDirection.IN) {
1307 if (type is ArrayType) {
1308 var array_type = (ArrayType) type;
1310 write_indent ();
1311 buffer.append_printf ("<array");
1312 if (array_type.fixed_length && array_type.length is IntegerLiteral) {
1313 var lit = (IntegerLiteral) array_type.length;
1314 buffer.append_printf (" fixed-size=\"%i\"", int.parse (lit.value));
1315 } else if (index != -1) {
1316 buffer.append_printf (" length=\"%i\"", index);
1318 buffer.append_printf (">\n");
1319 indent++;
1321 write_type (array_type.element_type);
1323 indent--;
1324 write_indent ();
1325 buffer.append_printf ("</array>\n");
1326 } else if (type is VoidType) {
1327 write_indent ();
1328 buffer.append_printf ("<type name=\"none\"/>\n");
1329 } else if (type is PointerType) {
1330 write_indent ();
1331 buffer.append_printf ("<type name=\"gpointer\" c:type=\"%s\"/>\n", get_ccode_name (type));
1332 } else if (type.data_type != null) {
1333 write_indent ();
1334 string type_name = gi_type_name (type.data_type);
1335 bool is_array = false;
1336 if ((type_name == "GLib.Array") || (type_name == "GLib.PtrArray")) {
1337 is_array = true;
1339 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 ? "" : "*");
1341 List<DataType> type_arguments = type.get_type_arguments ();
1342 if (type_arguments.size == 0) {
1343 buffer.append_printf ("/>\n");
1344 } else {
1345 buffer.append_printf (">\n");
1346 indent++;
1348 foreach (DataType type_argument in type_arguments) {
1349 write_type (type_argument);
1352 indent--;
1353 write_indent ();
1354 buffer.append_printf ("</%s>\n", is_array ? "array" : "type");
1356 } else if (type is DelegateType) {
1357 var deleg_type = (DelegateType) type;
1358 write_indent ();
1359 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (deleg_type.delegate_symbol), get_ccode_name (type));
1360 } else if (type is GenericType) {
1361 // generic type parameters not supported in GIR
1362 write_indent ();
1363 buffer.append ("<type name=\"gpointer\" c:type=\"gpointer\"/>\n");
1364 } else {
1365 write_indent ();
1366 buffer.append_printf ("<type name=\"%s\"/>\n", type.to_string ());
1370 private void write_annotations (CodeNode node) {
1371 foreach (Attribute attr in node.attributes) {
1372 string name = camel_case_to_canonical (attr.name);
1373 foreach (string arg_name in attr.args.get_keys ()) {
1374 string value = attr.args.get (arg_name);
1375 if (value.has_prefix ("\"")) {
1376 // eval string
1377 value = attr.get_string (arg_name);
1380 write_indent ();
1381 buffer.append_printf ("<attribute name=\"%s.%s\" value=\"%s\"/>\n",
1382 name, camel_case_to_canonical (arg_name), value);
1387 private string? get_full_gir_name (Symbol sym) {
1388 string? gir_fullname = sym.get_attribute_string ("GIR", "fullname");
1389 if (gir_fullname != null) {
1390 return gir_fullname;
1393 string? gir_name = sym.get_attribute_string ("GIR", "name");
1395 if (gir_name == null && sym is Namespace) {
1396 gir_name = sym.get_attribute_string ("CCode", "gir_namespace");
1398 if (gir_name == null) {
1399 gir_name = sym.name;
1402 if (sym.parent_symbol == null) {
1403 return gir_name;
1406 if (sym.name == null) {
1407 return get_full_gir_name (sym.parent_symbol);
1410 string parent_gir_name = get_full_gir_name (sym.parent_symbol);
1411 if (parent_gir_name == null) {
1412 return gir_name;
1415 string self_gir_name = gir_name.has_prefix (".") ? gir_name.substring (1) : gir_name;
1416 if ("." in parent_gir_name) {
1417 return "%s%s".printf (parent_gir_name, self_gir_name);
1418 } else {
1419 return "%s.%s".printf (parent_gir_name, self_gir_name);
1423 private string gi_type_name (TypeSymbol type_symbol) {
1424 Symbol parent = type_symbol.parent_symbol;
1425 if (parent is Namespace) {
1426 Namespace ns = parent as Namespace;
1427 var ns_gir_name = ns.get_attribute_string ("GIR", "name") ?? ns.name;
1428 if (ns_gir_name != null) {
1429 if (type_symbol.source_reference.file.gir_namespace != null) {
1430 GIRNamespace external = GIRNamespace (type_symbol.source_reference.file.gir_namespace, type_symbol.source_reference.file.gir_version);
1431 if (!externals.contains (external)) {
1432 externals.add (external);
1434 string? gir_fullname = type_symbol.get_attribute_string ("GIR", "fullname");
1435 if (gir_fullname != null) {
1436 return gir_fullname;
1438 var type_name = type_symbol.get_attribute_string ("GIR", "name") ?? type_symbol.name;
1439 return "%s.%s".printf (type_symbol.source_reference.file.gir_namespace, type_name);
1440 } else {
1441 unannotated_namespaces.add(ns);
1446 return get_full_gir_name (type_symbol);
1449 private string? literal_expression_to_value_string (Expression literal) {
1450 if (literal is StringLiteral) {
1451 var lit = literal as StringLiteral;
1452 if (lit != null) {
1453 return Markup.escape_text (lit.eval ());
1455 } else if (literal is CharacterLiteral) {
1456 return "%c".printf ((char) ((CharacterLiteral) literal).get_char ());
1457 } else if (literal is BooleanLiteral) {
1458 return ((BooleanLiteral) literal).value ? "true" : "false";
1459 } else if (literal is RealLiteral) {
1460 return ((RealLiteral) literal).value;
1461 } else if (literal is IntegerLiteral) {
1462 return ((IntegerLiteral) literal).value;
1463 } else if (literal is UnaryExpression) {
1464 var unary = (UnaryExpression) literal;
1465 if (unary.operator == UnaryOperator.MINUS) {
1466 if (unary.inner is RealLiteral) {
1467 return "-" + ((RealLiteral) unary.inner).value;
1468 } else if (unary.inner is IntegerLiteral) {
1469 return "-" + ((IntegerLiteral) unary.inner).value;
1473 return null;
1476 private string camel_case_to_canonical (string name) {
1477 string[] parts = Symbol.camel_case_to_lower_case (name).split ("_");
1478 return string.joinv ("-", parts);
1481 private bool check_accessibility (Symbol sym) {
1482 if (sym.access == SymbolAccessibility.PUBLIC ||
1483 sym.access == SymbolAccessibility.PROTECTED) {
1484 return true;
1487 return false;