girparser: Support async constructors.
[vala-lang.git] / vala / valagirparser.vala
blob0b7f1527de656ac282a5721d1b484387fcb723f3
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 * Pipeline:
29 * 1) Parse metadata
30 * 2) Parse GIR with metadata, track unresolved GIR symbols, create symbol mappings
31 * 3) Reconciliate the tree by mapping tracked symbols
32 * 4) Reparent nodes
33 * 5) Process callbacks/virtual
34 * 6) Process aliases
35 * 7) Autoreparent static methods
37 * Best hacking practices:
38 * - Keep GIR parsing bloat-free, it must contain the logic
39 * - Prefer being clean / short over performance
40 * - Try to make things common as much as possible
41 * - Prefer replace/merge after parse rather than a bunch of if-then-else and hardcoding
42 * - Prefer postprocessing over hardcoding the parser
44 public class Vala.GirParser : CodeVisitor {
45 enum MetadataType {
46 GENERIC,
47 PROPERTY,
48 SIGNAL
51 enum ArgumentType {
52 SKIP,
53 HIDDEN,
54 TYPE,
55 TYPE_ARGUMENTS,
56 CHEADER_FILENAME,
57 NAME,
58 OWNED,
59 UNOWNED,
60 PARENT,
61 NULLABLE,
62 DEPRECATED,
63 REPLACEMENT,
64 DEPRECATED_SINCE,
65 ARRAY,
66 ARRAY_LENGTH_IDX,
67 DEFAULT,
68 OUT,
69 REF,
70 VFUNC_NAME,
71 VIRTUAL,
72 ABSTRACT;
74 public static ArgumentType? from_string (string name) {
75 var enum_class = (EnumClass) typeof(ArgumentType).class_ref ();
76 var nick = name.replace ("_", "-");
77 unowned GLib.EnumValue? enum_value = enum_class.get_value_by_nick (nick);
78 if (enum_value != null) {
79 ArgumentType value = (ArgumentType) enum_value.value;
80 return value;
82 return null;
86 class Argument {
87 public Expression expression;
88 public SourceReference source_reference;
90 public bool used = false;
92 public Argument (Expression expression, SourceReference? source_reference = null) {
93 this.expression = expression;
94 this.source_reference = source_reference;
98 class MetadataSet : Metadata {
99 public MetadataSet (MetadataType type) {
100 base ("", type);
103 public void add_sibling (Metadata metadata) {
104 foreach (var child in metadata.children) {
105 add_child (child);
107 // merge arguments and take precedence
108 foreach (var key in metadata.args.get_keys ()) {
109 args[key] = metadata.args[key];
114 class Metadata {
115 private static Metadata _empty = null;
116 public static Metadata empty {
117 get {
118 if (_empty == null) {
119 _empty = new Metadata ("");
121 return _empty;
125 public string pattern;
126 public PatternSpec pattern_spec;
127 public MetadataType type;
128 public SourceReference source_reference;
130 public bool used = false;
131 public Vala.Map<ArgumentType,Argument> args = new HashMap<ArgumentType,Argument> ();
132 public ArrayList<Metadata> children = new ArrayList<Metadata> ();
134 public Metadata (string pattern, MetadataType type = MetadataType.GENERIC, SourceReference? source_reference = null) {
135 this.pattern = pattern;
136 this.pattern_spec = new PatternSpec (pattern);
137 this.type = type;
138 this.source_reference = source_reference;
141 public void add_child (Metadata metadata) {
142 children.add (metadata);
145 public Metadata? get_child (string pattern, MetadataType type = MetadataType.GENERIC) {
146 foreach (var metadata in children) {
147 if (metadata.type == type && metadata.pattern == pattern) {
148 return metadata;
151 return null;
154 public Metadata match_child (string name, MetadataType type = MetadataType.GENERIC) {
155 var result = Metadata.empty;
156 foreach (var metadata in children) {
157 if (metadata.type == type && metadata.pattern_spec.match_string (name)) {
158 metadata.used = true;
159 if (result == Metadata.empty) {
160 // first match
161 result = metadata;
162 } else {
163 var ms = result as MetadataSet;
164 if (ms == null) {
165 // second match
166 ms = new MetadataSet (type);
167 ms.add_sibling (result);
169 ms.add_sibling (metadata);
170 result = ms;
174 return result;
177 public void add_argument (ArgumentType key, Argument value) {
178 args.set (key, value);
181 public bool has_argument (ArgumentType key) {
182 return args.contains (key);
185 public Expression? get_expression (ArgumentType arg) {
186 var val = args.get (arg);
187 if (val != null) {
188 val.used = true;
189 return val.expression;
191 return null;
194 public string? get_string (ArgumentType arg) {
195 var lit = get_expression (arg) as StringLiteral;
196 if (lit != null) {
197 return lit.eval ();
199 return null;
202 public int get_integer (ArgumentType arg) {
203 var unary = get_expression (arg) as UnaryExpression;
204 if (unary != null && unary.operator == UnaryOperator.MINUS) {
205 var lit = unary.inner as IntegerLiteral;
206 if (lit != null) {
207 return -int.parse (lit.value);
209 } else {
210 var lit = get_expression (arg) as IntegerLiteral;
211 if (lit != null) {
212 return int.parse (lit.value);
216 return 0;
219 public bool get_bool (ArgumentType arg) {
220 var lit = get_expression (arg) as BooleanLiteral;
221 if (lit != null) {
222 return lit.value;
224 return false;
227 public SourceReference? get_source_reference (ArgumentType arg) {
228 var val = args.get (arg);
229 if (val != null) {
230 return val.source_reference;
232 return null;
236 class MetadataParser {
238 * Grammar:
239 * metadata ::= [ rule [ '\n' relativerule ]* ]
240 * rule ::= pattern ' ' [ args ]
241 * relativerule ::= [ access ] rule
242 * pattern ::= identifier [ access identifier ]*
243 * access ::= '.' | ':' | '::'
245 private Metadata tree = new Metadata ("");
246 private Scanner scanner;
247 private SourceLocation begin;
248 private SourceLocation end;
249 private SourceLocation old_end;
250 private TokenType current;
251 private Metadata parent_metadata;
253 public MetadataParser () {
254 tree.used = true;
257 SourceReference get_current_src () {
258 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
261 SourceReference get_src (SourceLocation begin) {
262 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
265 public Metadata parse_metadata (SourceFile metadata_file) {
266 scanner = new Scanner (metadata_file);
267 next ();
268 while (current != TokenType.EOF) {
269 if (!parse_rule ()) {
270 return Metadata.empty;
273 return tree;
276 TokenType next () {
277 old_end = end;
278 current = scanner.read_token (out begin, out end);
279 return current;
282 bool has_space () {
283 return old_end.pos != begin.pos;
286 bool has_newline () {
287 return old_end.line != begin.line;
290 string get_string () {
291 return ((string) begin.pos).substring (0, (int) (end.pos - begin.pos));
294 MetadataType? parse_metadata_access () {
295 switch (current) {
296 case TokenType.DOT:
297 next ();
298 return MetadataType.GENERIC;
299 case TokenType.COLON:
300 next ();
301 return MetadataType.PROPERTY;
302 case TokenType.DOUBLE_COLON:
303 next ();
304 return MetadataType.SIGNAL;
305 default:
306 return null;
310 string? parse_identifier (out SourceReference source_reference, bool is_glob) {
311 var begin = this.begin;
312 var builder = new StringBuilder ();
313 do {
314 if (is_glob && current == TokenType.STAR) {
315 builder.append_c ('*');
316 } else {
317 string str = null;
318 switch (current) {
319 case TokenType.IDENTIFIER:
320 case TokenType.UNOWNED:
321 case TokenType.OWNED:
322 case TokenType.GET:
323 case TokenType.NEW:
324 case TokenType.DEFAULT:
325 case TokenType.OUT:
326 case TokenType.REF:
327 case TokenType.VIRTUAL:
328 case TokenType.ABSTRACT:
329 str = get_string ();
330 break;
332 if (str == null) {
333 break;
335 builder.append (str);
337 source_reference = get_src (begin);
338 next ();
339 } while (!has_space ());
341 if (builder.str == "") {
342 if (is_glob) {
343 Report.error (get_src (begin), "expected pattern");
344 } else {
345 Report.error (get_src (begin), "expected identifier");
347 return null;
349 return builder.str;
352 Metadata? parse_pattern () {
353 Metadata metadata;
354 bool is_relative = false;
355 MetadataType? type = MetadataType.GENERIC;
356 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
357 // absolute pattern
358 parent_metadata = tree;
359 } else {
360 // relative pattern
361 type = parse_metadata_access ();
362 is_relative = true;
365 if (type == null) {
366 Report.error (get_current_src (), "expected pattern, `.', `:' or `::'");
367 return null;
370 if (parent_metadata == null) {
371 Report.error (get_current_src (), "cannot determinate parent metadata");
372 return null;
375 SourceReference src;
376 var pattern = parse_identifier (out src, true);
377 if (pattern == null) {
378 return null;
380 metadata = parent_metadata.get_child (pattern, type);
381 if (metadata == null) {
382 metadata = new Metadata (pattern, type, src);
383 parent_metadata.add_child (metadata);
386 while (current != TokenType.EOF && !has_space ()) {
387 type = parse_metadata_access ();
388 if (type == null) {
389 Report.error (get_current_src (), "expected `.', `:' or `::'");
390 return null;
393 pattern = parse_identifier (out src, true);
394 if (pattern == null) {
395 return null;
397 var child = metadata.get_child (pattern, type);
398 if (child == null) {
399 child = new Metadata (pattern, type, src);
400 metadata.add_child (child);
402 metadata = child;
404 if (!is_relative) {
405 parent_metadata = metadata;
408 return metadata;
411 Expression? parse_expression () {
412 var begin = this.begin;
413 var src = get_current_src ();
414 Expression expr = null;
415 switch (current) {
416 case TokenType.NULL:
417 expr = new NullLiteral (src);
418 break;
419 case TokenType.TRUE:
420 expr = new BooleanLiteral (true, src);
421 break;
422 case TokenType.FALSE:
423 expr = new BooleanLiteral (false, src);
424 break;
425 case TokenType.MINUS:
426 next ();
427 var inner = parse_expression ();
428 if (inner == null) {
429 Report.error (src, "expected expression after `-', got `%s'".printf (current.to_string ()));
430 } else {
431 expr = new UnaryExpression (UnaryOperator.MINUS, inner, get_src (begin));
433 return expr;
434 case TokenType.INTEGER_LITERAL:
435 expr = new IntegerLiteral (get_string (), src);
436 break;
437 case TokenType.REAL_LITERAL:
438 expr = new RealLiteral (get_string (), src);
439 break;
440 case TokenType.STRING_LITERAL:
441 expr = new StringLiteral (get_string (), src);
442 break;
443 case TokenType.IDENTIFIER:
444 expr = new MemberAccess (null, get_string (), src);
445 while (next () == TokenType.DOT) {
446 if (next () != TokenType.IDENTIFIER) {
447 Report.error (get_current_src (), "expected identifier got `%s'".printf (current.to_string ()));
448 break;
450 expr = new MemberAccess (expr, get_string (), get_current_src ());
452 return expr;
453 default:
454 Report.error (src, "expected literal or symbol got `%s'".printf (current.to_string ()));
455 break;
457 next ();
458 return expr;
461 bool parse_args (Metadata metadata) {
462 while (current != TokenType.EOF && has_space () && !has_newline ()) {
463 SourceReference src;
464 var id = parse_identifier (out src, false);
465 if (id == null) {
466 return false;
468 var arg_type = ArgumentType.from_string (id);
469 if (arg_type == null) {
470 Report.error (src, "unknown argument");
471 return false;
474 if (current != TokenType.ASSIGN) {
475 // threat as `true'
476 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, src), src));
477 continue;
479 next ();
481 Expression expr = parse_expression ();
482 if (expr == null) {
483 return false;
485 metadata.add_argument (arg_type, new Argument (expr, src));
488 return true;
491 bool parse_rule () {
492 var old_end = end;
493 var metadata = parse_pattern ();
494 if (metadata == null) {
495 return false;
498 if (current == TokenType.EOF || old_end.line != end.line) {
499 // eof or new rule
500 return true;
502 return parse_args (metadata);
506 class SymbolInfo {
507 public Symbol symbol;
508 public Metadata metadata;
509 // additional information from GIR
510 public HashMap<string,string> girdata;
513 class Alias {
514 public string name;
515 public string cname;
516 public DataType base_type;
517 public Symbol parent_symbol;
518 public SourceReference source_reference;
521 static GLib.Regex type_from_string_regex;
523 MarkupReader reader;
525 CodeContext context;
526 Namespace glib_ns;
528 SourceFile current_source_file;
529 Symbol current_symbol;
531 string current_gtype_struct_for;
532 SourceLocation begin;
533 SourceLocation end;
534 MarkupTokenType current_token;
536 string[] cheader_filenames;
538 ArrayList<Metadata> metadata_stack;
539 Metadata metadata;
540 ArrayList<HashMap<string,string>> girdata_stack;
541 HashMap<string,string> girdata;
543 HashMap<string,ArrayList<SymbolInfo>> current_symbols_info;
545 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
546 HashMap<Symbol,Symbol> concrete_symbols_map = new HashMap<Symbol,Symbol> ();
548 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
549 HashMap<UnresolvedSymbol,ArrayList<Symbol>> symbol_reparent_map = new HashMap<UnresolvedSymbol,ArrayList<Symbol>> (unresolved_symbol_hash, unresolved_symbol_equal);
550 HashMap<Namespace,ArrayList<Method>> namespace_methods = new HashMap<Namespace,ArrayList<Method>> ();
551 ArrayList<Alias> aliases = new ArrayList<Alias> ();
552 ArrayList<Interface> interfaces = new ArrayList<Interface> ();
554 HashMap<UnresolvedSymbol,ArrayList<Delegate>> gtype_callbacks;
557 * Parses all .gir source files in the specified code
558 * context and builds a code tree.
560 * @param context a code context
562 public void parse (CodeContext context) {
563 this.context = context;
564 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
565 context.accept (this);
567 resolve_gir_symbols ();
569 postprocess_interfaces ();
570 postprocess_reparenting ();
571 postprocess_aliases ();
572 postprocess_namespace_methods ();
575 public override void visit_source_file (SourceFile source_file) {
576 // collect gir namespaces
577 foreach (var node in source_file.get_nodes ()) {
578 if (node is Namespace) {
579 var ns = (Namespace) node;
580 var gir_namespace = source_file.gir_namespace;
581 if (gir_namespace == null) {
582 var a = ns.get_attribute ("CCode");
583 if (a != null && a.has_argument ("gir_namespace")) {
584 gir_namespace = a.get_string ("gir_namespace");
587 if (gir_namespace != null && gir_namespace != ns.name) {
588 var map_from = new UnresolvedSymbol (null, gir_namespace);
589 set_symbol_mapping (map_from, ns);
590 break;
595 if (source_file.filename.has_suffix (".gir")) {
596 parse_file (source_file);
600 public void parse_file (SourceFile source_file) {
601 metadata_stack = new ArrayList<Metadata> ();
602 metadata = Metadata.empty;
603 girdata_stack = new ArrayList<HashMap<string,string>> ();
605 // load metadata, first look into metadata directories then in the same directory of the .gir.
606 string? metadata_filename = context.get_metadata_path (source_file.filename);
607 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
608 var metadata_parser = new MetadataParser ();
609 var metadata_file = new SourceFile (context, source_file.file_type, metadata_filename);
610 context.add_source_file (metadata_file);
611 metadata = metadata_parser.parse_metadata (metadata_file);
614 this.current_source_file = source_file;
615 reader = new MarkupReader (source_file.filename);
617 // xml prolog
618 next ();
619 next ();
621 next ();
622 parse_repository ();
624 reader = null;
625 this.current_source_file = null;
628 void next () {
629 current_token = reader.read_token (out begin, out end);
631 // Skip *all* <doc> tags
632 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
633 skip_element();
636 void start_element (string name) {
637 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
638 // error
639 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
643 void end_element (string name) {
644 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
645 // error
646 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
648 next ();
651 SourceReference get_current_src () {
652 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
655 const string GIR_VERSION = "1.2";
657 void add_symbol_to_container (Symbol container, Symbol sym) {
658 var name = sym.name;
659 if (name == null && sym is CreationMethod) {
660 name = ".new";
662 if (container.scope.lookup (name) != null) {
663 // overridden by -custom.vala
664 return;
667 if (container is Class) {
668 unowned Class cl = (Class) container;
670 if (sym is Class) {
671 cl.add_class ((Class) sym);
672 } else if (sym is Constant) {
673 cl.add_constant ((Constant) sym);
674 } else if (sym is Enum) {
675 cl.add_enum ((Enum) sym);
676 } else if (sym is Field) {
677 cl.add_field ((Field) sym);
678 } else if (sym is Method) {
679 cl.add_method ((Method) sym);
680 } else if (sym is Property) {
681 cl.add_property ((Property) sym);
682 } else if (sym is Signal) {
683 cl.add_signal ((Signal) sym);
684 } else if (sym is Struct) {
685 cl.add_struct ((Struct) sym);
687 } else if (container is Enum) {
688 unowned Enum en = (Enum) container;
690 if (sym is EnumValue) {
691 en.add_value ((EnumValue) sym);
692 } else if (sym is Constant) {
693 en.add_constant ((Constant) sym);
694 } else if (sym is Method) {
695 en.add_method ((Method) sym);
697 } else if (container is Interface) {
698 unowned Interface iface = (Interface) container;
700 if (sym is Class) {
701 iface.add_class ((Class) sym);
702 } else if (sym is Constant) {
703 iface.add_constant ((Constant) sym);
704 } else if (sym is Enum) {
705 iface.add_enum ((Enum) sym);
706 } else if (sym is Field) {
707 iface.add_field ((Field) sym);
708 } else if (sym is Method) {
709 iface.add_method ((Method) sym);
710 } else if (sym is Property) {
711 iface.add_property ((Property) sym);
712 } else if (sym is Signal) {
713 iface.add_signal ((Signal) sym);
714 } else if (sym is Struct) {
715 iface.add_struct ((Struct) sym);
717 } else if (container is Namespace) {
718 unowned Namespace ns = (Namespace) container;
720 if (sym is Namespace) {
721 ns.add_namespace ((Namespace) sym);
722 } else if (sym is Class) {
723 ns.add_class ((Class) sym);
724 } else if (sym is Constant) {
725 ns.add_constant ((Constant) sym);
726 } else if (sym is Delegate) {
727 ns.add_delegate ((Delegate) sym);
728 } else if (sym is Enum) {
729 ns.add_enum ((Enum) sym);
730 } else if (sym is ErrorDomain) {
731 ns.add_error_domain ((ErrorDomain) sym);
732 } else if (sym is Field) {
733 ns.add_field ((Field) sym);
734 } else if (sym is Interface) {
735 ns.add_interface ((Interface) sym);
736 } else if (sym is Method) {
737 ns.add_method ((Method) sym);
738 } else if (sym is Namespace) {
739 ns.add_namespace ((Namespace) sym);
740 } else if (sym is Struct) {
741 ns.add_struct ((Struct) sym);
743 } else if (container is Struct) {
744 unowned Struct st = (Struct) container;
746 if (sym is Constant) {
747 st.add_constant ((Constant) sym);
748 } else if (sym is Field) {
749 st.add_field ((Field) sym);
750 } else if (sym is Method) {
751 st.add_method ((Method) sym);
752 } else if (sym is Property) {
753 st.add_property ((Property) sym);
755 } else {
756 Report.error (sym.source_reference, "impossible to add to container `%s'".printf (container.name));
760 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
761 UnresolvedSymbol? sym = null;
762 foreach (unowned string s in symbol_string.split (".")) {
763 sym = new UnresolvedSymbol (sym, s, source_reference);
765 if (sym == null) {
766 Report.error (source_reference, "a symbol must be specified");
768 return sym;
771 UnresolvedSymbol get_unresolved_symbol (Symbol symbol) {
772 if (symbol is UnresolvedSymbol) {
773 return (UnresolvedSymbol) symbol;
775 var sym = new UnresolvedSymbol (null, symbol.name);
776 var result = sym;
777 var cur = symbol.parent_node as Symbol;
778 while (cur != null && cur.name != null) {
779 sym = new UnresolvedSymbol (sym, cur.name);
780 cur = cur.parent_node as Symbol;
782 return result;
785 void set_symbol_mapping (Symbol map_from, Symbol map_to) {
786 // last mapping is the most up-to-date
787 if (map_from is UnresolvedSymbol) {
788 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
789 } else {
790 concrete_symbols_map[map_from] = map_to;
794 void assume_parameter_names (Signal sig, Symbol sym) {
795 Iterator<Parameter> iter;
796 if (sym is Method) {
797 iter = ((Method) sym).get_parameters ().iterator ();
798 } else {
799 iter = ((Delegate) sym).get_parameters ().iterator ();
801 foreach (var param in sig.get_parameters ()) {
802 if (!iter.next ()) {
803 // unreachable for valid GIR
804 break;
806 param.name = iter.get ().name;
810 SymbolInfo? add_symbol_info (Symbol symbol) {
811 var name = symbol.name;
812 if (symbol is CreationMethod && name == null) {
813 name = ".new";
816 var info = new SymbolInfo ();
817 info.symbol = symbol;
818 info.metadata = metadata;
819 info.girdata = girdata;
820 var colliding = current_symbols_info[name];
821 if (colliding == null) {
822 colliding = new ArrayList<SymbolInfo> ();
823 current_symbols_info[name] = colliding;
825 colliding.add (info);
826 return info;
829 SymbolInfo? find_invoker (Method method) {
830 /* most common use case is invoker has at least the given method prefix
831 and the same parameter names */
832 var prefix = "%s_".printf (method.name);
833 foreach (var name in current_symbols_info.get_keys ()) {
834 if (!name.has_prefix (prefix)) {
835 continue;
837 var infos = current_symbols_info[name];
838 foreach (var cinfo in infos) {
839 Method? invoker = cinfo.symbol as Method;
840 if (invoker == null || (method.get_parameters ().size != invoker.get_parameters ().size)) {
841 continue;
843 var iter = invoker.get_parameters ().iterator ();
844 foreach (var param in method.get_parameters ()) {
845 assert (iter.next ());
846 if (param.name != iter.get ().name) {
847 invoker = null;
848 break;
851 if (invoker != null) {
852 return cinfo;
857 return null;
860 void merge (SymbolInfo info, ArrayList<SymbolInfo> colliding, HashSet<SymbolInfo> merged) {
861 if (info.symbol is Struct) {
862 var gtype_struct_for = info.girdata["glib:is-gtype-struct-for"];
863 if (gtype_struct_for != null && current_symbols_info.contains (gtype_struct_for)) {
864 var iface = current_symbols_info.get (gtype_struct_for).get (0).symbol as Interface;
865 if (iface != null) {
866 // set the interface struct name
867 iface.set_type_cname (((Struct) info.symbol).get_cname ());
869 merged.add (info);
871 } else if (info.symbol is Property) {
872 var prop = (Property) info.symbol;
873 foreach (var cinfo in colliding) {
874 var sym = cinfo.symbol;
875 if (sym is Signal || sym is Field) {
876 // properties take precedence
877 merged.add (cinfo);
878 } else if (sym is Method) {
879 // assume method is getter
880 merged.add (cinfo);
883 var getter_name = "get_%s".printf (prop.name);
884 var setter_name = "set_%s".printf (prop.name);
885 if (prop.get_accessor != null && current_symbols_info.contains (getter_name)) {
886 var getter_list = current_symbols_info[getter_name];
887 foreach (var getter_info in getter_list) {
888 if (getter_info.symbol is Method) {
889 prop.no_accessor_method = false;
890 prop.get_accessor.value_type.value_owned = ((Method) getter_info.symbol).return_type.value_owned;
891 break;
894 } else if (prop.set_accessor != null && current_symbols_info.contains (setter_name)) {
895 prop.no_accessor_method = false;
897 } else if (info.symbol is Signal) {
898 var sig = (Signal) info.symbol;
899 foreach (var cinfo in colliding) {
900 var sym = cinfo.symbol;
901 if (sym is Method) {
902 var method = (Method) sym;
903 if (method.is_virtual) {
904 sig.is_virtual = true;
905 } else {
906 sig.has_emitter = true;
908 assume_parameter_names (sig, method);
909 merged.add (cinfo);
910 } else if (sym is Field) {
911 merged.add (cinfo);
914 } else if (info.symbol is Method && !(info.symbol is CreationMethod)) {
915 var m = (Method) info.symbol;
916 foreach (var cinfo in colliding) {
917 var sym = cinfo.symbol;
918 if (sym != m && m.is_virtual && sym is Method) {
919 bool different_invoker = false;
920 foreach (var attr in m.attributes) {
921 if (attr.name == "NoWrapper") {
922 /* no invoker but this method has the same name,
923 most probably the invoker has a different name
924 and g-ir-scanner missed it */
925 var invoker = find_invoker (m);
926 if (invoker != null) {
927 m.vfunc_name = m.name;
928 m.name = invoker.symbol.name;
929 m.attributes.remove (attr);
930 merged.add (invoker);
931 different_invoker = true;
932 break;
936 if (!different_invoker) {
937 merged.add (cinfo);
941 if (m.coroutine) {
942 // handle async methods
943 string finish_method_base;
944 if (m.name.has_suffix ("_async")) {
945 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
946 } else {
947 finish_method_base = m.name;
949 SymbolInfo finish_method_info = null;
950 if (current_symbols_info.contains (finish_method_base + "_finish")) {
951 finish_method_info = current_symbols_info.get (finish_method_base + "_finish")[0];
954 // check if the method is using non-standard finish method name
955 if (finish_method_info == null) {
956 var method_cname = m.get_finish_cname ();
957 foreach (var infos in current_symbols_info.get_values ()) {
958 foreach (var minfo in infos) {
959 if (minfo.symbol is Method && ((Method) minfo.symbol).get_cname () == method_cname) {
960 finish_method_info = minfo;
961 break;
964 if (finish_method_info != null) {
965 break;
970 if (finish_method_info != null && finish_method_info.symbol is Method) {
971 var finish_method = (Method) finish_method_info.symbol;
972 Method method;
973 if (finish_method is CreationMethod) {
974 method = new CreationMethod (((CreationMethod) finish_method).class_name, null, m.source_reference);
975 method.access = m.access;
976 method.binding = m.binding;
977 method.external = true;
978 method.coroutine = true;
979 method.has_construct_function = finish_method.has_construct_function;
980 method.attributes = m.attributes.copy ();
981 method.set_cname (m.get_cname ());
982 if (finish_method_base == "new") {
983 method.name = null;
984 } else if (finish_method_base.has_prefix ("new_")) {
985 method.name = m.name.substring ("new_".length);
987 foreach (var param in m.get_parameters ()) {
988 method.add_parameter (param);
990 info.symbol = method;
991 } else {
992 method = m;
994 method.return_type = finish_method.return_type.copy ();
995 method.no_array_length = finish_method.no_array_length;
996 method.array_null_terminated = finish_method.array_null_terminated;
997 foreach (var param in finish_method.get_parameters ()) {
998 if (param.direction == ParameterDirection.OUT) {
999 var async_param = param.copy ();
1000 if (method.scope.lookup (param.name) != null) {
1001 // parameter name conflict
1002 async_param.name += "_out";
1004 method.add_parameter (async_param);
1007 foreach (DataType error_type in finish_method.get_error_types ()) {
1008 method.add_error_type (error_type.copy ());
1010 merged.add (finish_method_info);
1013 } else if (info.symbol is Field) {
1014 foreach (var cinfo in colliding) {
1015 var sym = cinfo.symbol;
1016 if (sym is Method) {
1017 // assume method is getter
1018 merged.add (cinfo);
1022 var field = (Field) info.symbol;
1023 if (field.variable_type is ArrayType) {
1024 SymbolInfo array_length = null;
1025 if (current_symbols_info.contains ("n_%s".printf (field.name))) {
1026 array_length = current_symbols_info.get ("n_%s".printf (field.name)).get (0);
1027 } else if (current_symbols_info.contains ("%s_length".printf (field.name))) {
1028 array_length = current_symbols_info.get ("%s_length".printf (field.name)).get (0);
1030 if (array_length != null) {
1031 // array has length
1032 field.set_array_length_cname (array_length.symbol.name);
1033 field.no_array_length = false;
1034 merged.add (array_length);
1040 void postprocess_symbol (Symbol symbol, Metadata metadata) {
1041 // deprecation
1042 symbol.replacement = metadata.get_string (ArgumentType.REPLACEMENT);
1043 symbol.deprecated_since = element_get_string ("deprecated-version", ArgumentType.DEPRECATED_SINCE);
1044 symbol.deprecated = metadata.get_bool (ArgumentType.DEPRECATED) || symbol.replacement != null || symbol.deprecated_since != null;
1046 // mark to be reparented
1047 if (metadata.has_argument (ArgumentType.PARENT)) {
1048 var target_symbol = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
1049 var reparent_list = symbol_reparent_map[target_symbol];
1050 if (reparent_list == null) {
1051 reparent_list = new ArrayList<Symbol>();
1052 symbol_reparent_map[target_symbol] = reparent_list;
1054 reparent_list.add (symbol);
1056 // if referenceable, map unresolved references to point to the new place
1057 if (symbol is Namespace || symbol is TypeSymbol) {
1058 set_symbol_mapping (symbol, new UnresolvedSymbol (target_symbol, symbol.name));
1063 void merge_add_process (Symbol container) {
1064 var merged = new HashSet<SymbolInfo> ();
1065 foreach (var colliding in current_symbols_info.get_values ()) {
1066 foreach (var info in colliding) {
1067 merge (info, colliding, merged);
1071 foreach (var infos in current_symbols_info.get_values ()) {
1072 foreach (var info in infos) {
1073 if (merged.contains (info) || info.metadata.get_bool (ArgumentType.HIDDEN)) {
1074 continue;
1076 if (!(current_symbol is Namespace && info.symbol is Method) && !info.metadata.has_argument (ArgumentType.PARENT)) {
1077 add_symbol_to_container (container, info.symbol);
1079 postprocess_symbol (info.symbol, info.metadata);
1084 Metadata get_current_metadata () {
1085 var name = reader.name;
1086 var child_name = reader.get_attribute ("name");
1087 if (child_name == null) {
1088 return Metadata.empty;
1091 var type = MetadataType.GENERIC;
1092 if (name == "glib:signal") {
1093 child_name = child_name.replace ("-", "_");
1094 type = MetadataType.SIGNAL;
1095 } else if (name == "property") {
1096 type = MetadataType.PROPERTY;
1099 return metadata.match_child (child_name, type);
1102 bool push_metadata () {
1103 var new_metadata = get_current_metadata ();
1104 // skip ?
1105 if (new_metadata.has_argument (ArgumentType.SKIP)) {
1106 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1107 return false;
1109 } else if (reader.get_attribute ("introspectable") == "0") {
1110 return false;
1113 metadata_stack.add (metadata);
1114 metadata = new_metadata;
1115 girdata_stack.add (girdata);
1116 girdata = new HashMap<string,string> (str_hash, str_equal);
1118 return true;
1121 void pop_metadata () {
1122 metadata = metadata_stack[metadata_stack.size - 1];
1123 metadata_stack.remove_at (metadata_stack.size - 1);
1124 girdata = girdata_stack[girdata_stack.size - 1];
1125 girdata_stack.remove_at (girdata_stack.size - 1);
1128 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1129 int type_arguments_length = (int) type_arguments.length;
1130 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1132 int depth = 0;
1133 for (var c = 0 ; c < type_arguments_length ; c++) {
1134 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1135 depth++;
1136 current.append_unichar (type_arguments[c]);
1137 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1138 depth--;
1139 current.append_unichar (type_arguments[c]);
1140 } else if (type_arguments[c] == ',') {
1141 if (depth == 0) {
1142 var dt = parse_type_from_string (current.str, true, source_reference);
1143 if (dt == null) {
1144 return false;
1146 parent_type.add_type_argument (dt);
1147 current.truncate ();
1148 } else {
1149 current.append_unichar (type_arguments[c]);
1151 } else {
1152 current.append_unichar (type_arguments[c]);
1156 var dt = parse_type_from_string (current.str, true, source_reference);
1157 if (dt == null) {
1158 return false;
1160 parent_type.add_type_argument (dt);
1162 return true;
1165 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1166 if (type_from_string_regex == null) {
1167 try {
1168 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[,*\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1169 } catch (GLib.RegexError e) {
1170 GLib.error ("Unable to compile regex: %s", e.message);
1174 GLib.MatchInfo match;
1175 if (!type_from_string_regex.match (type_string, 0, out match)) {
1176 Report.error (source_reference, "unable to parse type");
1177 return null;
1180 DataType? type = null;
1182 var ownership_data = match.fetch (1);
1183 var type_name = match.fetch (2);
1184 var type_arguments_data = match.fetch (3);
1185 var pointers_data = match.fetch (4);
1186 var array_data = match.fetch (5);
1187 var nullable_data = match.fetch (6);
1189 var nullable = nullable_data != null && nullable_data.length > 0;
1191 if (ownership_data == null && type_name == "void") {
1192 if (array_data == null && !nullable) {
1193 type = new VoidType (source_reference);
1194 if (pointers_data != null) {
1195 for (int i=0; i < pointers_data.length; i++) {
1196 type = new PointerType (type);
1199 return type;
1200 } else {
1201 Report.error (source_reference, "invalid void type");
1202 return null;
1206 bool value_owned = owned_by_default;
1208 if (ownership_data == "owned") {
1209 if (owned_by_default) {
1210 Report.error (source_reference, "unexpected `owned' keyword");
1211 } else {
1212 value_owned = true;
1214 } else if (ownership_data == "unowned") {
1215 if (owned_by_default) {
1216 value_owned = false;
1217 } else {
1218 Report.error (source_reference, "unexpected `unowned' keyword");
1219 return null;
1223 var sym = parse_symbol_from_string (type_name, source_reference);
1224 if (sym == null) {
1225 return null;
1227 type = new UnresolvedType.from_symbol (sym, source_reference);
1229 if (type_arguments_data != null && type_arguments_data.length > 0) {
1230 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1231 return null;
1235 if (pointers_data != null) {
1236 for (int i=0; i < pointers_data.length; i++) {
1237 type = new PointerType (type);
1241 if (array_data != null) {
1242 type = new ArrayType (type, (int) array_data.length - 1, source_reference);
1245 type.nullable = nullable;
1246 type.value_owned = value_owned;
1247 return type;
1250 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1251 var str = metadata.get_string (arg_type);
1252 if (str == null) {
1253 str = reader.get_attribute (attribute_name);
1255 return str;
1259 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1260 * If type arguments change, the C declaration is not affected.
1262 DataType? element_get_type (DataType orig_type, bool owned_by_default, out bool changed = null) {
1263 changed = false;
1264 var type = orig_type;
1266 if (metadata.has_argument (ArgumentType.TYPE)) {
1267 var new_type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1268 changed = true;
1269 return new_type;
1272 if (type is VoidType) {
1273 return type;
1276 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1277 type.remove_all_type_arguments ();
1278 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1281 if (metadata.get_bool (ArgumentType.ARRAY)) {
1282 type = new ArrayType (type, 1, type.source_reference);
1283 changed = true;
1286 if (owned_by_default) {
1287 if (metadata.has_argument (ArgumentType.UNOWNED)) {
1288 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED);
1290 } else {
1291 if (metadata.has_argument (ArgumentType.OWNED)) {
1292 type.value_owned = metadata.get_bool (ArgumentType.OWNED);
1295 if (metadata.has_argument (ArgumentType.NULLABLE)) {
1296 type.nullable = metadata.get_bool (ArgumentType.NULLABLE);
1299 return type;
1302 string? element_get_name (bool remap = false) {
1303 var name = reader.get_attribute ("name");
1304 var orig_name = name;
1305 var pattern = metadata.get_string (ArgumentType.NAME);
1306 if (pattern != null) {
1307 try {
1308 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1309 GLib.MatchInfo match;
1310 if (!regex.match (name, 0, out match)) {
1311 name = pattern;
1312 } else {
1313 var matched = match.fetch (1);
1314 if (matched != null && matched.length > 0) {
1315 name = matched;
1316 } else {
1317 name = pattern;
1320 } catch (Error e) {
1321 name = pattern;
1323 } else {
1324 if (name != null && name.has_suffix ("Enum")) {
1325 name = name.substring (0, name.length - "Enum".length);
1328 if (name != orig_name && remap) {
1329 set_symbol_mapping (parse_symbol_from_string (orig_name), parse_symbol_from_string (name));
1332 return name;
1335 void set_array_ccode (Symbol sym, ParameterInfo info) {
1336 if (sym is Method) {
1337 var m = (Method) sym;
1338 m.carray_length_parameter_position = info.vala_idx;
1339 } else if (sym is Delegate) {
1340 var d = (Delegate) sym;
1341 d.carray_length_parameter_position = info.vala_idx;
1342 } else {
1343 var param = (Parameter) sym;
1344 param.carray_length_parameter_position = info.vala_idx;
1345 param.set_array_length_cname (info.param.name);
1347 if (info.param.variable_type.to_qualified_string () != "int") {
1348 var unresolved_type = (UnresolvedType) info.param.variable_type;
1349 var resolved_struct = resolve_symbol (glib_ns.scope, unresolved_type.unresolved_symbol) as Struct;
1350 if (resolved_struct != null) {
1351 if (sym is Method) {
1352 var m = (Method) sym;
1353 m.array_length_type = resolved_struct.get_cname ();
1354 } else {
1355 var param = (Parameter) sym;
1356 param.array_length_type = resolved_struct.get_cname ();
1362 void parse_repository () {
1363 start_element ("repository");
1364 if (reader.get_attribute ("version") != GIR_VERSION) {
1365 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1366 return;
1368 next ();
1369 while (current_token == MarkupTokenType.START_ELEMENT) {
1370 if (reader.name == "namespace") {
1371 var ns = parse_namespace ();
1372 if (ns != null) {
1373 context.root.add_namespace (ns);
1375 } else if (reader.name == "include") {
1376 parse_include ();
1377 } else if (reader.name == "package") {
1378 var pkg = parse_package ();
1379 if (context.has_package (pkg)) {
1380 // package already provided elsewhere, stop parsing this GIR
1381 return;
1382 } else {
1383 context.add_package (pkg);
1385 } else if (reader.name == "c:include") {
1386 parse_c_include ();
1387 } else {
1388 // error
1389 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1390 skip_element ();
1393 end_element ("repository");
1395 report_unused_metadata (metadata);
1398 void parse_include () {
1399 start_element ("include");
1400 var pkg = reader.get_attribute ("name");
1401 var version = reader.get_attribute ("version");
1402 if (version != null) {
1403 pkg = "%s-%s".printf (pkg, version);
1405 // add the package to the queue
1406 context.add_external_package (pkg);
1407 next ();
1408 end_element ("include");
1411 string parse_package () {
1412 start_element ("package");
1413 var pkg = reader.get_attribute ("name");
1414 next ();
1415 end_element ("package");
1416 return pkg;
1419 void parse_c_include () {
1420 start_element ("c:include");
1421 cheader_filenames += reader.get_attribute ("name");
1422 next ();
1423 end_element ("c:include");
1426 void skip_element () {
1427 next ();
1429 int level = 1;
1430 while (level > 0) {
1431 if (current_token == MarkupTokenType.START_ELEMENT) {
1432 level++;
1433 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1434 level--;
1435 } else if (current_token == MarkupTokenType.EOF) {
1436 Report.error (get_current_src (), "unexpected end of file");
1437 break;
1439 next ();
1443 Namespace? parse_namespace () {
1444 start_element ("namespace");
1446 bool new_namespace = false;
1447 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
1448 string namespace_name = cprefix;
1449 string gir_namespace = reader.get_attribute ("name");
1450 string gir_version = reader.get_attribute ("version");
1451 if (namespace_name == null) {
1452 namespace_name = gir_namespace;
1454 current_source_file.gir_namespace = gir_namespace;
1455 current_source_file.gir_version = gir_version;
1457 var ns_metadata = metadata.match_child (gir_namespace);
1458 if (ns_metadata.has_argument (ArgumentType.NAME)) {
1459 namespace_name = ns_metadata.get_string (ArgumentType.NAME);
1462 var ns = context.root.scope.lookup (namespace_name) as Namespace;
1463 if (ns == null) {
1464 ns = new Namespace (namespace_name, get_current_src ());
1465 new_namespace = true;
1466 } else {
1467 if (ns.external_package) {
1468 ns.attributes = null;
1469 ns.source_reference = get_current_src ();
1473 if (gir_namespace != ns.name) {
1474 set_symbol_mapping (new UnresolvedSymbol (null, gir_namespace), ns);
1477 if (cprefix != null) {
1478 ns.add_cprefix (cprefix);
1479 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
1482 if (ns_metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1483 var val = ns_metadata.get_string (ArgumentType.CHEADER_FILENAME);
1484 foreach (string filename in val.split (",")) {
1485 ns.add_cheader_filename (filename);
1487 } else {
1488 foreach (string c_header in cheader_filenames) {
1489 ns.add_cheader_filename (c_header);
1493 next ();
1494 var current_namespace_methods = namespace_methods[ns];
1495 if (current_namespace_methods == null) {
1496 current_namespace_methods = new ArrayList<Method> ();
1497 namespace_methods[ns] = current_namespace_methods;
1499 var old_symbols_info = current_symbols_info;
1500 var old_symbol = current_symbol;
1501 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1502 current_symbol = ns;
1503 gtype_callbacks = new HashMap<UnresolvedSymbol,ArrayList<Delegate>> (unresolved_symbol_hash, unresolved_symbol_equal);
1504 while (current_token == MarkupTokenType.START_ELEMENT) {
1505 if (!push_metadata ()) {
1506 skip_element ();
1507 continue;
1510 if (reader.name == "alias") {
1511 var alias = parse_alias ();
1512 aliases.add (alias);
1513 } else if (reader.name == "enumeration") {
1514 if (reader.get_attribute ("glib:error-quark") != null) {
1515 add_symbol_info (parse_error_domain ());
1516 } else {
1517 add_symbol_info (parse_enumeration ());
1519 } else if (reader.name == "bitfield") {
1520 add_symbol_info (parse_bitfield ());
1521 } else if (reader.name == "function") {
1522 var method = parse_method ("function");
1523 add_symbol_info (method);
1524 current_namespace_methods.add (method);
1525 } else if (reader.name == "callback") {
1526 add_symbol_info (parse_callback ());
1527 } else if (reader.name == "record") {
1528 if (reader.get_attribute ("glib:get-type") != null) {
1529 add_symbol_info (parse_boxed ("record"));
1530 } else {
1531 if (!reader.get_attribute ("name").has_suffix ("Private")) {
1532 add_symbol_info (parse_record ());
1533 } else {
1534 skip_element ();
1537 } else if (reader.name == "class") {
1538 add_symbol_info (parse_class ());
1539 } else if (reader.name == "interface") {
1540 var iface = parse_interface ();
1541 add_symbol_info (iface);
1542 interfaces.add (iface);
1543 } else if (reader.name == "glib:boxed") {
1544 add_symbol_info (parse_boxed ("glib:boxed"));
1545 } else if (reader.name == "union") {
1546 add_symbol_info (parse_union ());
1547 } else if (reader.name == "constant") {
1548 add_symbol_info (parse_constant ());
1549 } else {
1550 // error
1551 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
1552 skip_element ();
1555 pop_metadata ();
1557 end_element ("namespace");
1559 merge_add_process (ns);
1560 current_symbols_info = old_symbols_info;
1561 current_symbol = old_symbol;
1562 postprocess_gtype_callbacks (ns);
1564 if (!new_namespace) {
1565 ns = null;
1568 return ns;
1571 Alias parse_alias () {
1572 // alias has no type information
1573 start_element ("alias");
1574 var alias = new Alias ();
1575 alias.source_reference = get_current_src ();
1576 alias.name = reader.get_attribute ("name");
1577 alias.cname = reader.get_attribute ("c:type");
1578 alias.parent_symbol = current_symbol;
1579 next ();
1581 alias.base_type = element_get_type (parse_type (null, null, true), true);
1583 end_element ("alias");
1584 return alias;
1587 private void calculate_common_prefix (ref string common_prefix, string cname) {
1588 if (common_prefix == null) {
1589 common_prefix = cname;
1590 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
1591 // FIXME: could easily be made faster
1592 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1594 } else {
1595 while (!cname.has_prefix (common_prefix)) {
1596 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1599 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
1600 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
1601 // enum values may not consist solely of digits
1602 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1606 Enum parse_enumeration () {
1607 start_element ("enumeration");
1609 var en = new Enum (element_get_name (), get_current_src ());
1610 en.access = SymbolAccessibility.PUBLIC;
1612 string enum_cname = reader.get_attribute ("c:type");
1613 if (enum_cname != null) {
1614 en.set_cname (enum_cname);
1617 next ();
1619 string common_prefix = null;
1621 var old_symbol = current_symbol;
1622 current_symbol = en;
1623 while (current_token == MarkupTokenType.START_ELEMENT) {
1624 if (!push_metadata ()) {
1625 skip_element ();
1626 continue;
1629 if (reader.name == "member") {
1630 var ev = parse_enumeration_member ();
1631 en.add_value (ev);
1632 calculate_common_prefix (ref common_prefix, ev.get_cname ());
1633 } else {
1634 // error
1635 Report.error (get_current_src (), "unknown child element `%s' in `enumaration'".printf (reader.name));
1636 skip_element ();
1639 pop_metadata ();
1642 en.set_cprefix (common_prefix);
1644 end_element ("enumeration");
1645 current_symbol = old_symbol;
1646 return en;
1649 ErrorDomain parse_error_domain () {
1650 start_element ("enumeration");
1652 var ed = new ErrorDomain (element_get_name (true), get_current_src ());
1653 ed.access = SymbolAccessibility.PUBLIC;
1655 string enum_cname = reader.get_attribute ("c:type");
1656 if (enum_cname != null) {
1657 ed.set_cname (enum_cname);
1660 next ();
1662 string common_prefix = null;
1663 var old_symbol = current_symbol;
1664 current_symbol = ed;
1665 while (current_token == MarkupTokenType.START_ELEMENT) {
1666 if (!push_metadata ()) {
1667 skip_element ();
1668 continue;
1671 if (reader.name == "member") {
1672 ErrorCode ec = parse_error_member ();
1673 ed.add_code (ec);
1674 calculate_common_prefix (ref common_prefix, ec.get_cname ());
1675 } else {
1676 // error
1677 Report.error (get_current_src (), "unknown child element `%s' in `enumeration'".printf (reader.name));
1678 skip_element ();
1681 pop_metadata ();
1684 ed.set_cprefix (common_prefix);
1686 end_element ("enumeration");
1687 current_symbol = old_symbol;
1688 return ed;
1691 Enum parse_bitfield () {
1692 start_element ("bitfield");
1693 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1694 en.access = SymbolAccessibility.PUBLIC;
1695 next ();
1696 var old_symbol = current_symbol;
1697 current_symbol = en;
1698 while (current_token == MarkupTokenType.START_ELEMENT) {
1699 if (!push_metadata ()) {
1700 skip_element ();
1701 continue;
1704 if (reader.name == "member") {
1705 en.add_value (parse_enumeration_member ());
1706 } else {
1707 // error
1708 Report.error (get_current_src (), "unknown child element `%s' in `bitfield'".printf (reader.name));
1709 skip_element ();
1712 pop_metadata ();
1714 end_element ("bitfield");
1715 current_symbol = en;
1716 return en;
1719 EnumValue parse_enumeration_member () {
1720 start_element ("member");
1721 var ev = new EnumValue (reader.get_attribute ("name").up ().replace ("-", "_"), null, get_current_src ());
1722 ev.set_cname (reader.get_attribute ("c:identifier"));
1723 next ();
1724 end_element ("member");
1725 return ev;
1728 ErrorCode parse_error_member () {
1729 start_element ("member");
1731 ErrorCode ec;
1732 string name = reader.get_attribute ("name").up ().replace ("-", "_");
1733 string value = reader.get_attribute ("value");
1734 if (value != null) {
1735 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
1736 } else {
1737 ec = new ErrorCode (name);
1740 next ();
1741 end_element ("member");
1742 return ec;
1745 DataType parse_return_value (out string? ctype = null) {
1746 start_element ("return-value");
1747 string transfer = reader.get_attribute ("transfer-ownership");
1748 string allow_none = reader.get_attribute ("allow-none");
1749 next ();
1750 var transfer_elements = transfer == "full";
1751 var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
1752 if (transfer == "full" || transfer == "container") {
1753 type.value_owned = true;
1755 if (allow_none == "1") {
1756 type.nullable = true;
1758 end_element ("return-value");
1759 return type;
1762 Parameter parse_parameter (out int array_length_idx = null, out int closure_idx = null, out int destroy_idx = null, out string? scope = null, string? default_name = null) {
1763 Parameter param;
1765 if (&array_length_idx != null) {
1766 array_length_idx = -1;
1768 if (&closure_idx != null) {
1769 closure_idx = -1;
1771 if (&destroy_idx != null) {
1772 destroy_idx = -1;
1775 start_element ("parameter");
1776 string name = reader.get_attribute ("name");
1777 if (name == null) {
1778 name = default_name;
1780 string direction = null;
1781 if (metadata.has_argument (ArgumentType.OUT)) {
1782 if (metadata.get_bool (ArgumentType.OUT)) {
1783 direction = "out";
1784 } // null otherwise
1785 } else if (metadata.has_argument (ArgumentType.REF)) {
1786 if (metadata.get_bool (ArgumentType.REF)) {
1787 direction = "inout";
1788 } // null otherwise
1789 } else {
1790 direction = reader.get_attribute ("direction");
1792 string transfer = reader.get_attribute ("transfer-ownership");
1793 string allow_none = reader.get_attribute ("allow-none");
1795 if (&scope != null) {
1796 scope = reader.get_attribute ("scope");
1799 string closure = reader.get_attribute ("closure");
1800 string destroy = reader.get_attribute ("destroy");
1801 if (closure != null && &closure_idx != null) {
1802 closure_idx = int.parse (closure);
1804 if (destroy != null && &destroy_idx != null) {
1805 destroy_idx = int.parse (destroy);
1808 next ();
1809 if (reader.name == "varargs") {
1810 start_element ("varargs");
1811 next ();
1812 param = new Parameter.with_ellipsis (get_current_src ());
1813 end_element ("varargs");
1814 } else {
1815 string ctype;
1816 var type = parse_type (out ctype, out array_length_idx, transfer == "full");
1817 bool changed;
1818 type = element_get_type (type, false, out changed);
1819 if (!changed) {
1820 // discard ctype, duplicated information
1821 ctype = null;
1824 if (type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
1825 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
1828 if (transfer == "full" || transfer == "container" || destroy != null) {
1829 type.value_owned = true;
1831 if (allow_none == "1") {
1832 type.nullable = true;
1834 param = new Parameter (name, type, get_current_src ());
1835 param.ctype = ctype;
1836 if (direction == "out") {
1837 param.direction = ParameterDirection.OUT;
1838 } else if (direction == "inout") {
1839 param.direction = ParameterDirection.REF;
1841 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
1843 end_element ("parameter");
1844 return param;
1847 DataType parse_type (out string? ctype = null, out int array_length_index = null, bool transfer_elements = false, out bool no_array_length = null, out bool array_null_terminated = null) {
1848 bool is_array = false;
1849 string type_name = reader.get_attribute ("name");
1851 if (&array_length_index != null) {
1852 array_length_index = -1;
1855 if (reader.name == "array") {
1856 is_array = true;
1857 start_element ("array");
1859 if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
1860 if (reader.get_attribute ("length") != null
1861 && &array_length_index != null) {
1862 array_length_index = int.parse (reader.get_attribute ("length"));
1864 next ();
1865 var element_type = parse_type ();
1866 end_element ("array");
1867 return new ArrayType (element_type, 1, null);
1869 } else if (reader.name == "callback"){
1870 var callback = parse_callback ();
1871 return new DelegateType (callback);
1872 } else {
1873 start_element ("type");
1876 if (&ctype != null) {
1877 ctype = reader.get_attribute("c:type");
1880 next ();
1882 if (type_name == "GLib.PtrArray"
1883 && current_token == MarkupTokenType.START_ELEMENT) {
1884 type_name = "GLib.GenericArray";
1887 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
1889 // type arguments / element types
1890 while (current_token == MarkupTokenType.START_ELEMENT) {
1891 var element_type = parse_type ();
1892 element_type.value_owned = transfer_elements;
1893 type.add_type_argument (element_type);
1896 end_element (is_array ? "array" : "type");
1897 return type;
1900 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
1901 if (&no_array_length != null) {
1902 no_array_length = false;
1904 if (&array_null_terminated != null) {
1905 array_null_terminated = false;
1908 DataType type;
1909 if (type_name == "none") {
1910 type = new VoidType (get_current_src ());
1911 } else if (type_name == "gpointer") {
1912 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
1913 } else if (type_name == "GObject.Strv") {
1914 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, get_current_src ());
1915 if (&no_array_length != null) {
1916 no_array_length = true;
1918 if (&array_null_terminated != null) {
1919 array_null_terminated = true;
1921 } else {
1922 bool known_type = true;
1923 if (type_name == "utf8") {
1924 type_name = "string";
1925 } else if (type_name == "gboolean") {
1926 type_name = "bool";
1927 } else if (type_name == "gchar") {
1928 type_name = "char";
1929 } else if (type_name == "gshort") {
1930 type_name = "short";
1931 } else if (type_name == "gushort") {
1932 type_name = "ushort";
1933 } else if (type_name == "gint") {
1934 type_name = "int";
1935 } else if (type_name == "guint") {
1936 type_name = "uint";
1937 } else if (type_name == "glong") {
1938 if (ctype != null && ctype.has_prefix ("gssize")) {
1939 type_name = "ssize_t";
1940 } else {
1941 type_name = "long";
1943 } else if (type_name == "gulong") {
1944 if (ctype != null && ctype.has_prefix ("gsize")) {
1945 type_name = "size_t";
1946 } else {
1947 type_name = "ulong";
1949 } else if (type_name == "gint8") {
1950 type_name = "int8";
1951 } else if (type_name == "guint8") {
1952 type_name = "uint8";
1953 } else if (type_name == "gint16") {
1954 type_name = "int16";
1955 } else if (type_name == "guint16") {
1956 type_name = "uint16";
1957 } else if (type_name == "gint32") {
1958 type_name = "int32";
1959 } else if (type_name == "guint32") {
1960 type_name = "uint32";
1961 } else if (type_name == "gint64") {
1962 type_name = "int64";
1963 } else if (type_name == "guint64") {
1964 type_name = "uint64";
1965 } else if (type_name == "gfloat") {
1966 type_name = "float";
1967 } else if (type_name == "gdouble") {
1968 type_name = "double";
1969 } else if (type_name == "filename") {
1970 type_name = "string";
1971 } else if (type_name == "GLib.offset") {
1972 type_name = "int64";
1973 } else if (type_name == "gsize") {
1974 type_name = "size_t";
1975 } else if (type_name == "gssize") {
1976 type_name = "ssize_t";
1977 } else if (type_name == "GType") {
1978 type_name = "GLib.Type";
1979 } else if (type_name == "GLib.String") {
1980 type_name = "GLib.StringBuilder";
1981 } else if (type_name == "GObject.Class") {
1982 type_name = "GLib.ObjectClass";
1983 } else if (type_name == "GLib.unichar") {
1984 type_name = "unichar";
1985 } else if (type_name == "GLib.Data") {
1986 type_name = "GLib.Datalist";
1987 } else if (type_name == "Atk.ImplementorIface") {
1988 type_name = "Atk.Implementor";
1989 } else {
1990 known_type = false;
1992 var sym = parse_symbol_from_string (type_name, get_current_src ());
1993 type = new UnresolvedType.from_symbol (sym, get_current_src ());
1994 if (!known_type) {
1995 unresolved_gir_symbols.add (sym);
1999 return type;
2002 Struct parse_record () {
2003 start_element ("record");
2004 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2005 st.external = true;
2006 st.access = SymbolAccessibility.PUBLIC;
2008 string cname = reader.get_attribute ("c:type");
2009 if (cname != null) {
2010 st.set_cname (cname);
2013 current_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
2014 if (current_gtype_struct_for != null) {
2015 girdata["glib:is-gtype-struct-for"] = current_gtype_struct_for;
2018 next ();
2019 var old_symbols_info = current_symbols_info;
2020 var old_symbol = current_symbol;
2021 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2022 current_symbol = st;
2023 while (current_token == MarkupTokenType.START_ELEMENT) {
2024 if (!push_metadata ()) {
2025 skip_element ();
2026 continue;
2029 if (reader.name == "field") {
2030 if (reader.get_attribute ("name") != "priv") {
2031 add_symbol_info (parse_field ());
2032 } else {
2033 skip_element ();
2035 } else if (reader.name == "constructor") {
2036 parse_constructor ();
2037 } else if (reader.name == "method") {
2038 add_symbol_info (parse_method ("method"));
2039 } else if (reader.name == "union") {
2040 Struct s = parse_union ();
2041 var s_fields = s.get_fields ();
2042 foreach (var f in s_fields) {
2043 f.set_cname (s.get_cname () + "." + f.get_cname ());
2044 f.name = s.name + "_" + f.name;
2045 st.add_field (f);
2047 } else {
2048 // error
2049 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
2050 skip_element ();
2053 pop_metadata ();
2055 end_element ("record");
2057 merge_add_process (st);
2058 current_symbols_info = old_symbols_info;
2059 current_symbol = old_symbol;
2060 current_gtype_struct_for = null;
2062 return st;
2065 Class parse_class () {
2066 start_element ("class");
2067 var name = element_get_name ();
2068 string cname = reader.get_attribute ("c:type");
2069 string parent = reader.get_attribute ("parent");
2070 var cl = current_symbol.scope.lookup (name) as Class;
2071 if (cl == null) {
2072 cl = new Class (name, get_current_src ());
2073 cl.access = SymbolAccessibility.PUBLIC;
2074 cl.external = true;
2076 if (cname != null) {
2077 cl.set_cname (cname);
2080 if (parent != null) {
2081 cl.add_base_type (parse_type_from_gir_name (parent));
2084 next ();
2085 var first_field = true;
2086 var old_symbols_info = current_symbols_info;
2087 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2088 while (current_token == MarkupTokenType.START_ELEMENT) {
2089 if (!push_metadata ()) {
2090 skip_element ();
2091 continue;
2094 if (reader.name == "implements") {
2095 start_element ("implements");
2096 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
2097 next ();
2098 end_element ("implements");
2099 } else if (reader.name == "constant") {
2100 add_symbol_info (parse_constant ());
2101 } else if (reader.name == "field") {
2102 if (first_field && parent != null) {
2103 // first field is guaranteed to be the parent instance
2104 skip_element ();
2105 } else {
2106 if (reader.get_attribute ("name") != "priv") {
2107 add_symbol_info (parse_field ());
2108 } else {
2109 skip_element ();
2112 first_field = false;
2113 } else if (reader.name == "property") {
2114 add_symbol_info (parse_property ());
2115 } else if (reader.name == "constructor") {
2116 add_symbol_info (parse_constructor (cname));
2117 } else if (reader.name == "function") {
2118 add_symbol_info (parse_method ("function"));
2119 } else if (reader.name == "method") {
2120 add_symbol_info (parse_method ("method"));
2121 } else if (reader.name == "virtual-method") {
2122 add_symbol_info (parse_method ("virtual-method"));
2123 } else if (reader.name == "union") {
2124 Struct s = parse_union ();
2125 var s_fields = s.get_fields ();
2126 foreach (var f in s_fields) {
2127 f.set_cname (s.get_cname () + "." + f.get_cname ());
2128 f.name = s.name + "_" + f.name;
2129 add_symbol_info (f);
2131 } else if (reader.name == "glib:signal") {
2132 add_symbol_info (parse_signal ());
2133 } else {
2134 // error
2135 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2136 skip_element ();
2139 pop_metadata ();
2142 merge_add_process (cl);
2143 current_symbols_info = old_symbols_info;
2145 end_element ("class");
2146 return cl;
2149 Interface parse_interface () {
2150 start_element ("interface");
2151 var iface = new Interface (element_get_name (), get_current_src ());
2152 iface.access = SymbolAccessibility.PUBLIC;
2153 iface.external = true;
2155 string cname = reader.get_attribute ("c:type");
2156 if (cname != null) {
2157 iface.set_cname (cname);
2160 next ();
2161 var old_symbol = current_symbol;
2162 var old_symbols_info = current_symbols_info;
2163 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2164 current_symbol = iface;
2165 while (current_token == MarkupTokenType.START_ELEMENT) {
2166 if (!push_metadata ()) {
2167 skip_element ();
2168 continue;
2171 if (reader.name == "prerequisite") {
2172 start_element ("prerequisite");
2173 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2174 next ();
2175 end_element ("prerequisite");
2176 } else if (reader.name == "field") {
2177 add_symbol_info (parse_field ());
2178 } else if (reader.name == "property") {
2179 add_symbol_info (parse_property ());
2180 } else if (reader.name == "virtual-method") {
2181 add_symbol_info (parse_method ("virtual-method"));
2182 } else if (reader.name == "function") {
2183 add_symbol_info (parse_method ("function"));
2184 } else if (reader.name == "method") {
2185 add_symbol_info (parse_method ("method"));
2186 } else if (reader.name == "glib:signal") {
2187 add_symbol_info (parse_signal ());
2188 } else {
2189 // error
2190 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2191 skip_element ();
2194 pop_metadata ();
2197 merge_add_process (iface);
2198 current_symbol = old_symbol;
2199 current_symbols_info = old_symbols_info;
2201 end_element ("interface");
2202 return iface;
2205 Field parse_field () {
2206 start_element ("field");
2207 string name = reader.get_attribute ("name");
2208 string allow_none = reader.get_attribute ("allow-none");
2209 next ();
2210 var type = parse_type ();
2211 type = element_get_type (type, true);
2212 if (type is DelegateType && current_gtype_struct_for != null) {
2213 // virtual
2214 var gtype_struct_for = parse_symbol_from_string (current_gtype_struct_for);
2215 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2216 if (callbacks == null) {
2217 callbacks = new ArrayList<Delegate> ();
2218 gtype_callbacks.set (gtype_struct_for, callbacks);
2220 callbacks.add (((DelegateType) type).delegate_symbol);
2222 var field = new Field (name, type, null, get_current_src ());
2223 field.access = SymbolAccessibility.PUBLIC;
2224 field.no_array_length = true;
2225 field.array_null_terminated = true;
2226 if (allow_none == "1") {
2227 type.nullable = true;
2229 end_element ("field");
2230 return field;
2233 Property parse_property () {
2234 start_element ("property");
2235 string name = reader.get_attribute ("name").replace ("-", "_");
2236 string readable = reader.get_attribute ("readable");
2237 string writable = reader.get_attribute ("writable");
2238 string construct_ = reader.get_attribute ("construct");
2239 string construct_only = reader.get_attribute ("construct-only");
2240 next ();
2241 bool no_array_length;
2242 bool array_null_terminated;
2243 var type = parse_type (null, null, false, out no_array_length, out array_null_terminated);
2244 var prop = new Property (name, type, null, null, get_current_src ());
2245 prop.access = SymbolAccessibility.PUBLIC;
2246 prop.external = true;
2247 prop.no_accessor_method = true;
2248 prop.no_array_length = no_array_length;
2249 prop.array_null_terminated = array_null_terminated;
2250 if (readable != "0") {
2251 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
2252 prop.get_accessor.value_type.value_owned = true;
2254 if (writable == "1" || construct_only == "1") {
2255 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
2257 end_element ("property");
2258 return prop;
2261 Delegate parse_callback () {
2262 return this.parse_function ("callback") as Delegate;
2265 Method parse_constructor (string? parent_ctype = null) {
2266 start_element ("constructor");
2268 string throws_string = reader.get_attribute ("throws");
2269 string cname = reader.get_attribute ("c:identifier");
2270 var m = new CreationMethod (null, element_get_name (), get_current_src ());
2271 m.access = SymbolAccessibility.PUBLIC;
2272 m.has_construct_function = false;
2274 if (m.name == "new") {
2275 m.name = null;
2276 } else if (m.name.has_prefix ("new_")) {
2277 m.name = m.name.substring ("new_".length);
2279 if (cname != null) {
2280 m.set_cname (cname);
2283 next ();
2284 string? ctype;
2285 parse_return_value (out ctype);
2286 if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
2287 m.custom_return_type_cname = ctype;
2290 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2291 start_element ("parameters");
2292 next ();
2293 while (current_token == MarkupTokenType.START_ELEMENT) {
2294 if (!push_metadata ()) {
2295 skip_element ();
2296 continue;
2299 m.add_parameter (parse_parameter ());
2301 pop_metadata ();
2303 end_element ("parameters");
2306 if (throws_string == "1") {
2307 m.add_error_type (new ErrorType (null, null));
2309 end_element ("constructor");
2310 return m;
2313 class ParameterInfo {
2314 public ParameterInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx) {
2315 this.param = param;
2316 this.array_length_idx = array_length_idx;
2317 this.closure_idx = closure_idx;
2318 this.destroy_idx = destroy_idx;
2319 this.vala_idx = 0.0F;
2320 this.keep = true;
2323 public Parameter param;
2324 public float vala_idx;
2325 public int array_length_idx;
2326 public int closure_idx;
2327 public int destroy_idx;
2328 public bool keep;
2331 Symbol parse_function (string element_name) {
2332 start_element (element_name);
2333 string name = element_get_name ();
2334 string cname = reader.get_attribute ("c:identifier");
2335 string throws_string = reader.get_attribute ("throws");
2336 string invoker = reader.get_attribute ("invoker");
2337 next ();
2338 DataType return_type;
2339 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2340 return_type = parse_return_value ();
2341 } else {
2342 return_type = new VoidType ();
2344 return_type = element_get_type (return_type, true);
2346 Symbol s;
2348 if (element_name == "callback") {
2349 s = new Delegate (name, return_type, get_current_src ());
2350 } else {
2351 s = new Method (name, return_type, get_current_src ());
2354 s.access = SymbolAccessibility.PUBLIC;
2355 if (cname != null) {
2356 if (s is Method) {
2357 ((Method) s).set_cname (cname);
2358 } else {
2359 ((Delegate) s).set_cname (cname);
2363 s.external = true;
2365 if (element_name == "virtual-method" || element_name == "callback") {
2366 if (s is Method) {
2367 ((Method) s).is_virtual = true;
2368 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
2369 s.attributes.append (new Attribute ("NoWrapper", s.source_reference));
2373 if (invoker != null) {
2374 s.name = invoker;
2376 } else if (element_name == "function") {
2377 ((Method) s).binding = MemberBinding.STATIC;
2380 if (s is Method) {
2381 var method = (Method) s;
2382 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
2383 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
2384 method.is_abstract = false;
2385 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
2386 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
2387 method.is_virtual = false;
2389 method.vfunc_name = metadata.get_string (ArgumentType.VFUNC_NAME);
2392 var parameters = new ArrayList<ParameterInfo> ();
2393 var array_length_parameters = new ArrayList<int> ();
2394 var closure_parameters = new ArrayList<int> ();
2395 var destroy_parameters = new ArrayList<int> ();
2396 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2397 start_element ("parameters");
2398 next ();
2400 while (current_token == MarkupTokenType.START_ELEMENT) {
2401 if (!push_metadata ()) {
2402 skip_element ();
2403 continue;
2406 int array_length_idx, closure_idx, destroy_idx;
2407 string scope;
2408 string default_param_name = null;
2409 default_param_name = "arg%d".printf (parameters.size);
2410 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, default_param_name);
2411 if (array_length_idx != -1) {
2412 array_length_parameters.add (array_length_idx);
2414 if (closure_idx != -1) {
2415 closure_parameters.add (closure_idx);
2417 if (destroy_idx != -1) {
2418 destroy_parameters.add (destroy_idx);
2421 var info = new ParameterInfo(param, array_length_idx, closure_idx, destroy_idx);
2423 if (s is Method && scope == "async") {
2424 var unresolved_type = param.variable_type as UnresolvedType;
2425 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
2426 // GAsync-style method
2427 ((Method) s).coroutine = true;
2428 info.keep = false;
2432 parameters.add (info);
2433 pop_metadata ();
2435 end_element ("parameters");
2437 var array_length_idx = -1;
2438 if (return_type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
2439 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
2440 parameters[array_length_idx].keep = false;
2441 array_length_parameters.add (array_length_idx);
2444 int i = 0, j=1;
2446 int last = -1;
2447 foreach (ParameterInfo info in parameters) {
2448 if (s is Delegate && info.closure_idx == i) {
2449 var d = (Delegate) s;
2450 d.has_target = true;
2451 d.cinstance_parameter_position = (float) j - 0.1;
2452 info.keep = false;
2453 } else if (info.keep
2454 && !array_length_parameters.contains (i)
2455 && !closure_parameters.contains (i)
2456 && !destroy_parameters.contains (i)) {
2457 info.vala_idx = (float) j;
2458 info.keep = true;
2460 /* interpolate for vala_idx between this and last*/
2461 float last_idx = 0.0F;
2462 if (last != -1) {
2463 last_idx = parameters[last].vala_idx;
2465 for (int k=last+1; k < i; k++) {
2466 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
2468 last = i;
2469 j++;
2470 } else {
2471 info.keep = false;
2472 // make sure that vala_idx is always set
2473 // the above if branch does not set vala_idx for
2474 // hidden parameters at the end of the parameter list
2475 info.vala_idx = (j - 1) + (i - last) * 0.1F;
2477 i++;
2480 foreach (ParameterInfo info in parameters) {
2481 if (info.keep) {
2483 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
2484 so do it first*/
2485 if (s is Method) {
2486 ((Method) s).add_parameter (info.param);
2487 } else {
2488 ((Delegate) s).add_parameter (info.param);
2491 if (info.array_length_idx != -1) {
2492 if ((info.array_length_idx) >= parameters.size) {
2493 Report.error (get_current_src (), "invalid array_length index");
2494 continue;
2496 set_array_ccode (info.param, parameters[info.array_length_idx]);
2497 } else if (info.param.variable_type is ArrayType) {
2498 info.param.no_array_length = true;
2499 info.param.array_null_terminated = true;
2502 if (info.closure_idx != -1) {
2503 if ((info.closure_idx) >= parameters.size) {
2504 Report.error (get_current_src (), "invalid closure index");
2505 continue;
2507 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
2509 if (info.destroy_idx != -1) {
2510 if (info.destroy_idx >= parameters.size) {
2511 Report.error (get_current_src (), "invalid destroy index");
2512 continue;
2514 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
2518 if (array_length_idx != -1) {
2519 if (array_length_idx >= parameters.size) {
2520 Report.error (get_current_src (), "invalid array_length index");
2521 } else {
2522 set_array_ccode (s, parameters[array_length_idx]);
2524 } else if (return_type is ArrayType) {
2525 if (s is Method) {
2526 var m = (Method) s;
2527 m.no_array_length = true;
2528 m.array_null_terminated = true;
2529 } else {
2530 var d = (Delegate) s;
2531 d.no_array_length = true;
2532 d.array_null_terminated = true;
2536 if (throws_string == "1") {
2537 s.add_error_type (new ErrorType (null, null));
2539 end_element (element_name);
2540 return s;
2543 Method parse_method (string element_name) {
2544 return this.parse_function (element_name) as Method;
2547 Signal parse_signal () {
2548 start_element ("glib:signal");
2549 string name = reader.get_attribute ("name").replace ("-", "_");
2550 next ();
2551 DataType return_type;
2552 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2553 return_type = parse_return_value ();
2554 } else {
2555 return_type = new VoidType ();
2557 var sig = new Signal (name, return_type, get_current_src ());
2558 sig.access = SymbolAccessibility.PUBLIC;
2559 sig.external = true;
2560 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2561 start_element ("parameters");
2562 next ();
2563 while (current_token == MarkupTokenType.START_ELEMENT) {
2564 if (!push_metadata ()) {
2565 skip_element ();
2566 continue;
2569 sig.add_parameter (parse_parameter ());
2571 pop_metadata ();
2573 end_element ("parameters");
2575 end_element ("glib:signal");
2576 return sig;
2579 Class parse_boxed (string element_name) {
2580 start_element (element_name);
2581 string name = reader.get_attribute ("name");
2582 if (name == null) {
2583 name = reader.get_attribute ("glib:name");
2585 var cl = new Class (name, get_current_src ());
2586 cl.access = SymbolAccessibility.PUBLIC;
2587 cl.external = true;
2588 cl.is_compact = true;
2590 string cname = reader.get_attribute ("c:type");
2591 if (cname != null) {
2592 cl.set_cname (cname);
2595 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
2596 cl.set_free_function ("g_boxed_free");
2597 cl.set_dup_function ("g_boxed_copy");
2599 next ();
2600 var old_symbols_info = current_symbols_info;
2601 var old_symbol = current_symbol;
2602 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2603 current_symbol = cl;
2604 while (current_token == MarkupTokenType.START_ELEMENT) {
2605 if (!push_metadata ()) {
2606 skip_element ();
2607 continue;
2610 if (reader.name == "field") {
2611 add_symbol_info (parse_field ());
2612 } else if (reader.name == "constructor") {
2613 parse_constructor ();
2614 } else if (reader.name == "method") {
2615 add_symbol_info (parse_method ("method"));
2616 } else {
2617 // error
2618 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2619 skip_element ();
2622 pop_metadata ();
2624 end_element (element_name);
2626 merge_add_process (cl);
2627 current_symbols_info = old_symbols_info;
2628 current_symbol = old_symbol;
2630 return cl;
2633 Struct parse_union () {
2634 start_element ("union");
2635 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2636 st.access = SymbolAccessibility.PUBLIC;
2637 st.external = true;
2638 next ();
2640 var old_symbol = current_symbol;
2641 current_symbol = st;
2642 while (current_token == MarkupTokenType.START_ELEMENT) {
2643 if (!push_metadata ()) {
2644 skip_element ();
2645 continue;
2648 if (reader.name == "field") {
2649 st.add_field (parse_field ());
2650 } else if (reader.name == "constructor") {
2651 parse_constructor ();
2652 } else if (reader.name == "method") {
2653 st.add_method (parse_method ("method"));
2654 } else if (reader.name == "record") {
2655 Struct s = parse_record ();
2656 var fs = s.get_fields ();
2657 foreach (var f in fs) {
2658 f.set_cname (s.get_cname () + "." + f.get_cname ());
2659 f.name = s.name + "_" + f.name;
2660 st.add_field (f);
2662 } else {
2663 // error
2664 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
2665 skip_element ();
2668 pop_metadata ();
2671 end_element ("union");
2672 current_symbol = old_symbol;
2674 return st;
2677 Constant parse_constant () {
2678 start_element ("constant");
2679 string name = element_get_name ();
2680 next ();
2681 var type = parse_type ();
2682 var c = new Constant (name, type, null, get_current_src ());
2683 c.access = SymbolAccessibility.PUBLIC;
2684 c.external = true;
2685 end_element ("constant");
2686 return c;
2689 /* Reporting */
2690 void report_unused_metadata (Metadata metadata) {
2691 if (metadata == Metadata.empty) {
2692 return;
2695 if (metadata.args.size == 0 && metadata.children.size == 0) {
2696 Report.warning (metadata.source_reference, "empty metadata");
2697 return;
2700 foreach (var arg_type in metadata.args.get_keys ()) {
2701 var arg = metadata.args[arg_type];
2702 if (!arg.used) {
2703 // if metadata is used and argument is not, then it's a unexpected argument
2704 Report.warning (arg.source_reference, "argument never used");
2708 foreach (var child in metadata.children) {
2709 if (!child.used) {
2710 Report.warning (child.source_reference, "metadata never used");
2711 } else {
2712 report_unused_metadata (child);
2717 /* Post-parsing */
2719 void resolve_gir_symbols () {
2720 // we are remapping unresolved symbols, so create them from concrete symbols
2721 foreach (var map_from in concrete_symbols_map.get_keys ()) {
2722 unresolved_symbols_map[get_unresolved_symbol(map_from)] = concrete_symbols_map[map_from];
2725 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
2726 foreach (var map_from in unresolved_gir_symbols) {
2727 while (map_from != null) {
2728 var map_to = unresolved_symbols_map[map_from];
2729 if (map_to != null) {
2730 // remap the original symbol to match the target
2731 map_from.inner = null;
2732 map_from.name = map_to.name;
2733 if (map_to is UnresolvedSymbol) {
2734 var umap_to = (UnresolvedSymbol) map_to;
2735 while (umap_to.inner != null) {
2736 umap_to = umap_to.inner;
2737 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
2738 map_from = map_from.inner;
2740 } else {
2741 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
2742 map_to = map_to.parent_symbol;
2743 map_from.inner = new UnresolvedSymbol (null, map_to.name);
2744 map_from = map_from.inner;
2747 break;
2749 map_from = map_from.inner;
2754 Symbol? resolve_symbol (Scope parent_scope, UnresolvedSymbol unresolved_symbol) {
2755 // simple symbol resolver, enough for gir
2756 if (unresolved_symbol.inner == null) {
2757 var scope = parent_scope;
2758 while (scope != null) {
2759 var sym = scope.lookup (unresolved_symbol.name);
2760 if (sym != null) {
2761 return sym;
2763 scope = scope.parent_scope;
2765 } else {
2766 var inner = resolve_symbol (parent_scope, unresolved_symbol.inner);
2767 if (inner != null) {
2768 return inner.scope.lookup (unresolved_symbol.name);
2771 return null;
2774 void postprocess_interfaces () {
2775 foreach (var iface in interfaces) {
2776 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
2777 ensure we have at least one instantiable prerequisite */
2778 bool has_instantiable_prereq = false;
2779 foreach (DataType prereq in iface.get_prerequisites ()) {
2780 Symbol sym = null;
2781 if (prereq is UnresolvedType) {
2782 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
2783 sym = resolve_symbol (iface.parent_symbol.scope, unresolved_symbol);
2784 } else {
2785 sym = prereq.data_type;
2787 if (sym is Class) {
2788 has_instantiable_prereq = true;
2789 break;
2793 if (!has_instantiable_prereq) {
2794 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
2799 void postprocess_reparenting () {
2800 foreach (UnresolvedSymbol target_unresolved_symbol in symbol_reparent_map.get_keys ()) {
2801 var target_symbol = resolve_symbol (context.root.scope, target_unresolved_symbol);
2802 if (target_symbol == null) {
2803 // create namespaces backward
2804 var sym = target_unresolved_symbol;
2805 var ns = new Namespace (sym.name, sym.source_reference);
2806 var result = ns;
2807 sym = sym.inner;
2808 while (sym != null) {
2809 var res = resolve_symbol (context.root.scope, sym);
2810 if (res != null && !(res is Namespace)) {
2811 result = null;
2812 break;
2814 var parent = res as Namespace;
2815 if (res == null) {
2816 parent = new Namespace (sym.name, sym.source_reference);
2818 if (parent.scope.lookup (ns.name) == null) {
2819 parent.add_namespace (ns);
2821 ns = parent;
2822 sym = sym.inner;
2824 if (result != null && sym == null && context.root.scope.lookup (ns.name) == null) {
2825 // a new root namespace, helpful for a possible non-gobject gir?
2826 context.root.add_namespace (ns);
2828 target_symbol = result;
2830 if (target_symbol == null) {
2831 Report.error (null, "unable to reparent into `%s'".printf (target_unresolved_symbol.to_string ()));
2832 continue;
2834 var symbols = symbol_reparent_map[target_unresolved_symbol];
2835 foreach (var symbol in symbols) {
2836 add_symbol_to_container (target_symbol, symbol);
2841 void postprocess_gtype_callbacks (Namespace ns) {
2842 foreach (UnresolvedSymbol gtype_struct_for in gtype_callbacks.get_keys ()) {
2843 // parent symbol is the record, therefore use parent of parent symbol
2844 var gtype = resolve_symbol (ns.scope, gtype_struct_for) as ObjectTypeSymbol;
2845 if (gtype == null) {
2846 Report.error (null, "unknown symbol `%s' while postprocessing callbacks".printf (gtype_struct_for.name));
2847 continue;
2849 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2850 foreach (Delegate d in callbacks) {
2851 var symbol = gtype.scope.lookup (d.name);
2852 if (symbol == null) {
2853 continue;
2854 } else if (symbol is Method) {
2855 var meth = (Method) symbol;
2856 if (gtype is Class) {
2857 meth.is_virtual = true;
2858 } else if (gtype is Interface) {
2859 meth.is_abstract = true;
2861 } else if (symbol is Signal) {
2862 var sig = (Signal) symbol;
2863 sig.is_virtual = true;
2864 assume_parameter_names (sig, d);
2865 } else if (symbol is Property) {
2866 var prop = (Property) symbol;
2867 prop.is_virtual = true;
2868 } else {
2869 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (d.name, gtype.name));
2875 void postprocess_aliases () {
2876 /* this is unfortunate because <alias> tag has no type information, thus we have
2877 to guess it from the base type */
2878 foreach (var alias in aliases) {
2879 DataType base_type = null;
2880 Symbol type_sym = null;
2881 bool simple_type = false;
2882 if (alias.base_type is UnresolvedType) {
2883 base_type = alias.base_type;
2884 type_sym = resolve_symbol (alias.parent_symbol.scope, ((UnresolvedType) base_type).unresolved_symbol);
2885 } else if (alias.base_type is PointerType && ((PointerType) alias.base_type).base_type is VoidType) {
2886 // gpointer, if it's a struct make it a simpletype
2887 simple_type = true;
2888 } else {
2889 base_type = alias.base_type;
2890 type_sym = base_type.data_type;
2893 if (type_sym is Struct && ((Struct) type_sym).is_simple_type ()) {
2894 simple_type = true;
2897 if (base_type == null || type_sym == null || type_sym is Struct) {
2898 var st = new Struct (alias.name, alias.source_reference);
2899 st.access = SymbolAccessibility.PUBLIC;
2900 if (base_type != null) {
2901 // threat target="none" as a new struct
2902 st.base_type = base_type;
2904 st.external = true;
2905 if (alias.cname != null) {
2906 st.set_cname (alias.cname);
2908 if (simple_type) {
2909 st.set_simple_type (true);
2911 add_symbol_to_container (alias.parent_symbol, st);
2912 } else if (type_sym is Class) {
2913 var cl = new Class (alias.name, alias.source_reference);
2914 cl.access = SymbolAccessibility.PUBLIC;
2915 if (base_type != null) {
2916 cl.add_base_type (base_type);
2918 cl.external = true;
2919 if (alias.cname != null) {
2920 cl.set_cname (alias.cname);
2922 add_symbol_to_container (alias.parent_symbol, cl);
2927 void find_static_method_parent (string cname, Symbol current, ref Symbol best, ref double match, double match_char) {
2928 var old_best = best;
2929 if (current.scope.get_symbol_table () != null) {
2930 foreach (var child in current.scope.get_symbol_table().get_values ()) {
2931 if (child is Struct || child is ObjectTypeSymbol || child is Namespace) {
2932 find_static_method_parent (cname, child, ref best, ref match, match_char);
2936 if (best != old_best) {
2937 // child is better
2938 return;
2941 var current_cprefix = current.get_lower_case_cprefix ();
2942 if (cname.has_prefix (current_cprefix)) {
2943 var current_match = match_char * current_cprefix.length;
2944 if (current_match > match) {
2945 match = current_match;
2946 best = current;
2951 void postprocess_namespace_methods () {
2952 /* transform static methods into instance methods if possible.
2953 In most of cases this is a .gir fault we are going to fix */
2954 foreach (var ns in namespace_methods.get_keys ()) {
2955 var ns_cprefix = ns.get_lower_case_cprefix ();
2956 var methods = namespace_methods[ns];
2957 foreach (var method in methods) {
2958 if (method.parent_symbol != null) {
2959 // fixed earlier by metadata
2960 continue;
2963 var cname = method.get_cname ();
2965 Parameter first_param = null;
2966 if (method.get_parameters ().size > 0) {
2967 first_param = method.get_parameters()[0];
2969 if (first_param != null && first_param.variable_type is UnresolvedType) {
2970 // check if it's a missed instance method (often happens for structs)
2971 var parent = resolve_symbol (ns.scope, ((UnresolvedType) first_param.variable_type).unresolved_symbol);
2972 if (parent != null && (parent is Struct || parent is ObjectTypeSymbol || parent is Namespace)
2973 && cname.has_prefix (parent.get_lower_case_cprefix ())) {
2974 // instance method
2975 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2976 if (parent.scope.lookup (new_name) == null) {
2977 method.name = new_name;
2978 method.get_parameters().remove_at (0);
2979 method.binding = MemberBinding.INSTANCE;
2980 add_symbol_to_container (parent, method);
2981 } else {
2982 ns.add_method (method);
2984 continue;
2988 double match = 0;
2989 Symbol parent = ns;
2990 find_static_method_parent (cname, ns, ref parent, ref match, 1.0/cname.length);
2991 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2992 if (parent.scope.lookup (new_name) == null) {
2993 method.name = new_name;
2994 add_symbol_to_container (parent, method);
2995 } else {
2996 ns.add_method (method);
3002 /* Hash and equal functions */
3004 static uint unresolved_symbol_hash (void *ptr) {
3005 var sym = (UnresolvedSymbol) ptr;
3006 var builder = new StringBuilder ();
3007 while (sym != null) {
3008 builder.append (sym.name);
3009 sym = sym.inner;
3011 return builder.str.hash ();
3014 static bool unresolved_symbol_equal (void *ptr1, void *ptr2) {
3015 var sym1 = (UnresolvedSymbol) ptr1;
3016 var sym2 = (UnresolvedSymbol) ptr2;
3017 while (sym1 != sym2) {
3018 if (sym1 == null || sym2 == null) {
3019 return false;
3021 if (sym1.name != sym2.name) {
3022 return false;
3024 sym1 = sym1.inner;
3025 sym2 = sym2.inner;
3027 return true;