codegen: Drop useless comma expression when handling struct arguments
[vala-lang.git] / codegen / valagirwriter.vala
blob99b34ac7f1a101483e827475ce09d8d7b494e5d0
1 /* valagirwriter.vala
3 * Copyright (C) 2008-2010 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;
34 StringBuilder buffer = new StringBuilder();
35 FileStream stream;
36 Vala.HashSet<Namespace> unannotated_namespaces = new Vala.HashSet<Namespace>();
37 Vala.HashSet<Namespace> our_namespaces = new Vala.HashSet<Namespace>();
38 Vala.ArrayList<Vala.Symbol> hierarchy = new Vala.ArrayList<Vala.Symbol>();
39 Vala.ArrayList<Vala.CodeNode> deferred = new Vala.ArrayList<Vala.CodeNode>();
41 int indent;
43 private TypeSymbol gobject_type;
45 private struct GIRNamespace {
46 public GIRNamespace (string ns, string version) {
47 this.ns = ns; this.version = version;
49 public string ns;
50 public string version;
51 public bool equal (GIRNamespace g) {
52 return ((ns == g.ns) && (version == g.version));
56 private ArrayList<GIRNamespace?> externals = new ArrayList<GIRNamespace?> ((EqualFunc) GIRNamespace.equal);
58 public void write_includes() {
59 foreach (var i in externals) {
60 write_indent_stream ();
61 stream.printf ("<include name=\"%s\" version=\"%s\"/>\n", i.ns, i.version);
66 /**
67 * Writes the public interface of the specified code context into the
68 * specified file.
70 * @param context a code context
71 * @param filename a relative or absolute filename
73 public void write_file (CodeContext context, string directory, string gir_namespace, string gir_version, string package) {
74 this.context = context;
75 this.directory = directory;
76 this.gir_namespace = gir_namespace;
77 this.gir_version = gir_version;
79 var root_symbol = context.root;
80 var glib_ns = root_symbol.scope.lookup ("GLib");
81 gobject_type = (TypeSymbol) glib_ns.scope.lookup ("Object");
83 write_package (package);
85 context.accept (this);
87 indent--;
88 buffer.append_printf ("</repository>\n");
90 string filename = "%s%c%s-%s.gir".printf (directory, Path.DIR_SEPARATOR, gir_namespace, gir_version);
91 stream = FileStream.open (filename, "w");
92 if (stream == null) {
93 Report.error (null, "unable to open `%s' for writing".printf (filename));
94 return;
97 stream.printf ("<?xml version=\"1.0\"?>\n");
99 stream.printf ("<repository version=\"1.2\"");
100 stream.printf (" xmlns=\"http://www.gtk.org/introspection/core/1.0\"");
101 stream.printf (" xmlns:c=\"http://www.gtk.org/introspection/c/1.0\"");
102 stream.printf (" xmlns:glib=\"http://www.gtk.org/introspection/glib/1.0\"");
103 stream.printf (">\n");
104 indent++;
106 write_includes();
107 indent--;
109 stream.puts (buffer.str);
110 stream = null;
112 foreach (var ns in unannotated_namespaces) {
113 if (!our_namespaces.contains(ns)) {
114 Report.warning (ns.source_reference, "Namespace %s does not have a GIR namespace and version annotation".printf (ns.name));
117 foreach (var ns in our_namespaces) {
118 ns.source_reference.file.gir_namespace = gir_namespace;
119 ns.source_reference.file.gir_version = gir_version;
123 private void write_package (string package) {
124 write_indent ();
125 buffer.append_printf ("<package name=\"%s\"/>\n", package);
128 private void write_c_includes (Namespace ns) {
129 // Collect C header filenames
130 Set<string> header_filenames = new HashSet<string> (str_hash, str_equal);
131 foreach (string c_header_filename in ns.get_cheader_filenames ()) {
132 header_filenames.add (c_header_filename);
134 foreach (Symbol symbol in ns.scope.get_symbol_table ().get_values ()) {
135 foreach (string c_header_filename in symbol.get_cheader_filenames ()) {
136 header_filenames.add (c_header_filename);
140 // Generate c:include tags
141 foreach (string c_header_filename in header_filenames) {
142 write_c_include (c_header_filename);
146 private void write_c_include (string name) {
147 write_indent ();
148 buffer.append_printf ("<c:include name=\"%s\"/>\n", name);
151 public override void visit_namespace (Namespace ns) {
152 if (ns.external_package) {
153 return;
156 if (ns.name == null) {
157 // global namespace
158 hierarchy.insert (0, ns);
159 ns.accept_children (this);
160 hierarchy.remove_at (0);
161 return;
164 if (ns.parent_symbol.name != null) {
165 ns.accept_children (this);
166 return;
169 write_c_includes (ns);
171 write_indent ();
172 buffer.append_printf ("<namespace name=\"%s\" version=\"%s\"", gir_namespace, gir_version);
173 string? cprefix = ns.get_cprefix ();
174 if (cprefix != null) {
175 buffer.append_printf (" c:prefix=\"%s\"", cprefix);
177 buffer.append_printf (">\n");
178 indent++;
180 write_annotations (ns);
182 hierarchy.insert (0, ns);
183 ns.accept_children (this);
184 hierarchy.remove_at (0);
186 indent--;
187 write_indent ();
188 buffer.append_printf ("</namespace>\n");
189 our_namespaces.add(ns);
191 visit_deferred ();
194 private void write_symbol_attributes (Symbol symbol) {
195 if (symbol.deprecated) {
196 buffer.append_printf (" deprecated=\"%s\"", (symbol.replacement == null) ? "" : "Use %s".printf (symbol.replacement));
197 if (symbol.deprecated_since != null) {
198 buffer.append_printf (" deprecated-version=\"%s\"", symbol.deprecated_since);
203 public override void visit_class (Class cl) {
204 if (cl.external_package) {
205 return;
208 if (!check_accessibility (cl)) {
209 return;
212 if (!(hierarchy[0] is Namespace)) {
213 deferred.add (cl);
214 return;
217 if (cl.is_subtype_of (gobject_type)) {
218 string gtype_struct_name = cl.name + "Class";
220 write_indent ();
221 buffer.append_printf ("<class name=\"%s\"", get_gir_name (cl));
222 write_gtype_attributes (cl);
223 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
224 buffer.append_printf (" parent=\"%s\"", gi_type_name (cl.base_class));
225 if (cl.is_abstract) {
226 buffer.append_printf (" abstract=\"1\"");
228 write_symbol_attributes (cl);
229 buffer.append_printf (">\n");
230 indent++;
232 // write implemented interfaces
233 foreach (DataType base_type in cl.get_base_types ()) {
234 var object_type = (ObjectType) base_type;
235 if (object_type.type_symbol is Interface) {
236 write_indent ();
237 buffer.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type.type_symbol));
241 write_annotations (cl);
243 write_indent ();
244 buffer.append_printf ("<field name=\"parent_instance\">\n");
245 indent++;
246 write_indent ();
247 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl.base_class), cl.base_class.get_cname ());
248 indent--;
249 write_indent ();
250 buffer.append_printf("</field>\n");
252 write_indent ();
253 buffer.append_printf ("<field name=\"priv\">\n");
254 indent++;
255 write_indent ();
256 buffer.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", cl.name, cl.get_cname ());
257 indent--;
258 write_indent ();
259 buffer.append_printf("</field>\n");
261 hierarchy.insert (0, cl);
262 cl.accept_children (this);
263 hierarchy.remove_at (0);
265 indent--;
266 write_indent ();
267 buffer.append_printf ("</class>\n");
269 write_indent ();
270 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
271 write_ctype_attributes (cl, "Class");
272 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", cl.name);
273 buffer.append_printf (">\n");
274 indent++;
276 write_indent ();
277 buffer.append_printf ("<field name=\"parent_class\">\n");
278 indent++;
279 write_indent ();
280 buffer.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl.base_class), cl.base_class.get_cname ());
281 indent--;
282 write_indent ();
283 buffer.append_printf ("</field>\n");
285 foreach (Method m in cl.get_methods ()) {
286 if (m.is_abstract || m.is_virtual) {
287 write_indent ();
288 buffer.append_printf("<field name=\"%s\">\n", m.name);
289 indent++;
290 write_signature(m, "callback", true);
291 indent--;
292 write_indent ();
293 buffer.append_printf ("</field>\n");
297 foreach (Signal sig in cl.get_signals ()) {
298 if (sig.default_handler != null) {
299 write_indent ();
300 buffer.append_printf ("<field name=\"%s\">\n", sig.name);
301 indent++;
302 write_signature (sig.default_handler, "callback", true);
303 indent--;
304 write_indent ();
305 buffer.append_printf ("</field>\n");
310 indent--;
311 write_indent ();
312 buffer.append_printf ("</record>\n");
314 write_indent ();
315 buffer.append_printf ("<record name=\"%sPrivate\" c:type=\"%sPrivate\" disguised=\"1\"/>\n", cl.name, cl.get_cname ());
316 } else {
317 write_indent ();
318 buffer.append_printf ("<record name=\"%s\"", get_gir_name (cl));
319 write_symbol_attributes (cl);
320 buffer.append_printf (">\n");
321 indent++;
323 write_annotations (cl);
325 hierarchy.insert (0, cl);
326 cl.accept_children (this);
327 hierarchy.remove_at (0);
329 indent--;
330 write_indent ();
331 buffer.append_printf ("</record>\n");
334 visit_deferred ();
337 public override void visit_struct (Struct st) {
338 if (st.external_package) {
339 return;
342 if (!check_accessibility (st)) {
343 return;
346 if (!(hierarchy[0] is Namespace)) {
347 deferred.add (st);
348 return;
351 write_indent ();
352 buffer.append_printf ("<record name=\"%s\"", get_gir_name (st));
353 write_symbol_attributes (st);
354 buffer.append_printf (">\n");
355 indent++;
357 write_annotations (st);
359 hierarchy.insert (0, st);
360 st.accept_children (this);
361 hierarchy.remove_at (0);
363 indent--;
364 write_indent ();
365 buffer.append_printf ("</record>\n");
367 visit_deferred ();
370 public override void visit_interface (Interface iface) {
371 if (iface.external_package) {
372 return;
375 if (!check_accessibility (iface)) {
376 return;
379 if (!(hierarchy[0] is Namespace)) {
380 deferred.add (iface);
381 return;
384 string gtype_struct_name = iface.name + "Iface";
386 write_indent ();
387 buffer.append_printf ("<interface name=\"%s\"", get_gir_name (iface));
388 write_gtype_attributes (iface);
389 buffer.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name);
390 write_symbol_attributes (iface);
391 buffer.append_printf (">\n");
392 indent++;
394 // write prerequisites
395 if (iface.get_prerequisites ().size > 0) {
396 foreach (DataType base_type in iface.get_prerequisites ()) {
397 write_indent ();
398 buffer.append_printf ("<prerequisite name=\"%s\"/>\n", gi_type_name (((ObjectType) base_type).type_symbol));
402 write_annotations (iface);
404 hierarchy.insert (0, iface);
405 iface.accept_children (this);
406 hierarchy.remove_at (0);
408 indent--;
409 write_indent ();
410 buffer.append_printf ("</interface>\n");
412 write_indent ();
413 buffer.append_printf ("<record name=\"%s\"", gtype_struct_name);
414 write_ctype_attributes (iface, "Iface");
415 buffer.append_printf (" glib:is-gtype-struct-for=\"%s\"", iface.name);
416 buffer.append_printf (">\n");
417 indent++;
419 write_indent ();
420 buffer.append_printf ("<field name=\"parent_iface\">\n");
421 indent++;
422 write_indent ();
423 buffer.append_printf ("<type name=\"GObject.TypeInterface\" c:type=\"GTypeInterface\"/>\n");
424 indent--;
425 write_indent ();
426 buffer.append_printf ("</field>\n");
428 foreach (Method m in iface.get_methods ()) {
429 if (m.is_abstract || m.is_virtual) {
430 write_indent ();
431 buffer.append_printf("<field name=\"%s\">\n", m.name);
432 indent++;
433 write_signature(m, "callback", true);
434 indent--;
435 write_indent ();
436 buffer.append_printf ("</field>\n");
440 indent--;
441 write_indent ();
442 buffer.append_printf ("</record>\n");
444 visit_deferred ();
447 private void visit_deferred () {
448 var nodes = this.deferred;
449 this.deferred = new Vala.ArrayList<Vala.CodeNode>();
451 foreach (var node in nodes) {
452 node.accept (this);
456 private string? get_gir_name (Symbol symbol) {
457 string? gir_name = null;
458 var h0 = hierarchy[0];
460 for (Symbol? cur_sym = symbol ; cur_sym != null ; cur_sym = cur_sym.parent_symbol) {
461 if (cur_sym == h0) {
462 break;
465 gir_name = cur_sym.gir_name.concat (gir_name);
468 return gir_name;
471 public override void visit_enum (Enum en) {
472 if (en.external_package) {
473 return;
476 if (!check_accessibility (en)) {
477 return;
480 if (!(hierarchy[0] is Namespace)) {
481 deferred.add (en);
482 return;
485 string element_name = (en.is_flags) ? "bitfield" : "enumeration";
487 write_indent ();
488 buffer.append_printf ("<%s name=\"%s\"", element_name, get_gir_name (en));
489 write_gtype_attributes (en);
490 write_symbol_attributes (en);
491 buffer.append_printf (">\n");
492 indent++;
494 write_annotations (en);
496 enum_value = 0;
497 hierarchy.insert (0, en);
498 en.accept_children (this);
499 hierarchy.remove_at (0);
501 indent--;
502 write_indent ();
503 buffer.append_printf ("</%s>\n", element_name);
505 visit_deferred ();
508 private int enum_value;
510 public override void visit_enum_value (EnumValue ev) {
511 write_indent ();
512 var en = (Enum) hierarchy[0];
513 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev.name.down (), ev.get_cname ());
514 if (ev.value != null) {
515 string value = literal_expression_to_value_string (ev.value);
516 buffer.append_printf (" value=\"%s\"", value);
517 } else {
518 if (en.is_flags) {
519 buffer.append_printf (" value=\"%d\"", 1 << enum_value++);
520 } else {
521 buffer.append_printf (" value=\"%d\"", enum_value++);
524 write_symbol_attributes (ev);
525 buffer.append_printf ("/>\n");
528 public override void visit_error_domain (ErrorDomain edomain) {
529 if (edomain.external_package) {
530 return;
533 if (!check_accessibility (edomain)) {
534 return;
537 write_indent ();
538 buffer.append_printf ("<errordomain name=\"%s\"", edomain.name);
539 buffer.append_printf (" get-quark=\"%squark\"", edomain.get_lower_case_cprefix ());
540 buffer.append_printf (" codes=\"%s\"", edomain.name);
541 write_symbol_attributes (edomain);
542 buffer.append_printf (">\n");
544 write_annotations (edomain);
546 buffer.append_printf ("</errordomain>\n");
548 write_indent ();
549 buffer.append_printf ("<enumeration name=\"%s\"", edomain.name);
550 write_ctype_attributes (edomain);
551 buffer.append_printf (">\n");
552 indent++;
554 enum_value = 0;
555 hierarchy.insert (0, edomain);
556 edomain.accept_children (this);
557 hierarchy.remove_at (0);
559 indent--;
560 write_indent ();
561 buffer.append_printf ("</enumeration>\n");
563 visit_deferred ();
566 public override void visit_error_code (ErrorCode ecode) {
567 write_indent ();
568 buffer.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode.name.down (), ecode.get_cname ());
569 if (ecode.value != null) {
570 string value = literal_expression_to_value_string (ecode.value);
571 buffer.append_printf (" value=\"%s\"", value);
572 } else {
573 buffer.append_printf (" value=\"%d\"", enum_value++);
575 write_symbol_attributes (ecode);
576 buffer.append_printf ("/>\n");
579 public override void visit_constant (Constant c) {
580 if (c.external_package) {
581 return;
584 if (!check_accessibility (c)) {
585 return;
588 //TODO Add better constant evaluation
589 var initializer = c.value;
590 string value = literal_expression_to_value_string (initializer);
592 write_indent ();
593 buffer.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", c.name, c.get_cname ());
594 buffer.append_printf (" value=\"%s\"", value);
595 write_symbol_attributes (c);
596 buffer.append_printf (">\n");
597 indent++;
599 write_type (initializer.value_type);
601 indent--;
602 write_indent ();
603 buffer.append_printf ("</constant>\n");
606 public override void visit_field (Field f) {
607 if (f.external_package) {
608 return;
611 if (!check_accessibility (f)) {
612 return;
615 write_indent ();
616 buffer.append_printf ("<field name=\"%s\"", f.get_cname ());
617 if (f.variable_type.nullable) {
618 buffer.append_printf (" allow-none=\"1\"");
620 write_symbol_attributes (f);
621 buffer.append_printf (">\n");
622 indent++;
624 write_annotations (f);
626 write_type (f.variable_type);
628 indent--;
629 write_indent ();
630 buffer.append_printf ("</field>\n");
633 private void write_implicit_params (DataType type, ref int index, bool has_array_length, string name, ParameterDirection direction) {
634 if (type is ArrayType && has_array_length) {
635 var int_type = new IntegerType (CodeContext.get ().root.scope.lookup ("int") as Struct);
636 write_param_or_return (int_type, true, ref index, has_array_length, "%s_length1".printf (name), direction);
637 } else if (type is DelegateType) {
638 var data_type = new PointerType (new VoidType ());
639 write_param_or_return (data_type, true, ref index, false, "%s_target".printf (name), direction);
640 if (type.value_owned) {
641 var notify_type = new DelegateType (CodeContext.get ().root.scope.lookup ("GLib").scope.lookup ("DestroyNotify") as Delegate);
642 write_param_or_return (notify_type, true, ref index, false, "%s_target_destroy_notify".printf (name), direction);
647 private void write_params_and_return (List<Parameter> params, DataType? return_type, bool return_array_length, bool constructor = false, DataType? instance_type = null, bool user_data = false) {
648 int last_index = 0;
649 bool ret_is_struct = return_type != null && return_type.is_real_non_null_struct_type ();
650 if (params.size != 0 || instance_type != null || (return_type is ArrayType && return_array_length) || (return_type is DelegateType) || ret_is_struct) {
651 write_indent ();
652 buffer.append_printf ("<parameters>\n");
653 indent++;
654 int index = 0;
656 if (instance_type != null) {
657 write_param_or_return (instance_type, true, ref index, false, "self");
660 foreach (Parameter param in params) {
661 write_param_or_return (param.variable_type, true, ref index, !param.no_array_length, param.name, param.direction);
663 write_implicit_params (param.variable_type, ref index, !param.no_array_length, param.name, param.direction);
666 if (ret_is_struct) {
667 // struct returns are converted to parameters
668 write_param_or_return (return_type, true, ref index, false, "result", ParameterDirection.OUT, constructor, true);
669 } else {
670 write_implicit_params (return_type, ref index, return_array_length, "result", ParameterDirection.OUT);
673 last_index = index - 1;
675 if (user_data) {
676 write_indent ();
677 buffer.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index);
678 indent++;
679 write_indent ();
680 buffer.append_printf ("<type name=\"gpointer\" c:type=\"void*\"/>\n");
681 indent--;
682 write_indent ();
683 buffer.append_printf ("</parameter>\n");
686 indent--;
687 write_indent ();
688 buffer.append_printf ("</parameters>\n");
691 if (return_type != null && !ret_is_struct) {
692 write_param_or_return (return_type, false, ref last_index, return_array_length, null, ParameterDirection.IN, constructor);
693 } else if (ret_is_struct) {
694 write_param_or_return (new VoidType (), false, ref last_index, false, null, ParameterDirection.IN);
698 public override void visit_delegate (Delegate cb) {
699 if (cb.external_package) {
700 return;
703 if (!check_accessibility (cb)) {
704 return;
707 write_indent ();
708 buffer.append_printf ("<callback name=\"%s\"", cb.name);
709 buffer.append_printf (" c:type=\"%s\"", cb.get_cname ());
710 if (cb.tree_can_fail) {
711 buffer.append_printf (" throws=\"1\"");
713 write_symbol_attributes (cb);
714 buffer.append_printf (">\n");
715 indent++;
717 write_annotations (cb);
719 write_params_and_return (cb.get_parameters (), cb.return_type, !cb.no_array_length, false, null, cb.has_target);
721 indent--;
722 write_indent ();
723 buffer.append_printf ("</callback>\n");
726 public override void visit_method (Method m) {
727 if (m.external_package) {
728 return;
731 // don't write interface implementation unless it's an abstract or virtual method
732 if (!check_accessibility (m) || m.overrides || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
733 return;
736 string tag_name = "method";
737 var parent = this.hierarchy.get (0);
738 if (parent is Enum) {
739 deferred.add (m);
740 return;
743 if (parent is Namespace || m.binding == MemberBinding.STATIC || parent != m.parent_symbol) {
744 tag_name = "function";
747 write_signature (m, tag_name);
749 if (m.is_abstract || m.is_virtual) {
750 write_signature (m, "virtual-method", false);
754 private void write_signature (Method m, string tag_name, bool instance = false) {
755 var parent = this.hierarchy.get (0);
756 string name;
757 if (m.parent_symbol != parent) {
758 instance = false;
759 name = m.get_cname ();
760 var parent_prefix = parent.get_lower_case_cprefix ();
761 if (name.has_prefix (parent_prefix)) {
762 name = name.substring (parent_prefix.length);
764 } else {
765 name = m.name;
768 if (m.coroutine) {
769 string finish_name = name;
770 if (finish_name.has_suffix ("_async")) {
771 finish_name = finish_name.substring (0, finish_name.length - "_async".length);
773 finish_name += "_finish";
774 do_write_signature (m, tag_name, instance, name, m.get_cname (), m.get_async_begin_parameters (), new VoidType (), false);
775 do_write_signature (m, tag_name, instance, finish_name, m.get_finish_cname (), m.get_async_end_parameters (), m.return_type, m.tree_can_fail);
776 } else {
777 do_write_signature (m, tag_name, instance, name, m.get_cname (), m.get_parameters (), m.return_type, m.tree_can_fail);
781 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) {
782 write_indent ();
783 buffer.append_printf ("<%s name=\"%s\"", tag_name, name);
784 if (tag_name == "virtual-method") {
785 buffer.append_printf (" invoker=\"%s\"", name);
786 } else if (tag_name == "callback") {
787 /* this is only used for vfuncs */
788 buffer.append_printf (" c:type=\"%s\"", name);
789 } else {
790 buffer.append_printf (" c:identifier=\"%s\"", cname);
792 if (can_fail) {
793 buffer.append_printf (" throws=\"1\"");
795 write_symbol_attributes (m);
796 buffer.append_printf (">\n");
797 indent++;
799 write_annotations (m);
801 DataType instance_type = null;
802 if (instance) {
803 instance_type = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
806 write_params_and_return (params, return_type, !m.no_array_length, false, instance_type);
808 indent--;
809 write_indent ();
810 buffer.append_printf ("</%s>\n", tag_name);
813 public override void visit_creation_method (CreationMethod m) {
814 if (m.external_package) {
815 return;
818 if (!check_accessibility (m)) {
819 return;
822 write_indent ();
824 bool is_struct = m.parent_symbol is Struct;
825 // GI doesn't like constructors that return void type
826 string tag_name = is_struct ? "function" : "constructor";
828 if (m.parent_symbol is Class && m == ((Class)m.parent_symbol).default_construction_method ||
829 m.parent_symbol is Struct && m == ((Struct)m.parent_symbol).default_construction_method) {
830 string m_name = is_struct ? "init" : "new";
831 buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m_name, m.get_cname ());
832 } else {
833 buffer.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name, m.name, m.get_cname ());
836 if (m.tree_can_fail) {
837 buffer.append_printf (" throws=\"1\"");
839 buffer.append_printf (">\n");
840 indent++;
842 write_annotations (m);
845 var datatype = CCodeBaseModule.get_data_type_for_symbol ((TypeSymbol) m.parent_symbol);
846 write_params_and_return (m.get_parameters (), datatype, false, true);
848 indent--;
849 write_indent ();
850 buffer.append_printf ("</%s>\n", tag_name);
853 public override void visit_property (Property prop) {
854 if (!check_accessibility (prop) || prop.overrides || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
855 return;
858 write_indent ();
859 buffer.append_printf ("<property name=\"%s\"", prop.get_canonical_name ());
860 if (prop.get_accessor == null) {
861 buffer.append_printf (" readable=\"0\"");
863 if (prop.set_accessor != null) {
864 buffer.append_printf (" writable=\"1\"");
865 if (prop.set_accessor.construction) {
866 if (!prop.set_accessor.writable) {
867 buffer.append_printf (" construct-only=\"1\"");
868 } else {
869 buffer.append_printf (" construct=\"1\"");
873 write_symbol_attributes (prop);
874 buffer.append_printf (">\n");
875 indent++;
877 write_annotations (prop);
879 write_type (prop.property_type);
881 indent--;
882 write_indent ();
883 buffer.append_printf ("</property>\n");
886 public override void visit_signal (Signal sig) {
887 if (!check_accessibility (sig)) {
888 return;
891 write_indent ();
892 buffer.append_printf ("<glib:signal name=\"%s\"", sig.get_cname ());
893 write_symbol_attributes (sig);
894 buffer.append_printf (">\n");
895 indent++;
897 write_annotations (sig);
899 write_params_and_return (sig.get_parameters (), sig.return_type, false);
901 indent--;
902 write_indent ();
903 buffer.append_printf ("</glib:signal>\n");
906 private void write_indent () {
907 int i;
909 for (i = 0; i < indent; i++) {
910 buffer.append_c ('\t');
914 private void write_indent_stream () {
915 int i;
917 for (i = 0; i < indent; i++) {
918 stream.putc ('\t');
923 private void write_param_or_return (DataType type, bool is_parameter, ref int index, bool has_array_length, string? name = null, ParameterDirection direction = ParameterDirection.IN, bool constructor = false, bool caller_allocates = false) {
924 write_indent ();
925 string tag = is_parameter ? "parameter" : "return-value";
926 buffer.append_printf ("<%s", tag);
927 if (name != null) {
928 buffer.append_printf (" name=\"%s\"", name);
930 if (direction == ParameterDirection.REF) {
931 buffer.append_printf (" direction=\"inout\"");
932 } else if (direction == ParameterDirection.OUT) {
933 buffer.append_printf (" direction=\"out\"");
936 DelegateType delegate_type = type as DelegateType;
938 if ((type.value_owned && delegate_type == null) || constructor) {
939 buffer.append_printf (" transfer-ownership=\"full\"");
940 } else {
941 buffer.append_printf (" transfer-ownership=\"none\"");
943 if (caller_allocates) {
944 buffer.append_printf (" caller-allocates=\"1\"");
946 if (type.nullable) {
947 buffer.append_printf (" allow-none=\"1\"");
950 if (delegate_type != null && delegate_type.delegate_symbol.has_target) {
951 int closure_index = is_parameter ?
952 index + 1 : (type.value_owned ? index - 1 : index);
953 buffer.append_printf (" closure=\"%i\"", closure_index);
954 if (type.value_owned) {
955 buffer.append_printf (" destroy=\"%i\"", closure_index + 1);
959 buffer.append_printf (">\n");
960 indent++;
962 int length_param_index = -1;
963 if (has_array_length) {
964 length_param_index = is_parameter ? index + 1 : index;
966 write_type (type, length_param_index);
968 indent--;
969 write_indent ();
970 buffer.append_printf ("</%s>\n", tag);
971 index++;
974 private void write_ctype_attributes (TypeSymbol symbol, string suffix = "") {
975 buffer.append_printf (" c:type=\"%s%s\"", symbol.get_cname (), suffix);
978 private void write_gtype_attributes (TypeSymbol symbol) {
979 write_ctype_attributes(symbol);
980 buffer.append_printf (" glib:type-name=\"%s\"", symbol.get_cname ());
981 buffer.append_printf (" glib:get-type=\"%sget_type\"", symbol.get_lower_case_cprefix ());
984 private void write_type (DataType type, int index = -1) {
985 if (type is ArrayType) {
986 var array_type = (ArrayType) type;
988 write_indent ();
989 buffer.append_printf ("<array");
990 if (array_type.fixed_length) {
991 buffer.append_printf (" fixed-size=\"%i\"", array_type.length);
992 } else if (index != -1) {
993 buffer.append_printf (" length=\"%i\"", index);
995 buffer.append_printf (">\n");
996 indent++;
998 write_type (array_type.element_type);
1000 indent--;
1001 write_indent ();
1002 buffer.append_printf ("</array>\n");
1003 } else if (type is VoidType) {
1004 write_indent ();
1005 buffer.append_printf ("<type name=\"none\"/>\n");
1006 } else if (type is PointerType) {
1007 write_indent ();
1008 buffer.append_printf ("<type name=\"gpointer\" c:type=\"%s\"/>\n", type.get_cname ());
1009 } else if (type.data_type != null) {
1010 write_indent ();
1011 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"", gi_type_name (type.data_type), type.get_cname ());
1013 List<DataType> type_arguments = type.get_type_arguments ();
1014 if (type_arguments.size == 0) {
1015 buffer.append_printf ("/>\n");
1016 } else {
1017 buffer.append_printf (">\n");
1018 indent++;
1020 foreach (DataType type_argument in type_arguments) {
1021 write_type (type_argument);
1024 indent--;
1025 write_indent ();
1026 buffer.append_printf ("</type>\n");
1028 } else if (type is DelegateType) {
1029 var deleg_type = (DelegateType) type;
1030 write_indent ();
1031 buffer.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (deleg_type.delegate_symbol), type.get_cname ());
1032 } else if (type is GenericType) {
1033 // generic type parameters not supported in GIR
1034 write_indent ();
1035 buffer.append ("<type name=\"gpointer\" c:type=\"gpointer\"/>\n");
1036 } else {
1037 write_indent ();
1038 buffer.append_printf ("<type name=\"%s\"/>\n", type.to_string ());
1042 private void write_annotations (CodeNode node) {
1043 foreach (Attribute attr in node.attributes) {
1044 string name = camel_case_to_canonical (attr.name);
1045 foreach (string arg_name in attr.args.get_keys ()) {
1046 string value = attr.args.get (arg_name);
1047 if (value.has_prefix ("\"")) {
1048 // eval string
1049 value = attr.get_string (arg_name);
1052 write_indent ();
1053 buffer.append_printf ("<annotation key=\"%s.%s\" value=\"%s\"/>\n",
1054 name, camel_case_to_canonical (arg_name), value);
1059 private string gi_type_name (TypeSymbol type_symbol) {
1060 Symbol parent = type_symbol.parent_symbol;
1061 if (parent is Namespace) {
1062 Namespace ns = parent as Namespace;
1063 if (ns.gir_name != null) {
1064 if (type_symbol.source_reference.file.gir_namespace != null) {
1065 GIRNamespace external = GIRNamespace (type_symbol.source_reference.file.gir_namespace, type_symbol.source_reference.file.gir_version);
1066 if (!externals.contains (external)) {
1067 externals.add (external);
1069 return "%s.%s".printf (type_symbol.source_reference.file.gir_namespace, type_symbol.gir_name);
1070 } else {
1071 unannotated_namespaces.add(ns);
1076 return type_symbol.get_full_gir_name();
1079 private string? literal_expression_to_value_string (Expression literal) {
1080 if (literal is StringLiteral) {
1081 var lit = literal as StringLiteral;
1082 if (lit != null) {
1083 return Markup.escape_text (lit.eval ());
1085 } else if (literal is CharacterLiteral) {
1086 return "%c".printf ((char) ((CharacterLiteral) literal).get_char ());
1087 } else if (literal is BooleanLiteral) {
1088 return ((BooleanLiteral) literal).value ? "true" : "false";
1089 } else if (literal is RealLiteral) {
1090 return ((RealLiteral) literal).value;
1091 } else if (literal is IntegerLiteral) {
1092 return ((IntegerLiteral) literal).value;
1093 } else if (literal is UnaryExpression) {
1094 var unary = (UnaryExpression) literal;
1095 if (unary.operator == UnaryOperator.MINUS) {
1096 if (unary.inner is RealLiteral) {
1097 return "-" + ((RealLiteral) unary.inner).value;
1098 } else if (unary.inner is IntegerLiteral) {
1099 return "-" + ((IntegerLiteral) unary.inner).value;
1103 return null;
1106 private string camel_case_to_canonical (string name) {
1107 string[] parts = Symbol.camel_case_to_lower_case (name).split ("_");
1108 return string.joinv ("-", parts);
1111 private bool check_accessibility (Symbol sym) {
1112 if (sym.access == SymbolAccessibility.PUBLIC ||
1113 sym.access == SymbolAccessibility.PROTECTED) {
1114 return true;
1117 return false;