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
20 * Jürg Billeter <j@bitron.ch>
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
) {
39 protected virtual string?
get_struct_comment (Struct st
) {
43 protected virtual string?
get_enum_comment (Enum en
) {
47 protected virtual string?
get_class_comment (Class c
) {
51 protected virtual string?
get_error_code_comment (ErrorCode ecode
) {
55 protected virtual string?
get_enum_value_comment (EnumValue ev
) {
59 protected virtual string?
get_constant_comment (Constant c
) {
63 protected virtual string?
get_error_domain_comment (ErrorDomain edomain
) {
67 protected virtual string?
get_field_comment (Field f
) {
71 protected virtual string?
get_delegate_comment (Delegate cb
) {
75 protected virtual string?
get_method_comment (Method m
) {
79 protected virtual string?
get_property_comment (Property prop
) {
83 protected virtual string?
get_delegate_return_comment (Delegate cb
) {
87 protected virtual string?
get_signal_return_comment (Signal sig
) {
91 protected virtual string?
get_method_return_comment (Method m
) {
95 protected virtual string?
get_signal_comment (Signal sig
) {
99 protected virtual string?
get_parameter_comment (Parameter param
) {
103 StringBuilder buffer
= new
StringBuilder();
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
>();
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
;
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
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
);
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
));
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");
183 stream
.puts (buffer
.str
);
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) {
204 buffer
.append ("<doc xml:whitespace=\"preserve\">");
205 buffer
.append (comment
);
206 buffer
.append ("</doc>\n");
210 private void write_package (string package
) {
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
) {
235 buffer
.append_printf ("<c:include name=\"%s\"/>\n", name
);
238 public override void visit_namespace (Namespace ns
) {
239 if (ns
.external_package
) {
243 if (ns
.name
== null) {
245 hierarchy
.insert (0, ns
);
246 ns
.accept_children (this
);
247 hierarchy
.remove_at (0);
251 if (ns
.parent_symbol
.name
!= null) {
252 ns
.accept_children (this
);
256 write_c_includes (ns
);
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");
270 write_annotations (ns
);
272 hierarchy
.insert (0, ns
);
273 ns
.accept_children (this
);
274 hierarchy
.remove_at (0);
278 buffer
.append_printf ("</namespace>\n");
279 our_namespaces
.add(ns
);
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
) {
301 if (!check_accessibility (cl
)) {
305 if (!(hierarchy
[0] is Namespace
)) {
310 if (cl
.is_subtype_of (gobject_type
)) {
311 string gtype_struct_name
= get_gir_name (cl
) + "Class";
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");
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
) {
332 buffer
.append_printf ("<implements name=\"%s\"/>\n", gi_type_name (object_type
.type_symbol
));
336 write_annotations (cl
);
339 buffer
.append_printf ("<field name=\"parent_instance\">\n");
342 buffer
.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (cl
.base_class
), get_ccode_name (cl
.base_class
));
345 buffer
.append_printf("</field>\n");
348 buffer
.append_printf ("<field name=\"priv\">\n");
351 buffer
.append_printf ("<type name=\"%sPrivate\" c:type=\"%sPrivate*\"/>\n", get_gir_name (cl
), get_ccode_name (cl
));
354 buffer
.append_printf("</field>\n");
356 hierarchy
.insert (0, cl
);
357 cl
.accept_children (this
);
358 hierarchy
.remove_at (0);
362 buffer
.append_printf ("</class>\n");
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");
372 buffer
.append_printf ("<field name=\"parent_class\">\n");
375 buffer
.append_printf ("<type name=\"%sClass\" c:type=\"%sClass\"/>\n", gi_type_name (cl
.base_class
), get_ccode_name (cl
.base_class
));
378 buffer
.append_printf ("</field>\n");
380 foreach (Method m
in cl
.get_methods ()) {
381 if (m
.is_abstract
|| m
.is_virtual
) {
383 string finish_name
= m
.name
;
384 if (finish_name
.has_suffix ("_async")) {
385 finish_name
= finish_name
.substring (0, finish_name
.length
- "_async".length
);
387 finish_name
+= "_finish";
390 buffer
.append_printf("<field name=\"%s\">\n", m
.name
);
392 do_write_signature (m
, "callback", true, m
.name
, get_ccode_name (m
), m
.get_async_begin_parameters (), new
VoidType (), false, false);
395 buffer
.append_printf ("</field>\n");
398 buffer
.append_printf("<field name=\"%s\">\n", finish_name
);
400 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);
403 buffer
.append_printf ("</field>\n");
406 buffer
.append_printf("<field name=\"%s\">\n", m
.name
);
408 do_write_signature (m
, "callback", true, m
.name
, get_ccode_name (m
), m
.get_parameters (), m
.return_type
, m
.tree_can_fail
, false);
411 buffer
.append_printf ("</field>\n");
416 foreach (Signal sig
in cl
.get_signals ()) {
417 if (sig
.default_handler
!= null) {
419 buffer
.append_printf ("<field name=\"%s\">\n", get_ccode_lower_case_name (sig
));
421 write_signature (sig
.default_handler
, "callback", false, true);
424 buffer
.append_printf ("</field>\n");
430 buffer
.append_printf ("</record>\n");
433 buffer
.append_printf ("<record name=\"%sPrivate\" c:type=\"%sPrivate\" disguised=\"1\"/>\n", get_gir_name (cl
), get_ccode_name (cl
));
436 buffer
.append_printf ("<record name=\"%s\"", get_gir_name (cl
));
437 write_symbol_attributes (cl
);
438 buffer
.append_printf (">\n");
441 write_doc (get_class_comment (cl
));
443 write_annotations (cl
);
445 hierarchy
.insert (0, cl
);
446 cl
.accept_children (this
);
447 hierarchy
.remove_at (0);
451 buffer
.append_printf ("</record>\n");
457 public override void visit_struct (Struct st
) {
458 if (st
.external_package
) {
462 if (!check_accessibility (st
)) {
466 if (!(hierarchy
[0] is Namespace
)) {
472 buffer
.append_printf ("<record name=\"%s\"", get_gir_name (st
));
473 write_symbol_attributes (st
);
474 buffer
.append_printf (">\n");
477 write_doc (get_struct_comment (st
));
479 write_annotations (st
);
481 hierarchy
.insert (0, st
);
482 st
.accept_children (this
);
483 hierarchy
.remove_at (0);
487 buffer
.append_printf ("</record>\n");
492 public override void visit_interface (Interface iface
) {
493 if (iface
.external_package
) {
497 if (!check_accessibility (iface
)) {
501 if (!(hierarchy
[0] is Namespace
)) {
502 deferred
.add (iface
);
506 string gtype_struct_name
= iface
.name
+ "Iface";
509 buffer
.append_printf ("<interface name=\"%s\"", get_gir_name (iface
));
510 write_gtype_attributes (iface
);
511 buffer
.append_printf (" glib:type-struct=\"%s\"", gtype_struct_name
);
512 write_symbol_attributes (iface
);
513 buffer
.append_printf (">\n");
516 write_doc (get_interface_comment (iface
));
518 // write prerequisites
519 if (iface
.get_prerequisites ().size
> 0) {
520 foreach (DataType base_type
in iface
.get_prerequisites ()) {
522 buffer
.append_printf ("<prerequisite name=\"%s\"/>\n", gi_type_name (((ObjectType
) base_type
).type_symbol
));
526 write_annotations (iface
);
528 hierarchy
.insert (0, iface
);
529 iface
.accept_children (this
);
530 hierarchy
.remove_at (0);
534 buffer
.append_printf ("</interface>\n");
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");
544 buffer
.append_printf ("<field name=\"parent_iface\">\n");
547 buffer
.append_printf ("<type name=\"GObject.TypeInterface\" c:type=\"GTypeInterface\"/>\n");
550 buffer
.append_printf ("</field>\n");
552 foreach (Method m
in iface
.get_methods ()) {
553 if (m
.is_abstract
|| m
.is_virtual
) {
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";
562 buffer
.append_printf("<field name=\"%s\">\n", m
.name
);
564 do_write_signature (m
, "callback", true, m
.name
, get_ccode_name (m
), m
.get_async_begin_parameters (), new
VoidType (), false, false);
567 buffer
.append_printf ("</field>\n");
570 buffer
.append_printf("<field name=\"%s\">\n", finish_name
);
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);
575 buffer
.append_printf ("</field>\n");
578 buffer
.append_printf("<field name=\"%s\">\n", m
.name
);
580 do_write_signature (m
, "callback", true, m
.name
, get_ccode_name (m
), m
.get_parameters (), m
.return_type
, m
.tree_can_fail
, false);
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 ();
593 buffer
.append_printf("<field name=\"%s\">\n", m
.name
);
595 do_write_signature (m
, "callback", true, m
.name
, get_ccode_name (m
), m
.get_parameters (), m
.return_type
, m
.tree_can_fail
, false);
598 buffer
.append_printf ("</field>\n");
601 if (prop
.set_accessor
!= null) {
602 var m
= prop
.set_accessor
.get_method ();
604 buffer
.append_printf("<field name=\"%s\">\n", m
.name
);
606 do_write_signature (m
, "callback", true, m
.name
, get_ccode_name (m
), m
.get_parameters (), m
.return_type
, m
.tree_can_fail
, false);
609 buffer
.append_printf ("</field>\n");
616 buffer
.append_printf ("</record>\n");
621 private void visit_deferred () {
622 var nodes
= this
.deferred
;
623 this
.deferred
= new Vala
.ArrayList
<Vala
.CodeNode
>();
625 foreach (var node
in nodes
) {
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
) {
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
);
649 public override void visit_enum (Enum en
) {
650 if (en
.external_package
) {
654 if (!check_accessibility (en
)) {
658 if (!(hierarchy
[0] is Namespace
)) {
663 string element_name
= (en
.is_flags
) ?
"bitfield" : "enumeration";
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");
672 write_doc (get_enum_comment (en
));
674 write_annotations (en
);
677 hierarchy
.insert (0, en
);
678 en
.accept_children (this
);
679 hierarchy
.remove_at (0);
683 buffer
.append_printf ("</%s>\n", element_name
);
688 private int enum_value
;
690 public override void visit_enum_value (EnumValue ev
) {
692 var en
= (Enum
) hierarchy
[0];
693 buffer
.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ev
.name
.down (), get_ccode_name (ev
));
694 if (ev
.value
!= null) {
695 string value
= literal_expression_to_value_string (ev
.value
);
696 buffer
.append_printf (" value=\"%s\"", value
);
699 buffer
.append_printf (" value=\"%d\"", 1 << enum_value
++);
701 buffer
.append_printf (" value=\"%d\"", enum_value
++);
704 write_symbol_attributes (ev
);
706 string? comment
= get_enum_value_comment (ev
);
707 if (comment
== null) {
708 buffer
.append_printf ("/>\n");
710 buffer
.append_printf (">\n");
717 buffer
.append_printf ("</member>\n");
721 public override void visit_error_domain (ErrorDomain edomain
) {
722 if (edomain
.external_package
) {
726 if (!check_accessibility (edomain
)) {
731 buffer
.append_printf ("<enumeration name=\"%s\"", edomain
.name
);
732 write_ctype_attributes (edomain
);
733 buffer
.append_printf (" glib:error-domain=\"%s\"", get_ccode_quark_name (edomain
));
734 buffer
.append_printf (">\n");
737 write_doc (get_error_domain_comment (edomain
));
740 hierarchy
.insert (0, edomain
);
741 edomain
.accept_children (this
);
742 hierarchy
.remove_at (0);
746 buffer
.append_printf ("</enumeration>\n");
751 public override void visit_error_code (ErrorCode ecode
) {
753 buffer
.append_printf ("<member name=\"%s\" c:identifier=\"%s\"", ecode
.name
.down (), get_ccode_name (ecode
));
754 if (ecode
.value
!= null) {
755 string value
= literal_expression_to_value_string (ecode
.value
);
756 buffer
.append_printf (" value=\"%s\"", value
);
758 buffer
.append_printf (" value=\"%d\"", enum_value
++);
760 write_symbol_attributes (ecode
);
762 string? comment
= get_error_code_comment (ecode
);
763 if (comment
== null) {
764 buffer
.append_printf ("/>\n");
766 buffer
.append_printf (">\n");
773 buffer
.append_printf ("</member>\n");
777 public override void visit_constant (Constant c
) {
778 if (c
.external_package
) {
782 if (!check_accessibility (c
)) {
786 //TODO Add better constant evaluation
787 var initializer
= c
.value
;
788 string value
= literal_expression_to_value_string (initializer
);
791 buffer
.append_printf ("<constant name=\"%s\" c:identifier=\"%s\"", c
.name
, get_ccode_name (c
));
792 buffer
.append_printf (" value=\"%s\"", value
);
793 write_symbol_attributes (c
);
794 buffer
.append_printf (">\n");
797 write_doc (get_constant_comment (c
));
799 write_type (initializer
.value_type
);
803 buffer
.append_printf ("</constant>\n");
806 public override void visit_field (Field f
) {
807 if (f
.external_package
) {
811 if (!check_accessibility (f
)) {
816 buffer
.append_printf ("<field name=\"%s\"", get_ccode_name (f
));
817 if (f
.variable_type
.nullable
) {
818 buffer
.append_printf (" allow-none=\"1\"");
820 write_symbol_attributes (f
);
821 buffer
.append_printf (">\n");
824 write_doc (get_field_comment (f
));
826 write_annotations (f
);
828 write_type (f
.variable_type
);
832 buffer
.append_printf ("</field>\n");
835 private void write_implicit_params (DataType? type
, ref int index
, bool has_array_length
, string? name
, ParameterDirection direction
) {
836 if (type is ArrayType
&& has_array_length
) {
837 var int_type
= new
IntegerType (CodeContext
.get ().root
.scope
.lookup ("int") as Struct
);
838 for (var i
= 0; i
< ((ArrayType
) type
).rank
; i
++) {
839 write_param_or_return (int_type
, true, ref index
, has_array_length
, "%s_length%i".printf (name
, i
+ 1), null, direction
);
841 } else if (type is DelegateType
) {
842 var deleg_type
= (DelegateType
) type
;
843 if (deleg_type
.delegate_symbol
.has_target
) {
844 var data_type
= new
PointerType (new
VoidType ());
845 write_param_or_return (data_type
, true, ref index
, false, "%s_target".printf (name
), null, direction
);
846 if (deleg_type
.is_disposable ()) {
847 var notify_type
= new
DelegateType (CodeContext
.get ().root
.scope
.lookup ("GLib").scope
.lookup ("DestroyNotify") as Delegate
);
848 write_param_or_return (notify_type
, true, ref index
, false, "%s_target_destroy_notify".printf (name
), null, direction
);
854 void skip_implicit_params (DataType? type
, ref int index
, bool has_array_length
) {
855 if (type is ArrayType
&& has_array_length
) {
856 index
+= ((ArrayType
) type
).rank
;
857 } else if (type is DelegateType
) {
859 var deleg_type
= (DelegateType
) type
;
860 if (deleg_type
.is_disposable ()) {
866 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 bool ret_is_struct
= return_type
!= null && return_type
.is_real_non_null_struct_type ();
870 if (params
.size
!= 0 || instance_type
!= null || (return_type is ArrayType
&& return_array_length
) || (return_type is DelegateType
) || ret_is_struct
) {
873 if (instance_type
!= null) {
877 foreach (Parameter param
in params
) {
880 skip_implicit_params (param
.variable_type
, ref index
, get_ccode_array_length (param
));
886 skip_implicit_params (return_type
, ref index
, return_array_length
);
887 if (return_type is ArrayType
&& return_array_length
) {
888 index
-= ((ArrayType
) return_type
).rank
- 1;
892 last_index
= index
- 1;
895 if (return_type
!= null && !ret_is_struct
) {
896 write_param_or_return (return_type
, false, ref last_index
, return_array_length
, null, return_comment
, ParameterDirection
.IN
, constructor
);
897 } else if (ret_is_struct
) {
898 write_param_or_return (new
VoidType (), false, ref last_index
, false, null, return_comment
, ParameterDirection
.IN
);
901 if (params
.size
!= 0 || instance_type
!= null || (return_type is ArrayType
&& return_array_length
) || (return_type is DelegateType
) || ret_is_struct
) {
903 buffer
.append_printf ("<parameters>\n");
907 if (instance_type
!= null) {
908 write_param_or_return (instance_type
, true, ref index
, false, "self");
911 foreach (Parameter param
in params
) {
912 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
);
914 write_implicit_params (param
.variable_type
, ref index
, get_ccode_array_length (param
), param
.name
, param
.direction
);
918 // struct returns are converted to parameters
919 write_param_or_return (return_type
, true, ref index
, false, "result", return_comment
, ParameterDirection
.OUT
, constructor
, true);
921 write_implicit_params (return_type
, ref index
, return_array_length
, "result", ParameterDirection
.OUT
);
926 buffer
.append_printf ("<parameter name=\"user_data\" transfer-ownership=\"none\" closure=\"%d\">\n", index
);
929 buffer
.append_printf ("<type name=\"gpointer\" c:type=\"void*\"/>\n");
932 buffer
.append_printf ("</parameter>\n");
937 buffer
.append_printf ("</parameters>\n");
941 public override void visit_delegate (Delegate cb
) {
942 if (cb
.external_package
) {
946 if (!check_accessibility (cb
)) {
951 buffer
.append_printf ("<callback name=\"%s\"", cb
.name
);
952 buffer
.append_printf (" c:type=\"%s\"", get_ccode_name (cb
));
953 if (cb
.tree_can_fail
) {
954 buffer
.append_printf (" throws=\"1\"");
956 write_symbol_attributes (cb
);
957 buffer
.append_printf (">\n");
960 write_doc (get_delegate_comment (cb
));
962 write_annotations (cb
);
964 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
);
968 buffer
.append_printf ("</callback>\n");
971 public override void visit_method (Method m
) {
972 if (m
.external_package
) {
976 // don't write interface implementation unless it's an abstract or virtual method
977 if (!check_accessibility (m
) || m
.overrides
|| (m
.base_interface_method
!= null && !m
.is_abstract
&& !m
.is_virtual
)) {
981 string tag_name
= "method";
982 var parent
= this
.hierarchy
.get (0);
983 if (parent is Enum
) {
988 if (parent is Namespace
|| m
.binding
== MemberBinding
.STATIC
|| parent
!= m
.parent_symbol
) {
989 tag_name
= "function";
992 write_signature (m
, tag_name
, true);
994 if (m
.is_abstract
|| m
.is_virtual
) {
995 write_signature (m
, "virtual-method", true, false);
999 bool is_type_introspectable (DataType type
) {
1000 // gobject-introspection does not currently support va_list parameters
1001 if (get_ccode_name (type
) == "va_list") {
1008 bool is_introspectable (Method m
) {
1009 if (!is_type_introspectable (m
.return_type
)) {
1012 foreach (var param
in m
.get_parameters ()) {
1013 if (param
.ellipsis
|| !is_type_introspectable (param
.variable_type
)) {
1020 private void write_signature (Method m
, string tag_name
, bool write_doc
, bool instance
= false) {
1021 var parent
= this
.hierarchy
.get (0);
1023 if (m
.parent_symbol
!= parent
) {
1025 name
= get_ccode_name (m
);
1026 var parent_prefix
= get_ccode_lower_case_prefix (parent
);
1027 if (name
.has_prefix (parent_prefix
)) {
1028 name
= name
.substring (parent_prefix
.length
);
1035 string finish_name
= name
;
1036 if (finish_name
.has_suffix ("_async")) {
1037 finish_name
= finish_name
.substring (0, finish_name
.length
- "_async".length
);
1039 finish_name
+= "_finish";
1040 do_write_signature (m
, tag_name
, instance
, name
, get_ccode_name (m
), m
.get_async_begin_parameters (), new
VoidType (), false, true);
1041 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);
1043 do_write_signature (m
, tag_name
, instance
, name
, get_ccode_name (m
), m
.get_parameters (), m
.return_type
, m
.tree_can_fail
, true);
1047 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
) {
1049 buffer
.append_printf ("<%s name=\"%s\"", tag_name
, name
);
1050 if (tag_name
== "virtual-method") {
1051 buffer
.append_printf (" invoker=\"%s\"", name
);
1052 } else if (tag_name
== "callback") {
1053 /* this is only used for vfuncs */
1054 buffer
.append_printf (" c:type=\"%s\"", name
);
1056 buffer
.append_printf (" c:identifier=\"%s\"", cname
);
1059 buffer
.append_printf (" throws=\"1\"");
1061 write_symbol_attributes (m
);
1062 if (!is_introspectable (m
)) {
1063 buffer
.append_printf (" introspectable=\"0\"");
1065 buffer
.append_printf (">\n");
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 write_annotations (m
);
1076 DataType instance_type
= null;
1078 instance_type
= CCodeBaseModule
.get_data_type_for_symbol ((TypeSymbol
) m
.parent_symbol
);
1081 write_params_and_return (params
, return_type
, get_ccode_array_length (m
), return_comment
, false, instance_type
);
1085 buffer
.append_printf ("</%s>\n", tag_name
);
1088 public override void visit_creation_method (CreationMethod m
) {
1089 if (m
.external_package
) {
1093 if (!check_accessibility (m
)) {
1097 if (m
.parent_symbol is Class
&& ((Class
) m
.parent_symbol
).is_abstract
) {
1103 bool is_struct
= m
.parent_symbol is Struct
;
1104 // GI doesn't like constructors that return void type
1105 string tag_name
= is_struct ?
"function" : "constructor";
1107 if (m
.parent_symbol is Class
&& m
== ((Class
)m
.parent_symbol
).default_construction_method
||
1108 m
.parent_symbol is Struct
&& m
== ((Struct
)m
.parent_symbol
).default_construction_method
) {
1109 string m_name
= is_struct ?
"init" : "new";
1110 buffer
.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name
, m_name
, get_ccode_name (m
));
1112 buffer
.append_printf ("<%s name=\"%s\" c:identifier=\"%s\"", tag_name
, m
.name
, get_ccode_name (m
));
1115 if (m
.tree_can_fail
) {
1116 buffer
.append_printf (" throws=\"1\"");
1118 if (!is_introspectable (m
)) {
1119 buffer
.append_printf (" introspectable=\"0\"");
1121 buffer
.append_printf (">\n");
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);
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
)) {
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\"");
1153 buffer
.append_printf (" construct=\"1\"");
1157 write_symbol_attributes (prop
);
1158 buffer
.append_printf (">\n");
1161 write_doc (get_property_comment (prop
));
1163 write_annotations (prop
);
1165 write_type (prop
.property_type
);
1169 buffer
.append_printf ("</property>\n");
1171 if (prop
.get_accessor
!= null) {
1172 var m
= prop
.get_accessor
.get_method ();
1178 if (prop
.set_accessor
!= null) {
1179 var m
= prop
.set_accessor
.get_method ();
1186 public override void visit_signal (Signal sig
) {
1187 if (!check_accessibility (sig
)) {
1191 if (sig
.emitter
!= null) {
1192 sig
.emitter
.accept (this
);
1196 buffer
.append_printf ("<glib:signal name=\"%s\"", get_ccode_name (sig
));
1197 write_symbol_attributes (sig
);
1198 buffer
.append_printf (">\n");
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
));
1209 buffer
.append_printf ("</glib:signal>\n");
1212 private void write_indent () {
1215 for (i
= 0; i
< indent
; i
++) {
1216 buffer
.append_c ('\t');
1220 private void write_indent_stream () {
1223 for (i
= 0; i
< indent
; i
++) {
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, bool ellipsis
= false) {
1231 string tag
= is_parameter ?
"parameter" : "return-value";
1232 buffer
.append_printf ("<%s", tag
);
1237 buffer
.append_printf (" name=\"%s\"", name
);
1239 if (direction
== ParameterDirection
.REF
) {
1240 buffer
.append_printf (" direction=\"inout\"");
1241 } else if (direction
== ParameterDirection
.OUT
) {
1242 buffer
.append_printf (" direction=\"out\"");
1245 DelegateType delegate_type
= type as DelegateType
;
1247 if (type
!= null && ((type
.value_owned
&& delegate_type
== null) || (constructor
&& !type
.data_type
.is_subtype_of (ginitiallyunowned_type
)))) {
1248 var any_owned
= false;
1249 foreach (var generic_arg
in type
.get_type_arguments ()) {
1250 any_owned
|= generic_arg
.value_owned
;
1252 if (type
.has_type_arguments () && !any_owned
) {
1253 buffer
.append_printf (" transfer-ownership=\"container\"");
1255 buffer
.append_printf (" transfer-ownership=\"full\"");
1258 buffer
.append_printf (" transfer-ownership=\"none\"");
1260 if (caller_allocates
) {
1261 buffer
.append_printf (" caller-allocates=\"1\"");
1263 if (type
!= null && type
.nullable
) {
1264 buffer
.append_printf (" allow-none=\"1\"");
1267 if (delegate_type
!= null && delegate_type
.delegate_symbol
.has_target
) {
1268 int closure_index
= is_parameter ?
1269 index
+ 1 : (type
.value_owned ? index
- 1 : index
);
1270 buffer
.append_printf (" closure=\"%i\"", closure_index
);
1271 if (delegate_type
.is_called_once
) {
1272 buffer
.append (" scope=\"async\"");
1273 } else if (type
.value_owned
) {
1274 buffer
.append_printf (" scope=\"notified\" destroy=\"%i\"", closure_index
+ 1);
1276 buffer
.append (" scope=\"call\"");
1278 } else if (delegate_type
!= null) {
1279 buffer
.append (" scope=\"call\"");
1282 buffer
.append_printf (">\n");
1285 write_doc (comment
);
1289 buffer
.append ("<varargs/>\n");
1290 } else if (type
!= null) {
1291 int length_param_index
= -1;
1292 if (has_array_length
) {
1293 length_param_index
= is_parameter ? index
+ 1 : index
;
1295 write_type (type
, length_param_index
, direction
);
1300 buffer
.append_printf ("</%s>\n", tag
);
1304 private void write_ctype_attributes (TypeSymbol symbol
, string suffix
= "") {
1305 buffer
.append_printf (" c:type=\"%s%s\"", get_ccode_name (symbol
), suffix
);
1308 private void write_gtype_attributes (TypeSymbol symbol
) {
1309 write_ctype_attributes(symbol
);
1310 buffer
.append_printf (" glib:type-name=\"%s\"", get_ccode_name (symbol
));
1311 buffer
.append_printf (" glib:get-type=\"%sget_type\"", get_ccode_lower_case_prefix (symbol
));
1314 private void write_type (DataType type
, int index
= -1, ParameterDirection direction
= ParameterDirection
.IN
) {
1315 if (type is ArrayType
) {
1316 var array_type
= (ArrayType
) type
;
1319 buffer
.append_printf ("<array");
1320 if (array_type
.fixed_length
&& array_type
.length is IntegerLiteral
) {
1321 var lit
= (IntegerLiteral
) array_type
.length
;
1322 buffer
.append_printf (" fixed-size=\"%i\"", int.parse (lit
.value
));
1323 } else if (index
!= -1) {
1324 buffer
.append_printf (" length=\"%i\"", index
);
1326 buffer
.append_printf (">\n");
1329 write_type (array_type
.element_type
);
1333 buffer
.append_printf ("</array>\n");
1334 } else if (type is VoidType
) {
1336 buffer
.append_printf ("<type name=\"none\"/>\n");
1337 } else if (type is PointerType
) {
1339 buffer
.append_printf ("<type name=\"gpointer\" c:type=\"%s\"/>\n", get_ccode_name (type
));
1340 } else if (type
.data_type
!= null) {
1342 string type_name
= gi_type_name (type
.data_type
);
1343 bool is_array
= false;
1344 if ((type_name
== "GLib.Array") || (type_name
== "GLib.PtrArray")) {
1347 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 ?
"" : "*");
1349 List
<DataType
> type_arguments
= type
.get_type_arguments ();
1350 if (type_arguments
.size
== 0) {
1351 buffer
.append_printf ("/>\n");
1353 buffer
.append_printf (">\n");
1356 foreach (DataType type_argument
in type_arguments
) {
1357 write_type (type_argument
);
1362 buffer
.append_printf ("</%s>\n", is_array ?
"array" : "type");
1364 } else if (type is DelegateType
) {
1365 var deleg_type
= (DelegateType
) type
;
1367 buffer
.append_printf ("<type name=\"%s\" c:type=\"%s\"/>\n", gi_type_name (deleg_type
.delegate_symbol
), get_ccode_name (type
));
1368 } else if (type is GenericType
) {
1369 // generic type parameters not supported in GIR
1371 buffer
.append ("<type name=\"gpointer\" c:type=\"gpointer\"/>\n");
1374 buffer
.append_printf ("<type name=\"%s\"/>\n", type
.to_string ());
1378 private void write_annotations (CodeNode node
) {
1379 foreach (Attribute attr
in node
.attributes
) {
1380 string name
= camel_case_to_canonical (attr
.name
);
1381 foreach (string arg_name
in attr
.args
.get_keys ()) {
1382 string value
= attr
.args
.get (arg_name
);
1383 if (value
.has_prefix ("\"")) {
1385 value
= attr
.get_string (arg_name
);
1389 buffer
.append_printf ("<attribute name=\"%s.%s\" value=\"%s\"/>\n",
1390 name
, camel_case_to_canonical (arg_name
), value
);
1395 private string?
get_full_gir_name (Symbol sym
) {
1396 string? gir_fullname
= sym
.get_attribute_string ("GIR", "fullname");
1397 if (gir_fullname
!= null) {
1398 return gir_fullname
;
1401 string? gir_name
= sym
.get_attribute_string ("GIR", "name");
1403 if (gir_name
== null && sym is Namespace
) {
1404 gir_name
= sym
.get_attribute_string ("CCode", "gir_namespace");
1406 if (gir_name
== null) {
1407 gir_name
= sym
.name
;
1410 if (sym
.parent_symbol
== null) {
1414 if (sym
.name
== null) {
1415 return get_full_gir_name (sym
.parent_symbol
);
1418 string parent_gir_name
= get_full_gir_name (sym
.parent_symbol
);
1419 if (parent_gir_name
== null) {
1423 string self_gir_name
= gir_name
.has_prefix (".") ? gir_name
.substring (1) : gir_name
;
1424 if ("." in parent_gir_name
) {
1425 return "%s%s".printf (parent_gir_name
, self_gir_name
);
1427 return "%s.%s".printf (parent_gir_name
, self_gir_name
);
1431 private string gi_type_name (TypeSymbol type_symbol
) {
1432 Symbol parent
= type_symbol
.parent_symbol
;
1433 if (parent is Namespace
) {
1434 Namespace ns
= parent as Namespace
;
1435 var ns_gir_name
= ns
.get_attribute_string ("GIR", "name") ?? ns
.name
;
1436 if (ns_gir_name
!= null) {
1437 if (type_symbol
.source_reference
.file
.gir_namespace
!= null) {
1438 GIRNamespace external
= GIRNamespace (type_symbol
.source_reference
.file
.gir_namespace
, type_symbol
.source_reference
.file
.gir_version
);
1439 if (!externals
.contains (external
)) {
1440 externals
.add (external
);
1442 string? gir_fullname
= type_symbol
.get_attribute_string ("GIR", "fullname");
1443 if (gir_fullname
!= null) {
1444 return gir_fullname
;
1446 var type_name
= type_symbol
.get_attribute_string ("GIR", "name") ?? type_symbol
.name
;
1447 return "%s.%s".printf (type_symbol
.source_reference
.file
.gir_namespace
, type_name
);
1449 unannotated_namespaces
.add(ns
);
1454 return get_full_gir_name (type_symbol
);
1457 private string?
literal_expression_to_value_string (Expression literal
) {
1458 if (literal is StringLiteral
) {
1459 var lit
= literal as StringLiteral
;
1461 return Markup
.escape_text (lit
.eval ());
1463 } else if (literal is CharacterLiteral
) {
1464 return "%c".printf ((char) ((CharacterLiteral
) literal
).get_char ());
1465 } else if (literal is BooleanLiteral
) {
1466 return ((BooleanLiteral
) literal
).value ?
"true" : "false";
1467 } else if (literal is RealLiteral
) {
1468 return ((RealLiteral
) literal
).value
;
1469 } else if (literal is IntegerLiteral
) {
1470 return ((IntegerLiteral
) literal
).value
;
1471 } else if (literal is UnaryExpression
) {
1472 var unary
= (UnaryExpression
) literal
;
1473 if (unary
.operator
== UnaryOperator
.MINUS
) {
1474 if (unary
.inner is RealLiteral
) {
1475 return "-" + ((RealLiteral
) unary
.inner
).value
;
1476 } else if (unary
.inner is IntegerLiteral
) {
1477 return "-" + ((IntegerLiteral
) unary
.inner
).value
;
1484 private string camel_case_to_canonical (string name
) {
1485 string[] parts
= Symbol
.camel_case_to_lower_case (name
).split ("_");
1486 return string.joinv ("-", parts
);
1489 private bool check_accessibility (Symbol sym
) {
1490 if (sym
.access
== SymbolAccessibility
.PUBLIC
||
1491 sym
.access
== SymbolAccessibility
.PROTECTED
) {