pango: mark several arguments as out
[vala-lang.git] / vala / valagirparser.vala
blobbedf769e129ad529d708130b4cafe3b5622516a7
1 /* valagirparser.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 parsing all Vala source files.
28 public class Vala.GirParser : CodeVisitor {
29 MarkupReader reader;
31 CodeContext context;
32 Namespace glib_ns;
34 SourceFile current_source_file;
35 SourceLocation begin;
36 SourceLocation end;
37 MarkupTokenType current_token;
39 string[] cheader_filenames;
40 string[] package_names;
42 HashMap<string,string> attributes_map = new HashMap<string,string> (str_hash, str_equal);
44 HashMap<string,ArrayList<Method>> gtype_callbacks = new HashMap<string,ArrayList<Method>> (str_hash, str_equal);
46 /**
47 * Parses all .gir source files in the specified code
48 * context and builds a code tree.
50 * @param context a code context
52 public void parse (CodeContext context) {
53 this.context = context;
54 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
55 context.accept (this);
58 public override void visit_source_file (SourceFile source_file) {
59 if (source_file.filename.has_suffix (".gir")) {
60 parse_file (source_file);
64 public void parse_file (SourceFile source_file) {
65 this.current_source_file = source_file;
66 reader = new MarkupReader (source_file.filename);
68 // xml prolog
69 next ();
70 next ();
72 next ();
73 parse_repository ();
75 var remove_queue = new ArrayList<CodeNode> ();
77 foreach (CodeNode node in source_file.get_nodes ()) {
78 if (node is Class) {
79 var cl = (Class) node;
80 var ns = cl.parent_symbol as Namespace;
81 // remove Class records
82 var class_struct = ns.scope.lookup (cl.name + "Class") as Struct;
83 if (class_struct != null) {
84 ns.remove_struct ((Struct) class_struct);
85 remove_queue.add (class_struct);
87 } else if (node is Interface) {
88 var iface = (Interface) node;
89 var ns = iface.parent_symbol as Namespace;
90 // remove Iface records
91 var iface_struct = ns.scope.lookup (iface.name + "Iface") as Struct;
92 if (iface_struct != null) {
93 ns.remove_struct ((Struct) iface_struct);
94 remove_queue.add (iface_struct);
99 foreach (CodeNode node in remove_queue) {
100 source_file.remove_node (node);
103 reader = null;
104 this.current_source_file = null;
107 void next () {
108 current_token = reader.read_token (out begin, out end);
110 // Skip *all* <doc> tags
111 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
112 skip_element();
115 void start_element (string name) {
116 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
117 // error
118 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
122 void end_element (string name) {
123 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
124 // error
125 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
127 next ();
130 SourceReference get_current_src () {
131 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
134 const string GIR_VERSION = "1.2";
136 void parse_repository () {
137 start_element ("repository");
138 if (reader.get_attribute ("version") != GIR_VERSION) {
139 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
140 return;
142 next ();
143 while (current_token == MarkupTokenType.START_ELEMENT) {
144 if (reader.name == "namespace") {
145 var ns = parse_namespace ();
146 if (ns != null) {
147 context.root.add_namespace (ns);
149 } else if (reader.name == "include") {
150 parse_include ();
151 } else if (reader.name == "package") {
152 parse_package ();
153 } else if (reader.name == "c:include") {
154 parse_c_include ();
155 } else {
156 // error
157 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
158 break;
161 end_element ("repository");
164 void parse_include () {
165 start_element ("include");
166 next ();
167 end_element ("include");
170 void parse_package () {
171 start_element ("package");
172 add_package_name (reader.get_attribute ("name"));
173 next ();
174 end_element ("package");
177 void parse_c_include () {
178 start_element ("c:include");
179 cheader_filenames += reader.get_attribute ("name");
180 next ();
181 end_element ("c:include");
184 void skip_element () {
185 next ();
187 int level = 1;
188 while (level > 0) {
189 if (current_token == MarkupTokenType.START_ELEMENT) {
190 level++;
191 } else if (current_token == MarkupTokenType.END_ELEMENT) {
192 level--;
193 } else if (current_token == MarkupTokenType.EOF) {
194 Report.error (get_current_src (), "unexpected end of file");
195 break;
197 next ();
201 Namespace? parse_namespace () {
202 start_element ("namespace");
204 bool new_namespace = false;
205 string namespace_name = transform_namespace_name (reader.get_attribute ("name"));
206 var ns = context.root.scope.lookup (namespace_name) as Namespace;
207 if (ns == null) {
208 ns = new Namespace (namespace_name, get_current_src ());
209 new_namespace = true;
210 } else {
211 if (ns.external_package) {
212 ns.attributes = null;
213 ns.source_reference = get_current_src ();
217 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
218 if (cprefix != null) {
219 ns.add_cprefix (cprefix);
220 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
223 foreach (string c_header in cheader_filenames) {
224 ns.add_cheader_filename (c_header);
226 next ();
227 while (current_token == MarkupTokenType.START_ELEMENT) {
228 if (reader.get_attribute ("introspectable") == "0") {
229 skip_element ();
230 continue;
233 Symbol sym = null;
234 if (reader.name == "alias") {
235 sym = parse_alias ();
236 } else if (reader.name == "enumeration") {
237 if (reader.get_attribute ("glib:error-quark") != null) {
238 sym = parse_error_domain ();
239 } else {
240 sym = parse_enumeration ();
242 } else if (reader.name == "bitfield") {
243 sym = parse_bitfield ();
244 } else if (reader.name == "function") {
245 sym = parse_method ("function");
246 } else if (reader.name == "callback") {
247 sym = parse_callback ();
248 } else if (reader.name == "record") {
249 if (reader.get_attribute ("glib:get-type") != null) {
250 sym = parse_boxed ();
251 } else {
252 sym = parse_record ();
254 } else if (reader.name == "class") {
255 sym = parse_class ();
256 } else if (reader.name == "interface") {
257 sym = parse_interface ();
258 } else if (reader.name == "glib:boxed") {
259 sym = parse_boxed ();
260 } else if (reader.name == "union") {
261 sym = parse_union ();
262 } else if (reader.name == "constant") {
263 sym = parse_constant ();
264 } else {
265 // error
266 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
267 break;
270 if (sym is Class) {
271 ns.add_class ((Class) sym);
272 } else if (sym is Interface) {
273 ns.add_interface ((Interface) sym);
274 } else if (sym is Struct) {
275 ns.add_struct ((Struct) sym);
276 } else if (sym is Enum) {
277 ns.add_enum ((Enum) sym);
278 } else if (sym is ErrorDomain) {
279 ns.add_error_domain ((ErrorDomain) sym);
280 } else if (sym is Delegate) {
281 ns.add_delegate ((Delegate) sym);
282 } else if (sym is Method) {
283 ns.add_method ((Method) sym);
284 } else if (sym is Constant) {
285 ns.add_constant ((Constant) sym);
286 } else if (sym == null) {
287 continue;
290 end_element ("namespace");
292 postprocess_gtype_callbacks (ns);
294 if (!new_namespace) {
295 ns = null;
298 return ns;
301 Struct parse_alias () {
302 start_element ("alias");
303 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
304 st.access = SymbolAccessibility.PUBLIC;
305 st.external = true;
306 next ();
308 st.base_type = parse_type (null, null, true);
310 end_element ("alias");
311 return st;
314 private void calculate_common_prefix (ref string common_prefix, string cname) {
315 if (common_prefix == null) {
316 common_prefix = cname;
317 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
318 // FIXME: could easily be made faster
319 common_prefix = common_prefix.ndup (common_prefix.length - 1);
321 } else {
322 while (!cname.has_prefix (common_prefix)) {
323 common_prefix = common_prefix.ndup (common_prefix.length - 1);
326 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
327 (cname.offset (common_prefix.length).get_char ().isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
328 // enum values may not consist solely of digits
329 common_prefix = common_prefix.ndup (common_prefix.length - 1);
333 Enum parse_enumeration () {
334 start_element ("enumeration");
335 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
336 en.access = SymbolAccessibility.PUBLIC;
338 string enum_cname = reader.get_attribute ("c:type");
339 if (enum_cname != null) {
340 en.set_cname (enum_cname);
343 next ();
345 string common_prefix = null;
347 while (current_token == MarkupTokenType.START_ELEMENT) {
348 if (reader.get_attribute ("introspectable") == "0") {
349 skip_element ();
350 continue;
353 if (reader.name == "member") {
354 var ev = parse_enumeration_member ();
355 en.add_value (ev);
356 calculate_common_prefix (ref common_prefix, ev.get_cname ());
357 } else {
358 // error
359 break;
363 en.set_cprefix (common_prefix);
365 end_element ("enumeration");
366 return en;
369 ErrorDomain parse_error_domain () {
370 start_element ("enumeration");
372 var ed = new ErrorDomain (reader.get_attribute ("name"), get_current_src ());
373 ed.access = SymbolAccessibility.PUBLIC;
375 string enum_cname = reader.get_attribute ("c:type");
376 if (enum_cname != null) {
377 ed.set_cname (enum_cname);
380 next ();
382 string common_prefix = null;
384 while (current_token == MarkupTokenType.START_ELEMENT) {
385 if (reader.get_attribute ("introspectable") == "0") {
386 skip_element ();
387 continue;
390 if (reader.name == "member") {
391 ErrorCode ec = parse_error_member ();
392 ed.add_code (ec);
393 calculate_common_prefix (ref common_prefix, ec.get_cname ());
394 } else {
395 // error
396 break;
400 ed.set_cprefix (common_prefix);
402 end_element ("enumeration");
403 return ed;
406 Enum parse_bitfield () {
407 start_element ("bitfield");
408 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
409 en.access = SymbolAccessibility.PUBLIC;
410 next ();
411 while (current_token == MarkupTokenType.START_ELEMENT) {
412 if (reader.get_attribute ("introspectable") == "0") {
413 skip_element ();
414 continue;
417 if (reader.name == "member") {
418 en.add_value (parse_enumeration_member ());
419 } else {
420 // error
421 break;
424 end_element ("bitfield");
425 return en;
428 EnumValue parse_enumeration_member () {
429 start_element ("member");
430 var ev = new EnumValue (string.joinv ("_", reader.get_attribute ("name").up ().split ("-")), null);
431 ev.set_cname (reader.get_attribute ("c:identifier"));
432 next ();
433 end_element ("member");
434 return ev;
437 ErrorCode parse_error_member () {
438 start_element ("member");
440 ErrorCode ec;
441 string name = string.joinv ("_", reader.get_attribute ("name").up ().split ("-"));
442 string value = reader.get_attribute ("value");
443 if (value != null) {
444 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
445 } else {
446 ec = new ErrorCode (name);
449 next ();
450 end_element ("member");
451 return ec;
454 DataType parse_return_value (out string? ctype = null) {
455 start_element ("return-value");
456 string transfer = reader.get_attribute ("transfer-ownership");
457 string allow_none = reader.get_attribute ("allow-none");
458 next ();
459 var transfer_elements = transfer == "full";
460 var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
461 if (transfer == "full" || transfer == "container") {
462 type.value_owned = true;
464 if (allow_none == "1") {
465 type.nullable = true;
467 end_element ("return-value");
468 return type;
471 FormalParameter parse_parameter (out int array_length_idx = null, out int closure_idx = null, out int destroy_idx = null, out string? scope = null) {
472 FormalParameter param;
474 if (&array_length_idx != null) {
475 array_length_idx = -1;
477 if (&closure_idx != null) {
478 closure_idx = -1;
480 if (&destroy_idx != null) {
481 destroy_idx = -1;
484 start_element ("parameter");
485 string name = reader.get_attribute ("name");
486 string direction = reader.get_attribute ("direction");
487 string transfer = reader.get_attribute ("transfer-ownership");
488 string allow_none = reader.get_attribute ("allow-none");
490 if (&scope != null) {
491 scope = reader.get_attribute ("scope");
494 string closure = reader.get_attribute ("closure");
495 string destroy = reader.get_attribute ("destroy");
496 if (closure != null && &closure_idx != null) {
497 closure_idx = closure.to_int ();
499 if (destroy != null && &destroy_idx != null) {
500 destroy_idx = destroy.to_int ();
503 next ();
504 if (reader.name == "varargs") {
505 start_element ("varargs");
506 next ();
507 param = new FormalParameter.with_ellipsis (get_current_src ());
508 end_element ("varargs");
509 } else {
510 var type = parse_type (null, out array_length_idx, transfer == "full");
511 if (transfer == "full" || transfer == "container" || destroy != null) {
512 type.value_owned = true;
514 if (allow_none == "1") {
515 type.nullable = true;
517 param = new FormalParameter (name, type, get_current_src ());
518 if (direction == "out") {
519 param.direction = ParameterDirection.OUT;
520 } else if (direction == "inout") {
521 param.direction = ParameterDirection.REF;
524 end_element ("parameter");
525 return param;
528 DataType parse_type (out string? ctype = null, out int array_length_index = null, bool transfer_elements = false) {
529 bool is_array = false;
530 string type_name = reader.get_attribute ("name");
532 if (reader.name == "array") {
533 is_array = true;
534 start_element ("array");
536 if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
537 if (reader.get_attribute ("length") != null
538 && &array_length_index != null) {
539 array_length_index = reader.get_attribute ("length").to_int ();
541 next ();
542 var element_type = parse_type ();
543 end_element ("array");
544 return new ArrayType (element_type, 1, null);
546 } else if (reader.name == "callback"){
547 var callback = parse_callback ();
548 return new DelegateType (callback);
549 } else {
550 start_element ("type");
553 if (&ctype != null) {
554 ctype = reader.get_attribute("c:type");
557 next ();
559 if (type_name == "GLib.PtrArray"
560 && current_token == MarkupTokenType.START_ELEMENT) {
561 type_name = "GLib.GenericArray";
564 DataType type = parse_type_from_name (type_name);
566 // type arguments / element types
567 while (current_token == MarkupTokenType.START_ELEMENT) {
568 var element_type = parse_type ();
569 element_type.value_owned = transfer_elements;
570 type.add_type_argument (element_type);
573 end_element (is_array ? "array" : "type");
574 return type;
577 DataType parse_type_from_name (string type_name) {
578 DataType type;
579 if (type_name == "none") {
580 type = new VoidType ();
581 } else if (type_name == "gpointer") {
582 type = new PointerType (new VoidType ());
583 } else if (type_name == "GObject.Strv") {
584 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, null);
585 } else {
586 if (type_name == "utf8") {
587 type_name = "string";
588 } else if (type_name == "gboolean") {
589 type_name = "bool";
590 } else if (type_name == "gchar") {
591 type_name = "char";
592 } else if (type_name == "gshort") {
593 type_name = "short";
594 } else if (type_name == "gushort") {
595 type_name = "ushort";
596 } else if (type_name == "gint") {
597 type_name = "int";
598 } else if (type_name == "guint") {
599 type_name = "uint";
600 } else if (type_name == "glong") {
601 type_name = "long";
602 } else if (type_name == "gulong") {
603 type_name = "ulong";
604 } else if (type_name == "gint8") {
605 type_name = "int8";
606 } else if (type_name == "guint8") {
607 type_name = "uint8";
608 } else if (type_name == "gint16") {
609 type_name = "int16";
610 } else if (type_name == "guint16") {
611 type_name = "uint16";
612 } else if (type_name == "gint32") {
613 type_name = "int32";
614 } else if (type_name == "guint32") {
615 type_name = "uint32";
616 } else if (type_name == "gint64") {
617 type_name = "int64";
618 } else if (type_name == "guint64") {
619 type_name = "uint64";
620 } else if (type_name == "gfloat") {
621 type_name = "float";
622 } else if (type_name == "gdouble") {
623 type_name = "double";
624 } else if (type_name == "filename") {
625 type_name = "string";
626 } else if (type_name == "GLib.offset") {
627 type_name = "int64";
628 } else if (type_name == "gsize") {
629 type_name = "size_t";
630 } else if (type_name == "gssize") {
631 type_name = "ssize_t";
632 } else if (type_name == "GType") {
633 type_name = "GLib.Type";
634 } else if (type_name == "GLib.String") {
635 type_name = "GLib.StringBuilder";
636 } else if (type_name == "GObject.Class") {
637 type_name = "GLib.ObjectClass";
638 } else if (type_name == "GLib.unichar") {
639 type_name = "unichar";
640 } else if (type_name == "GLib.Data") {
641 type_name = "GLib.Datalist";
642 } else if (type_name == "Atk.ImplementorIface") {
643 type_name = "Atk.Implementor";
645 string[] type_components = type_name.split (".");
646 if (type_components[1] != null) {
647 // namespaced name
648 string namespace_name = transform_namespace_name (type_components[0]);
649 string transformed_type_name = type_components[1];
650 type = new UnresolvedType.from_symbol (new UnresolvedSymbol (new UnresolvedSymbol (null, namespace_name), transformed_type_name));
651 } else {
652 type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, type_name));
656 return type;
659 string transform_namespace_name (string gir_module_name) {
660 if (gir_module_name == "GObject") {
661 return "GLib";
662 } else if (gir_module_name == "Gio") {
663 return "GLib";
664 } else if (gir_module_name == "GModule") {
665 return "GLib";
667 return gir_module_name;
670 Struct parse_record () {
671 start_element ("record");
672 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
673 st.external = true;
675 string glib_is_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
677 st.access = SymbolAccessibility.PUBLIC;
678 next ();
679 while (current_token == MarkupTokenType.START_ELEMENT) {
680 if (reader.get_attribute ("introspectable") == "0") {
681 skip_element ();
682 continue;
685 if (reader.name == "field") {
686 st.add_field (parse_field ());
687 } else if (reader.name == "callback") {
688 if (glib_is_gtype_struct_for != null) {
689 ArrayList<Method> callbacks = gtype_callbacks.get (glib_is_gtype_struct_for);
690 if (callbacks == null) {
691 callbacks = new ArrayList<Method> ();
692 gtype_callbacks.set (glib_is_gtype_struct_for, callbacks);
694 callbacks.add (parse_method ("callback"));
695 } else {
696 parse_callback ();
698 } else if (reader.name == "constructor") {
699 parse_constructor ();
700 } else if (reader.name == "method") {
701 st.add_method (parse_method ("method"));
702 } else if (reader.name == "union") {
703 Struct s = parse_union ();
704 var s_fields = s.get_fields ();
705 foreach (var f in s_fields) {
706 f.set_cname (s.get_cname () + "." + f.get_cname ());
707 f.name = s.name + "_" + f.name;
708 st.add_field (f);
710 } else {
711 // error
712 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
713 break;
716 end_element ("record");
717 return st;
720 void postprocess_gtype_callbacks (Namespace ns) {
721 foreach (string gtype_name in gtype_callbacks.get_keys ()) {
722 var gtype = ns.scope.lookup (gtype_name) as ObjectTypeSymbol;
723 ArrayList<Method> callbacks = gtype_callbacks.get (gtype_name);
724 foreach (Method m in callbacks) {
725 var symbol = gtype.scope.lookup (m.name);
726 if (symbol == null) {
727 continue;
728 } else if (symbol is Method) {
729 var meth = (Method) symbol;
730 if (gtype is Class) {
731 meth.is_virtual = true;
732 } else if (gtype is Interface) {
733 meth.is_abstract = true;
735 } else if (symbol is Signal) {
736 var sig = (Signal) symbol;
737 sig.is_virtual = true;
738 } else {
739 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (m.name, gtype.name));
745 Class parse_class () {
746 start_element ("class");
747 var cl = new Class (reader.get_attribute ("name"), get_current_src ());
748 cl.access = SymbolAccessibility.PUBLIC;
749 cl.external = true;
751 string cname = reader.get_attribute ("c:type");
752 if (cname != null) {
753 cl.set_cname (cname);
756 string parent = reader.get_attribute ("parent");
757 if (parent != null) {
758 cl.add_base_type (parse_type_from_name (parent));
761 next ();
762 var signals = new ArrayList<Signal> ();
763 var methods = new ArrayList<Method> ();
764 var vmethods = new ArrayList<Method> ();
765 var fields = new ArrayList<Field> ();
766 while (current_token == MarkupTokenType.START_ELEMENT) {
767 if (reader.get_attribute ("introspectable") == "0") {
768 skip_element ();
769 continue;
772 if (reader.name == "implements") {
773 start_element ("implements");
774 cl.add_base_type (parse_type_from_name (reader.get_attribute ("name")));
775 next ();
776 end_element ("implements");
777 } else if (reader.name == "constant") {
778 cl.add_constant (parse_constant ());
779 } else if (reader.name == "field") {
780 fields.add (parse_field ());
781 } else if (reader.name == "property") {
782 cl.add_property (parse_property ());
783 } else if (reader.name == "constructor") {
784 cl.add_method (parse_constructor (cname));
785 } else if (reader.name == "function") {
786 methods.add (parse_method ("function"));
787 } else if (reader.name == "method") {
788 methods.add (parse_method ("method"));
789 } else if (reader.name == "virtual-method") {
790 vmethods.add (parse_method ("virtual-method"));
791 } else if (reader.name == "union") {
792 Struct s = parse_union ();
793 var s_fields = s.get_fields ();
794 foreach (var f in s_fields) {
795 f.set_cname (s.get_cname () + "." + f.get_cname ());
796 f.name = s.name + "_" + f.name;
797 fields.add (f);
799 } else if (reader.name == "glib:signal") {
800 signals.add (parse_signal ());
801 } else {
802 // error
803 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
804 break;
808 // signal merging
809 foreach (Signal sig in signals) {
810 var symbol = cl.scope.lookup (sig.name);
811 if (symbol == null) {
812 cl.add_signal (sig);
813 } else if (symbol is Property) {
814 // properties take precedence
815 } else {
816 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (sig.name, cl.name));
820 // virtual method merging
821 foreach (Method m in vmethods) {
822 var symbol = cl.scope.lookup (m.name);
823 if (symbol == null) {
824 cl.add_method (m);
825 } else if (symbol is Signal) {
826 var sig = (Signal) symbol;
827 sig.is_virtual = true;
828 } else if (symbol is Property || symbol is Field) {
829 // assume method is getter for property/field ignore method
830 } else {
831 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, cl.name));
835 // method merging
836 foreach (Method m in methods) {
837 var symbol = cl.scope.lookup (m.name);
838 if (symbol == null) {
839 cl.add_method (m);
840 } else if (symbol is Signal) {
841 var sig = (Signal) symbol;
842 sig.has_emitter = true;
843 } else if (symbol is Property || symbol is Field) {
844 // assume method is getter for property/field ignore method
845 } else if (symbol is Method) {
846 // assume method is wrapper for virtual method
847 } else {
848 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, cl.name));
852 // fields have lowest priority
853 foreach (Field f in fields) {
854 var symbol = cl.scope.lookup (f.name);
855 if (symbol == null) {
856 cl.add_field (f);
860 handle_async_methods (cl);
862 end_element ("class");
863 return cl;
866 Interface parse_interface () {
867 start_element ("interface");
868 var iface = new Interface (reader.get_attribute ("name"), get_current_src ());
869 iface.access = SymbolAccessibility.PUBLIC;
870 iface.external = true;
872 string cname = reader.get_attribute ("c:type");
873 if (cname != null) {
874 iface.set_cname (cname);
877 next ();
878 var methods = new ArrayList<Method> ();
879 var vmethods = new ArrayList<Method> ();
880 while (current_token == MarkupTokenType.START_ELEMENT) {
881 if (reader.get_attribute ("introspectable") == "0") {
882 skip_element ();
883 continue;
886 if (reader.name == "prerequisite") {
887 start_element ("prerequisite");
888 iface.add_prerequisite (parse_type_from_name (reader.get_attribute ("name")));
889 next ();
890 end_element ("prerequisite");
891 } else if (reader.name == "field") {
892 parse_field ();
893 } else if (reader.name == "property") {
894 iface.add_property (parse_property ());
895 } else if (reader.name == "virtual-method") {
896 vmethods.add (parse_method ("virtual-method"));
897 } else if (reader.name == "function") {
898 methods.add (parse_method ("function"));
899 } else if (reader.name == "method") {
900 methods.add (parse_method ("method"));
901 } else if (reader.name == "glib:signal") {
902 iface.add_signal (parse_signal ());
903 } else {
904 // error
905 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
906 break;
910 // ensure we have at least one instantiable prerequisite (GLib.Object)
911 bool has_instantiable_prereq = false;
912 foreach (DataType prereq in iface.get_prerequisites ()) {
913 if (prereq.data_type is Class) {
914 has_instantiable_prereq = true;
915 break;
919 if (!has_instantiable_prereq)
920 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
922 // virtual method merging
923 foreach (Method m in vmethods) {
924 var symbol = iface.scope.lookup (m.name);
925 if (symbol == null) {
926 iface.add_method (m);
927 } else if (symbol is Signal) {
928 var sig = (Signal) symbol;
929 sig.is_virtual = true;
930 } else {
931 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, iface.name));
935 // method merging
936 foreach (Method m in methods) {
937 var symbol = iface.scope.lookup (m.name);
938 if (symbol == null) {
939 iface.add_method (m);
940 } else if (symbol is Signal) {
941 var sig = (Signal) symbol;
942 sig.has_emitter = true;
943 } else if (symbol is Method) {
944 // assume method is wrapper for virtual method
945 } else {
946 Report.error (get_current_src (), "duplicate member `%s' in `%s'".printf (m.name, iface.name));
950 handle_async_methods (iface);
952 end_element ("interface");
953 return iface;
956 void handle_async_methods (ObjectTypeSymbol type_symbol) {
957 var methods = type_symbol.get_methods ();
958 for (int method_n = 0 ; method_n < methods.size ; method_n++) {
959 var m = methods.get (method_n);
961 if (m.coroutine) {
962 string finish_method_base;
963 if (m.name.has_suffix ("_async")) {
964 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
965 } else {
966 finish_method_base = m.name;
968 var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method;
970 // check if the method is using non-standard finish method name
971 if (finish_method == null) {
972 var method_cname = m.get_finish_cname ();
973 foreach (Method method in type_symbol.get_methods ()) {
974 if (method.get_cname () == method_cname) {
975 finish_method = method;
976 break;
981 if (finish_method != null) {
982 m.return_type = finish_method.return_type.copy ();
983 m.no_array_length = finish_method.no_array_length;
984 m.array_null_terminated = finish_method.array_null_terminated;
985 foreach (var param in finish_method.get_parameters ()) {
986 if (param.direction == ParameterDirection.OUT) {
987 var async_param = param.copy ();
988 if (m.scope.lookup (param.name) != null) {
989 // parameter name conflict
990 async_param.name += "_out";
992 m.add_parameter (async_param);
995 foreach (DataType error_type in finish_method.get_error_types ()) {
996 m.add_error_type (error_type.copy ());
998 if (methods.index_of (finish_method) < method_n) {
999 method_n--;
1001 type_symbol.scope.remove (finish_method.name);
1002 methods.remove (finish_method);
1008 Field parse_field () {
1009 start_element ("field");
1010 string name = reader.get_attribute ("name");
1011 string allow_none = reader.get_attribute ("allow-none");
1012 next ();
1013 var type = parse_type ();
1014 var field = new Field (name, type, null, get_current_src ());
1015 field.access = SymbolAccessibility.PUBLIC;
1016 if (allow_none == "1") {
1017 type.nullable = true;
1019 end_element ("field");
1020 return field;
1023 Property parse_property () {
1024 start_element ("property");
1025 string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
1026 string readable = reader.get_attribute ("readable");
1027 string writable = reader.get_attribute ("writable");
1028 string construct_ = reader.get_attribute ("construct");
1029 string construct_only = reader.get_attribute ("construct-only");
1030 next ();
1031 var type = parse_type ();
1032 var prop = new Property (name, type, null, null, get_current_src ());
1033 prop.access = SymbolAccessibility.PUBLIC;
1034 if (readable != "0") {
1035 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
1037 if (writable == "1" || construct_only == "1") {
1038 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
1040 end_element ("property");
1041 return prop;
1044 Delegate parse_callback () {
1045 return this.parse_function ("callback") as Delegate;
1048 Method parse_constructor (string? parent_ctype = null) {
1049 start_element ("constructor");
1050 string name = reader.get_attribute ("name");
1051 string throws_string = reader.get_attribute ("throws");
1052 string cname = reader.get_attribute ("c:identifier");
1053 next ();
1055 string? ctype;
1056 parse_return_value (out ctype);
1058 var m = new CreationMethod (null, name, get_current_src ());
1059 m.access = SymbolAccessibility.PUBLIC;
1060 m.has_construct_function = false;
1061 if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
1062 m.custom_return_type_cname = ctype;
1064 if (m.name == "new") {
1065 m.name = null;
1066 } else if (m.name.has_prefix ("new_")) {
1067 m.name = m.name.offset ("new_".length);
1069 if (cname != null) {
1070 m.set_cname (cname);
1072 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
1073 start_element ("parameters");
1074 next ();
1075 while (current_token == MarkupTokenType.START_ELEMENT) {
1076 m.add_parameter (parse_parameter ());
1078 end_element ("parameters");
1081 if (throws_string == "1") {
1082 m.add_error_type (new ErrorType (null, null));
1084 end_element ("constructor");
1085 return m;
1088 class MethodInfo {
1089 public MethodInfo (FormalParameter param, int array_length_idx, int closure_idx, int destroy_idx) {
1090 this.param = param;
1091 this.array_length_idx = array_length_idx;
1092 this.closure_idx = closure_idx;
1093 this.destroy_idx = destroy_idx;
1094 this.vala_idx = 0.0F;
1095 this.keep = true;
1098 public FormalParameter param;
1099 public float vala_idx;
1100 public int array_length_idx;
1101 public int closure_idx;
1102 public int destroy_idx;
1103 public bool keep;
1106 Symbol parse_function (string element_name) {
1107 start_element (element_name);
1108 string name = reader.get_attribute ("name");
1109 string cname = reader.get_attribute ("c:identifier");
1110 string throws_string = reader.get_attribute ("throws");
1111 string invoker = reader.get_attribute ("invoker");
1112 next ();
1113 DataType return_type;
1114 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
1115 return_type = parse_return_value ();
1116 } else {
1117 return_type = new VoidType ();
1120 Symbol s;
1122 if (element_name == "callback") {
1123 s = new Delegate (name, return_type, get_current_src ());
1124 } else {
1125 s = new Method (name, return_type, get_current_src ());
1128 s.access = SymbolAccessibility.PUBLIC;
1129 if (cname != null) {
1130 if (s is Method) {
1131 ((Method) s).set_cname (cname);
1132 } else {
1133 ((Delegate) s).set_cname (cname);
1137 if (element_name == "virtual-method" || element_name == "callback") {
1138 if (s is Method) {
1139 ((Method) s).is_virtual = true;
1142 if (invoker != null){
1143 s.name = invoker;
1145 } else if (element_name == "function") {
1146 ((Method) s).binding = MemberBinding.STATIC;
1149 var parameters = new ArrayList<MethodInfo> ();
1150 var array_length_parameters = new ArrayList<int> ();
1151 var closure_parameters = new ArrayList<int> ();
1152 var destroy_parameters = new ArrayList<int> ();
1153 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
1154 start_element ("parameters");
1155 next ();
1157 while (current_token == MarkupTokenType.START_ELEMENT) {
1158 int array_length_idx, closure_idx, destroy_idx;
1159 string scope;
1160 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope);
1161 if (array_length_idx != -1) {
1162 array_length_parameters.add (array_length_idx);
1164 if (closure_idx != -1) {
1165 closure_parameters.add (closure_idx);
1167 if (destroy_idx != -1) {
1168 destroy_parameters.add (destroy_idx);
1171 var info = new MethodInfo(param, array_length_idx, closure_idx, destroy_idx);
1173 if (s is Method && scope == "async") {
1174 var unresolved_type = param.variable_type as UnresolvedType;
1175 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
1176 // GAsync-style method
1177 ((Method) s).coroutine = true;
1178 info.keep = false;
1182 parameters.add (info);
1184 end_element ("parameters");
1186 int i = 0, j=1;
1188 int last = -1;
1189 foreach (MethodInfo info in parameters) {
1190 if (s is Delegate && info.closure_idx == i) {
1191 var d = (Delegate) s;
1192 d.has_target = true;
1193 d.cinstance_parameter_position = (float) j - 0.1;
1194 info.keep = false;
1195 } else if (info.keep
1196 && !array_length_parameters.contains (i)
1197 && !closure_parameters.contains (i)
1198 && !destroy_parameters.contains (i)) {
1199 info.vala_idx = (float) j;
1200 info.keep = true;
1202 /* interpolate for vala_idx between this and last*/
1203 float last_idx = 0.0F;
1204 if (last != -1) {
1205 last_idx = parameters[last].vala_idx;
1207 for (int k=last+1; k < i; k++) {
1208 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
1210 last = i;
1211 j++;
1212 } else {
1213 info.keep = false;
1214 // make sure that vala_idx is always set
1215 // the above if branch does not set vala_idx for
1216 // hidden parameters at the end of the parameter list
1217 info.vala_idx = (j - 1) + (i - last) * 0.1F;
1219 i++;
1222 foreach (MethodInfo info in parameters) {
1223 if (info.keep) {
1225 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
1226 so do it first*/
1227 if (s is Method) {
1228 ((Method) s).add_parameter (info.param);
1229 } else {
1230 ((Delegate) s).add_parameter (info.param);
1233 if (info.array_length_idx != -1) {
1234 if ((info.array_length_idx) >= parameters.size) {
1235 Report.error (get_current_src (), "invalid array_length index");
1236 continue;
1238 info.param.carray_length_parameter_position = parameters[info.array_length_idx].vala_idx;
1239 info.param.set_array_length_cname (parameters[info.array_length_idx].param.name);
1241 if (info.param.variable_type is ArrayType && info.array_length_idx == -1) {
1242 info.param.no_array_length = true;
1245 if (info.closure_idx != -1) {
1246 if ((info.closure_idx) >= parameters.size) {
1247 Report.error (get_current_src (), "invalid closure index");
1248 continue;
1250 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
1252 if (info.destroy_idx != -1) {
1253 if (info.destroy_idx >= parameters.size) {
1254 Report.error (get_current_src (), "invalid destroy index");
1255 continue;
1257 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
1262 if (throws_string == "1") {
1263 s.add_error_type (new ErrorType (null, null));
1265 end_element (element_name);
1266 return s;
1269 Method parse_method (string element_name) {
1270 return this.parse_function (element_name) as Method;
1273 Signal parse_signal () {
1274 start_element ("glib:signal");
1275 string name = string.joinv ("_", reader.get_attribute ("name").split ("-"));
1276 next ();
1277 DataType return_type;
1278 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
1279 return_type = parse_return_value ();
1280 } else {
1281 return_type = new VoidType ();
1283 var sig = new Signal (name, return_type);
1284 sig.access = SymbolAccessibility.PUBLIC;
1285 sig.external = true;
1286 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
1287 start_element ("parameters");
1288 next ();
1289 while (current_token == MarkupTokenType.START_ELEMENT) {
1290 sig.add_parameter (parse_parameter ());
1292 end_element ("parameters");
1294 end_element ("glib:signal");
1295 return sig;
1298 Class parse_boxed () {
1299 string name = reader.get_attribute ("name");
1300 if (name == null) {
1301 name = reader.get_attribute ("glib:name");
1303 var cl = new Class (name);
1304 cl.access = SymbolAccessibility.PUBLIC;
1305 cl.external = true;
1306 cl.is_compact = true;
1308 string cname = reader.get_attribute ("c:type");
1309 if (cname != null) {
1310 cl.set_cname (cname);
1313 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
1314 cl.set_free_function ("g_boxed_free");
1315 cl.set_dup_function ("g_boxed_copy");
1317 next ();
1319 while (current_token == MarkupTokenType.START_ELEMENT) {
1320 if (reader.get_attribute ("introspectable") == "0") {
1321 skip_element ();
1322 continue;
1325 if (reader.name == "field") {
1326 cl.add_field (parse_field ());
1327 } else if (reader.name == "constructor") {
1328 parse_constructor ();
1329 } else if (reader.name == "method") {
1330 cl.add_method (parse_method ("method"));
1331 } else {
1332 // error
1333 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
1334 break;
1338 if (current_token != MarkupTokenType.END_ELEMENT) {
1339 // error
1340 Report.error (get_current_src (), "expected end element");
1342 next ();
1343 return cl;
1346 Struct parse_union () {
1347 start_element ("union");
1348 var st = new Struct (reader.get_attribute ("name"));
1349 st.access = SymbolAccessibility.PUBLIC;
1350 st.external = true;
1351 next ();
1353 while (current_token == MarkupTokenType.START_ELEMENT) {
1354 if (reader.get_attribute ("introspectable") == "0") {
1355 skip_element ();
1356 continue;
1359 if (reader.name == "field") {
1360 st.add_field (parse_field ());
1361 } else if (reader.name == "constructor") {
1362 parse_constructor ();
1363 } else if (reader.name == "method") {
1364 st.add_method (parse_method ("method"));
1365 } else if (reader.name == "record") {
1366 Struct s = parse_record ();
1367 var fs = s.get_fields ();
1368 foreach (var f in fs) {
1369 f.set_cname (s.get_cname () + "." + f.get_cname ());
1370 f.name = s.name + "_" + f.name;
1371 st.add_field (f);
1373 } else {
1374 // error
1375 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
1376 break;
1380 end_element ("union");
1381 return st;
1384 Constant parse_constant () {
1385 start_element ("constant");
1386 string name = reader.get_attribute ("name");
1387 next ();
1388 var type = parse_type ();
1389 var c = new Constant (name, type, null, get_current_src ());
1390 c.access = SymbolAccessibility.PUBLIC;
1391 c.external = true;
1392 end_element ("constant");
1393 return c;
1396 public void parse_metadata (string metadata_filename) {
1397 if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
1398 try {
1399 string metadata;
1400 FileUtils.get_contents (metadata_filename, out metadata, null);
1402 foreach (string line in metadata.split ("\n")) {
1403 if (line.has_prefix ("#")) {
1404 // ignore comment lines
1405 continue;
1408 string[] tokens = line.split (" ", 2);
1410 if (null == tokens[0]) {
1411 continue;
1414 foreach (string attribute in tokens[1].split (" ")) {
1415 string[] pair = attribute.split ("=", 2);
1416 if (pair[0] == null || pair[1] == null) {
1417 continue;
1420 string key = "%s/@%s".printf (tokens[0], pair[0]);
1421 attributes_map.set (key, pair[1].substring (1, pair[1].length - 2));
1424 } catch (FileError e) {
1425 Report.error (null, "Unable to read metadata file: %s".printf (e.message));
1427 } else {
1428 Report.error (null, "Metadata file `%s' not found".printf (metadata_filename));
1432 void add_package_name (string name) {
1433 if (package_names == null) {
1434 package_names = new string[0];
1437 foreach (var existing in package_names) {
1438 if (name == existing) {
1439 return;
1443 package_names += name;
1446 public string[]? get_package_names () {
1447 return package_names;