girparser: Provide default constructor for classes.
[vala-lang.git] / vala / valagirparser.vala
blobd56d01715853a357f72538799e633011cbcca44f
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,
73 SCOPE;
75 public static ArgumentType? from_string (string name) {
76 var enum_class = (EnumClass) typeof(ArgumentType).class_ref ();
77 var nick = name.replace ("_", "-");
78 unowned GLib.EnumValue? enum_value = enum_class.get_value_by_nick (nick);
79 if (enum_value != null) {
80 ArgumentType value = (ArgumentType) enum_value.value;
81 return value;
83 return null;
87 class Argument {
88 public Expression expression;
89 public SourceReference source_reference;
91 public bool used = false;
93 public Argument (Expression expression, SourceReference? source_reference = null) {
94 this.expression = expression;
95 this.source_reference = source_reference;
99 class MetadataSet : Metadata {
100 public MetadataSet (MetadataType type) {
101 base ("", type);
104 public void add_sibling (Metadata metadata) {
105 foreach (var child in metadata.children) {
106 add_child (child);
108 // merge arguments and take precedence
109 foreach (var key in metadata.args.get_keys ()) {
110 args[key] = metadata.args[key];
115 class Metadata {
116 private static Metadata _empty = null;
117 public static Metadata empty {
118 get {
119 if (_empty == null) {
120 _empty = new Metadata ("");
122 return _empty;
126 public string pattern;
127 public PatternSpec pattern_spec;
128 public MetadataType type;
129 public SourceReference source_reference;
131 public bool used = false;
132 public Vala.Map<ArgumentType,Argument> args = new HashMap<ArgumentType,Argument> ();
133 public ArrayList<Metadata> children = new ArrayList<Metadata> ();
135 public Metadata (string pattern, MetadataType type = MetadataType.GENERIC, SourceReference? source_reference = null) {
136 this.pattern = pattern;
137 this.pattern_spec = new PatternSpec (pattern);
138 this.type = type;
139 this.source_reference = source_reference;
142 public void add_child (Metadata metadata) {
143 children.add (metadata);
146 public Metadata? get_child (string pattern, MetadataType type = MetadataType.GENERIC) {
147 foreach (var metadata in children) {
148 if (metadata.type == type && metadata.pattern == pattern) {
149 return metadata;
152 return null;
155 public Metadata match_child (string name, MetadataType type = MetadataType.GENERIC) {
156 var result = Metadata.empty;
157 foreach (var metadata in children) {
158 if (metadata.type == type && metadata.pattern_spec.match_string (name)) {
159 metadata.used = true;
160 if (result == Metadata.empty) {
161 // first match
162 result = metadata;
163 } else {
164 var ms = result as MetadataSet;
165 if (ms == null) {
166 // second match
167 ms = new MetadataSet (type);
168 ms.add_sibling (result);
170 ms.add_sibling (metadata);
171 result = ms;
175 return result;
178 public void add_argument (ArgumentType key, Argument value) {
179 args.set (key, value);
182 public bool has_argument (ArgumentType key) {
183 return args.contains (key);
186 public Expression? get_expression (ArgumentType arg) {
187 var val = args.get (arg);
188 if (val != null) {
189 val.used = true;
190 return val.expression;
192 return null;
195 public string? get_string (ArgumentType arg) {
196 var lit = get_expression (arg) as StringLiteral;
197 if (lit != null) {
198 return lit.eval ();
200 return null;
203 public int get_integer (ArgumentType arg) {
204 var unary = get_expression (arg) as UnaryExpression;
205 if (unary != null && unary.operator == UnaryOperator.MINUS) {
206 var lit = unary.inner as IntegerLiteral;
207 if (lit != null) {
208 return -int.parse (lit.value);
210 } else {
211 var lit = get_expression (arg) as IntegerLiteral;
212 if (lit != null) {
213 return int.parse (lit.value);
217 return 0;
220 public bool get_bool (ArgumentType arg) {
221 var lit = get_expression (arg) as BooleanLiteral;
222 if (lit != null) {
223 return lit.value;
225 return false;
228 public SourceReference? get_source_reference (ArgumentType arg) {
229 var val = args.get (arg);
230 if (val != null) {
231 return val.source_reference;
233 return null;
237 class MetadataParser {
239 * Grammar:
240 * metadata ::= [ rule [ '\n' relativerule ]* ]
241 * rule ::= pattern ' ' [ args ]
242 * relativerule ::= [ access ] rule
243 * pattern ::= identifier [ access identifier ]*
244 * access ::= '.' | ':' | '::'
246 private Metadata tree = new Metadata ("");
247 private Scanner scanner;
248 private SourceLocation begin;
249 private SourceLocation end;
250 private SourceLocation old_end;
251 private TokenType current;
252 private Metadata parent_metadata;
254 public MetadataParser () {
255 tree.used = true;
258 SourceReference get_current_src () {
259 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
262 SourceReference get_src (SourceLocation begin) {
263 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
266 public Metadata parse_metadata (SourceFile metadata_file) {
267 scanner = new Scanner (metadata_file);
268 next ();
269 while (current != TokenType.EOF) {
270 if (!parse_rule ()) {
271 return Metadata.empty;
274 return tree;
277 TokenType next () {
278 old_end = end;
279 current = scanner.read_token (out begin, out end);
280 return current;
283 bool has_space () {
284 return old_end.pos != begin.pos;
287 bool has_newline () {
288 return old_end.line != begin.line;
291 string get_string () {
292 return ((string) begin.pos).substring (0, (int) (end.pos - begin.pos));
295 MetadataType? parse_metadata_access () {
296 switch (current) {
297 case TokenType.DOT:
298 next ();
299 return MetadataType.GENERIC;
300 case TokenType.COLON:
301 next ();
302 return MetadataType.PROPERTY;
303 case TokenType.DOUBLE_COLON:
304 next ();
305 return MetadataType.SIGNAL;
306 default:
307 return null;
311 string? parse_identifier (out SourceReference source_reference, bool is_glob) {
312 var begin = this.begin;
313 var builder = new StringBuilder ();
314 do {
315 if (is_glob && current == TokenType.STAR) {
316 builder.append_c ('*');
317 } else {
318 string str = null;
319 switch (current) {
320 case TokenType.IDENTIFIER:
321 case TokenType.UNOWNED:
322 case TokenType.OWNED:
323 case TokenType.GET:
324 case TokenType.NEW:
325 case TokenType.DEFAULT:
326 case TokenType.OUT:
327 case TokenType.REF:
328 case TokenType.VIRTUAL:
329 case TokenType.ABSTRACT:
330 str = get_string ();
331 break;
333 if (str == null) {
334 break;
336 builder.append (str);
338 source_reference = get_src (begin);
339 next ();
340 } while (!has_space ());
342 if (builder.str == "") {
343 if (is_glob) {
344 Report.error (get_src (begin), "expected pattern");
345 } else {
346 Report.error (get_src (begin), "expected identifier");
348 return null;
350 return builder.str;
353 Metadata? parse_pattern () {
354 Metadata metadata;
355 bool is_relative = false;
356 MetadataType? type = MetadataType.GENERIC;
357 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
358 // absolute pattern
359 parent_metadata = tree;
360 } else {
361 // relative pattern
362 type = parse_metadata_access ();
363 is_relative = true;
366 if (type == null) {
367 Report.error (get_current_src (), "expected pattern, `.', `:' or `::'");
368 return null;
371 if (parent_metadata == null) {
372 Report.error (get_current_src (), "cannot determinate parent metadata");
373 return null;
376 SourceReference src;
377 var pattern = parse_identifier (out src, true);
378 if (pattern == null) {
379 return null;
381 metadata = parent_metadata.get_child (pattern, type);
382 if (metadata == null) {
383 metadata = new Metadata (pattern, type, src);
384 parent_metadata.add_child (metadata);
387 while (current != TokenType.EOF && !has_space ()) {
388 type = parse_metadata_access ();
389 if (type == null) {
390 Report.error (get_current_src (), "expected `.', `:' or `::'");
391 return null;
394 pattern = parse_identifier (out src, true);
395 if (pattern == null) {
396 return null;
398 var child = metadata.get_child (pattern, type);
399 if (child == null) {
400 child = new Metadata (pattern, type, src);
401 metadata.add_child (child);
403 metadata = child;
405 if (!is_relative) {
406 parent_metadata = metadata;
409 return metadata;
412 Expression? parse_expression () {
413 var begin = this.begin;
414 var src = get_current_src ();
415 Expression expr = null;
416 switch (current) {
417 case TokenType.NULL:
418 expr = new NullLiteral (src);
419 break;
420 case TokenType.TRUE:
421 expr = new BooleanLiteral (true, src);
422 break;
423 case TokenType.FALSE:
424 expr = new BooleanLiteral (false, src);
425 break;
426 case TokenType.MINUS:
427 next ();
428 var inner = parse_expression ();
429 if (inner == null) {
430 Report.error (src, "expected expression after `-', got `%s'".printf (current.to_string ()));
431 } else {
432 expr = new UnaryExpression (UnaryOperator.MINUS, inner, get_src (begin));
434 return expr;
435 case TokenType.INTEGER_LITERAL:
436 expr = new IntegerLiteral (get_string (), src);
437 break;
438 case TokenType.REAL_LITERAL:
439 expr = new RealLiteral (get_string (), src);
440 break;
441 case TokenType.STRING_LITERAL:
442 expr = new StringLiteral (get_string (), src);
443 break;
444 case TokenType.IDENTIFIER:
445 expr = new MemberAccess (null, get_string (), src);
446 while (next () == TokenType.DOT) {
447 if (next () != TokenType.IDENTIFIER) {
448 Report.error (get_current_src (), "expected identifier got `%s'".printf (current.to_string ()));
449 break;
451 expr = new MemberAccess (expr, get_string (), get_current_src ());
453 return expr;
454 default:
455 Report.error (src, "expected literal or symbol got `%s'".printf (current.to_string ()));
456 break;
458 next ();
459 return expr;
462 bool parse_args (Metadata metadata) {
463 while (current != TokenType.EOF && has_space () && !has_newline ()) {
464 SourceReference src;
465 var id = parse_identifier (out src, false);
466 if (id == null) {
467 return false;
469 var arg_type = ArgumentType.from_string (id);
470 if (arg_type == null) {
471 Report.error (src, "unknown argument");
472 return false;
475 if (current != TokenType.ASSIGN) {
476 // threat as `true'
477 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, src), src));
478 continue;
480 next ();
482 Expression expr = parse_expression ();
483 if (expr == null) {
484 return false;
486 metadata.add_argument (arg_type, new Argument (expr, src));
489 return true;
492 bool parse_rule () {
493 var old_end = end;
494 var metadata = parse_pattern ();
495 if (metadata == null) {
496 return false;
499 if (current == TokenType.EOF || old_end.line != end.line) {
500 // eof or new rule
501 return true;
503 return parse_args (metadata);
507 class SymbolInfo {
508 public Symbol symbol;
509 public Metadata metadata;
510 // additional information from GIR
511 public HashMap<string,string> girdata;
514 class Alias {
515 public string name;
516 public string cname;
517 public DataType base_type;
518 public Symbol parent_symbol;
519 public SourceReference source_reference;
522 static GLib.Regex type_from_string_regex;
524 MarkupReader reader;
526 CodeContext context;
527 Namespace glib_ns;
529 SourceFile current_source_file;
530 Symbol current_symbol;
532 string current_gtype_struct_for;
533 SourceLocation begin;
534 SourceLocation end;
535 MarkupTokenType current_token;
537 string[] cheader_filenames;
539 ArrayList<Metadata> metadata_stack;
540 Metadata metadata;
541 ArrayList<HashMap<string,string>> girdata_stack;
542 HashMap<string,string> girdata;
544 ArrayList<SymbolInfo> current_symbols_info;
546 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
547 HashMap<Symbol,Symbol> concrete_symbols_map = new HashMap<Symbol,Symbol> ();
549 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
550 HashMap<UnresolvedSymbol,ArrayList<Symbol>> symbol_reparent_map = new HashMap<UnresolvedSymbol,ArrayList<Symbol>> (unresolved_symbol_hash, unresolved_symbol_equal);
551 HashMap<Namespace,ArrayList<Method>> namespace_methods = new HashMap<Namespace,ArrayList<Method>> ();
552 ArrayList<Alias> aliases = new ArrayList<Alias> ();
553 ArrayList<Interface> interfaces = new ArrayList<Interface> ();
555 HashMap<UnresolvedSymbol,ArrayList<Delegate>> gtype_callbacks;
558 * Parses all .gir source files in the specified code
559 * context and builds a code tree.
561 * @param context a code context
563 public void parse (CodeContext context) {
564 this.context = context;
565 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
566 context.accept (this);
568 resolve_gir_symbols ();
570 postprocess_interfaces ();
571 postprocess_reparenting ();
572 postprocess_aliases ();
573 postprocess_namespace_methods ();
576 public override void visit_source_file (SourceFile source_file) {
577 // collect gir namespaces
578 foreach (var node in source_file.get_nodes ()) {
579 if (node is Namespace) {
580 var ns = (Namespace) node;
581 var gir_namespace = source_file.gir_namespace;
582 if (gir_namespace == null) {
583 var a = ns.get_attribute ("CCode");
584 if (a != null && a.has_argument ("gir_namespace")) {
585 gir_namespace = a.get_string ("gir_namespace");
588 if (gir_namespace != null && gir_namespace != ns.name) {
589 var map_from = new UnresolvedSymbol (null, gir_namespace);
590 set_symbol_mapping (map_from, ns);
591 break;
596 if (source_file.filename.has_suffix (".gir")) {
597 parse_file (source_file);
601 public void parse_file (SourceFile source_file) {
602 metadata_stack = new ArrayList<Metadata> ();
603 metadata = Metadata.empty;
604 girdata_stack = new ArrayList<HashMap<string,string>> ();
606 // load metadata, first look into metadata directories then in the same directory of the .gir.
607 string? metadata_filename = context.get_metadata_path (source_file.filename);
608 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
609 var metadata_parser = new MetadataParser ();
610 var metadata_file = new SourceFile (context, source_file.file_type, metadata_filename);
611 context.add_source_file (metadata_file);
612 metadata = metadata_parser.parse_metadata (metadata_file);
615 this.current_source_file = source_file;
616 reader = new MarkupReader (source_file.filename);
618 // xml prolog
619 next ();
620 next ();
622 next ();
623 parse_repository ();
625 reader = null;
626 this.current_source_file = null;
629 void next () {
630 current_token = reader.read_token (out begin, out end);
632 // Skip *all* <doc> tags
633 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
634 skip_element();
637 void start_element (string name) {
638 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
639 // error
640 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
644 void end_element (string name) {
645 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
646 // error
647 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
649 next ();
652 SourceReference get_current_src () {
653 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
656 const string GIR_VERSION = "1.2";
658 void add_symbol_to_container (Symbol container, Symbol sym) {
659 var name = sym.name;
660 if (name == null && sym is CreationMethod) {
661 name = ".new";
663 if (container.scope.lookup (name) != null) {
664 // overridden by -custom.vala
665 return;
668 if (container is Class) {
669 unowned Class cl = (Class) container;
671 if (sym is Class) {
672 cl.add_class ((Class) sym);
673 } else if (sym is Constant) {
674 cl.add_constant ((Constant) sym);
675 } else if (sym is Enum) {
676 cl.add_enum ((Enum) sym);
677 } else if (sym is Field) {
678 cl.add_field ((Field) sym);
679 } else if (sym is Method) {
680 cl.add_method ((Method) sym);
681 } else if (sym is Property) {
682 cl.add_property ((Property) sym);
683 } else if (sym is Signal) {
684 cl.add_signal ((Signal) sym);
685 } else if (sym is Struct) {
686 cl.add_struct ((Struct) sym);
688 } else if (container is Enum) {
689 unowned Enum en = (Enum) container;
691 if (sym is EnumValue) {
692 en.add_value ((EnumValue) sym);
693 } else if (sym is Constant) {
694 en.add_constant ((Constant) sym);
695 } else if (sym is Method) {
696 en.add_method ((Method) sym);
698 } else if (container is Interface) {
699 unowned Interface iface = (Interface) container;
701 if (sym is Class) {
702 iface.add_class ((Class) sym);
703 } else if (sym is Constant) {
704 iface.add_constant ((Constant) sym);
705 } else if (sym is Enum) {
706 iface.add_enum ((Enum) sym);
707 } else if (sym is Field) {
708 iface.add_field ((Field) sym);
709 } else if (sym is Method) {
710 iface.add_method ((Method) sym);
711 } else if (sym is Property) {
712 iface.add_property ((Property) sym);
713 } else if (sym is Signal) {
714 iface.add_signal ((Signal) sym);
715 } else if (sym is Struct) {
716 iface.add_struct ((Struct) sym);
718 } else if (container is Namespace) {
719 unowned Namespace ns = (Namespace) container;
721 if (sym is Namespace) {
722 ns.add_namespace ((Namespace) sym);
723 } else if (sym is Class) {
724 ns.add_class ((Class) sym);
725 } else if (sym is Constant) {
726 ns.add_constant ((Constant) sym);
727 } else if (sym is Delegate) {
728 ns.add_delegate ((Delegate) sym);
729 } else if (sym is Enum) {
730 ns.add_enum ((Enum) sym);
731 } else if (sym is ErrorDomain) {
732 ns.add_error_domain ((ErrorDomain) sym);
733 } else if (sym is Field) {
734 ns.add_field ((Field) sym);
735 } else if (sym is Interface) {
736 ns.add_interface ((Interface) sym);
737 } else if (sym is Method) {
738 ns.add_method ((Method) sym);
739 } else if (sym is Namespace) {
740 ns.add_namespace ((Namespace) sym);
741 } else if (sym is Struct) {
742 ns.add_struct ((Struct) sym);
744 } else if (container is Struct) {
745 unowned Struct st = (Struct) container;
747 if (sym is Constant) {
748 st.add_constant ((Constant) sym);
749 } else if (sym is Field) {
750 st.add_field ((Field) sym);
751 } else if (sym is Method) {
752 st.add_method ((Method) sym);
753 } else if (sym is Property) {
754 st.add_property ((Property) sym);
756 } else {
757 Report.error (sym.source_reference, "impossible to add to container `%s'".printf (container.name));
761 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
762 UnresolvedSymbol? sym = null;
763 foreach (unowned string s in symbol_string.split (".")) {
764 sym = new UnresolvedSymbol (sym, s, source_reference);
766 if (sym == null) {
767 Report.error (source_reference, "a symbol must be specified");
769 return sym;
772 UnresolvedSymbol get_unresolved_symbol (Symbol symbol) {
773 if (symbol is UnresolvedSymbol) {
774 return (UnresolvedSymbol) symbol;
776 var sym = new UnresolvedSymbol (null, symbol.name);
777 var result = sym;
778 var cur = symbol.parent_node as Symbol;
779 while (cur != null && cur.name != null) {
780 sym = new UnresolvedSymbol (sym, cur.name);
781 cur = cur.parent_node as Symbol;
783 return result;
786 void set_symbol_mapping (Symbol map_from, Symbol map_to) {
787 // last mapping is the most up-to-date
788 if (map_from is UnresolvedSymbol) {
789 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
790 } else {
791 concrete_symbols_map[map_from] = map_to;
795 void assume_parameter_names (Signal sig, Symbol sym, bool skip_first) {
796 Iterator<Parameter> iter;
797 if (sym is Method) {
798 iter = ((Method) sym).get_parameters ().iterator ();
799 } else {
800 iter = ((Delegate) sym).get_parameters ().iterator ();
802 bool first = true;
803 foreach (var param in sig.get_parameters ()) {
804 if (!iter.next ()) {
805 // unreachable for valid GIR
806 break;
808 if (skip_first && first) {
809 if (!iter.next ()) {
810 // unreachable for valid GIR
811 break;
813 first = false;
815 param.name = iter.get ().name;
819 SymbolInfo? add_symbol_info (Symbol symbol) {
820 var name = symbol.name;
821 if (symbol is CreationMethod && name == null) {
822 name = ".new";
825 var info = new SymbolInfo ();
826 info.symbol = symbol;
827 info.metadata = metadata;
828 info.girdata = girdata;
829 current_symbols_info.add (info);
830 return info;
833 ArrayList<SymbolInfo> get_colliding_symbols_info (SymbolInfo info) {
834 var name = info.symbol.name;
835 if (name == null && info.symbol is CreationMethod) {
836 name = ".new";
838 var result = new ArrayList<SymbolInfo> ();
839 foreach (var cinfo in current_symbols_info) {
840 if (cinfo.symbol.name == name) {
841 result.add (cinfo);
844 return result;
847 SymbolInfo? get_current_first_symbol_info (string name) {
848 foreach (var info in current_symbols_info) {
849 if (info.symbol.name == name) {
850 return info;
853 return null;
856 Symbol? get_current_first_symbol (string name) {
857 var info = get_current_first_symbol_info (name);
858 if (info != null) {
859 return info.symbol;
861 return null;
864 SymbolInfo? find_invoker (Method method) {
865 /* most common use case is invoker has at least the given method prefix
866 and the same parameter names */
867 var prefix = "%s_".printf (method.name);
868 foreach (var info in current_symbols_info) {
869 if (!info.symbol.name.has_prefix (prefix)) {
870 continue;
872 Method? invoker = info.symbol as Method;
873 if (invoker == null || (method.get_parameters ().size != invoker.get_parameters ().size)) {
874 continue;
876 var iter = invoker.get_parameters ().iterator ();
877 foreach (var param in method.get_parameters ()) {
878 assert (iter.next ());
879 if (param.name != iter.get ().name) {
880 invoker = null;
881 break;
884 if (invoker != null) {
885 return info;
889 return null;
892 void merge (SymbolInfo info, ArrayList<SymbolInfo> colliding, HashSet<SymbolInfo> merged) {
893 if (info.symbol is Struct) {
894 var gtype_struct_for = info.girdata["glib:is-gtype-struct-for"];
895 if (gtype_struct_for != null) {
896 var iface = get_current_first_symbol (gtype_struct_for) as Interface;
897 if (iface != null) {
898 // set the interface struct name
899 iface.set_type_cname (((Struct) info.symbol).get_cname ());
901 merged.add (info);
903 } else if (info.symbol is Property) {
904 var prop = (Property) info.symbol;
905 foreach (var cinfo in colliding) {
906 var sym = cinfo.symbol;
907 if (sym is Signal || sym is Field) {
908 // properties take precedence
909 merged.add (cinfo);
910 } else if (sym is Method) {
911 // assume method is getter
912 merged.add (cinfo);
915 var getter_name = "get_%s".printf (prop.name);
916 var setter_name = "set_%s".printf (prop.name);
917 if (prop.get_accessor != null) {
918 var getter_method = get_current_first_symbol (getter_name) as Method;
919 if (getter_method != null) {
920 prop.no_accessor_method = false;
921 prop.get_accessor.value_type.value_owned = getter_method.return_type.value_owned;
923 } else if (prop.set_accessor != null && get_current_first_symbol_info (setter_name) != null) {
924 prop.no_accessor_method = false;
926 } else if (info.symbol is Signal) {
927 var sig = (Signal) info.symbol;
928 foreach (var cinfo in colliding) {
929 var sym = cinfo.symbol;
930 if (sym is Method) {
931 var method = (Method) sym;
932 if (method.is_virtual) {
933 sig.is_virtual = true;
934 } else {
935 sig.has_emitter = true;
937 assume_parameter_names (sig, method, false);
938 merged.add (cinfo);
939 } else if (sym is Field) {
940 merged.add (cinfo);
943 } else if (info.symbol is Method && !(info.symbol is CreationMethod)) {
944 var m = (Method) info.symbol;
945 foreach (var cinfo in colliding) {
946 var sym = cinfo.symbol;
947 if (sym != m && m.is_virtual && sym is Method) {
948 bool different_invoker = false;
949 foreach (var attr in m.attributes) {
950 if (attr.name == "NoWrapper") {
951 /* no invoker but this method has the same name,
952 most probably the invoker has a different name
953 and g-ir-scanner missed it */
954 var invoker = find_invoker (m);
955 if (invoker != null) {
956 m.vfunc_name = m.name;
957 m.name = invoker.symbol.name;
958 m.attributes.remove (attr);
959 merged.add (invoker);
960 different_invoker = true;
962 break;
965 if (!different_invoker) {
966 merged.add (cinfo);
970 // merge custom vfunc
971 if (info.metadata.has_argument (ArgumentType.VFUNC_NAME)) {
972 var vfunc = get_current_first_symbol_info (info.metadata.get_string (ArgumentType.VFUNC_NAME));
973 if (vfunc != null && vfunc != info) {
974 merged.add (vfunc);
977 if (m.coroutine) {
978 // handle async methods
979 string finish_method_base;
980 if (m.name.has_suffix ("_async")) {
981 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
982 } else {
983 finish_method_base = m.name;
985 var finish_method_info = get_current_first_symbol_info (finish_method_base + "_finish");
987 // check if the method is using non-standard finish method name
988 if (finish_method_info == null) {
989 var method_cname = m.get_finish_cname ();
990 foreach (var minfo in current_symbols_info) {
991 if (minfo.symbol is Method && ((Method) minfo.symbol).get_cname () == method_cname) {
992 finish_method_info = minfo;
993 break;
998 if (finish_method_info != null && finish_method_info.symbol is Method) {
999 var finish_method = (Method) finish_method_info.symbol;
1000 Method method;
1001 if (finish_method is CreationMethod) {
1002 method = new CreationMethod (((CreationMethod) finish_method).class_name, null, m.source_reference);
1003 method.access = m.access;
1004 method.binding = m.binding;
1005 method.external = true;
1006 method.coroutine = true;
1007 method.has_construct_function = finish_method.has_construct_function;
1008 method.attributes = m.attributes.copy ();
1009 method.set_cname (m.get_cname ());
1010 if (finish_method_base == "new") {
1011 method.name = null;
1012 } else if (finish_method_base.has_prefix ("new_")) {
1013 method.name = m.name.substring ("new_".length);
1015 foreach (var param in m.get_parameters ()) {
1016 method.add_parameter (param);
1018 info.symbol = method;
1019 } else {
1020 method = m;
1022 method.return_type = finish_method.return_type.copy ();
1023 method.no_array_length = finish_method.no_array_length;
1024 method.array_null_terminated = finish_method.array_null_terminated;
1025 foreach (var param in finish_method.get_parameters ()) {
1026 if (param.direction == ParameterDirection.OUT) {
1027 var async_param = param.copy ();
1028 if (method.scope.lookup (param.name) != null) {
1029 // parameter name conflict
1030 async_param.name += "_out";
1032 method.add_parameter (async_param);
1035 foreach (DataType error_type in finish_method.get_error_types ()) {
1036 method.add_error_type (error_type.copy ());
1038 merged.add (finish_method_info);
1041 } else if (info.symbol is Field) {
1042 foreach (var cinfo in colliding) {
1043 var sym = cinfo.symbol;
1044 if (sym is Method) {
1045 // assume method is getter
1046 merged.add (cinfo);
1050 var field = (Field) info.symbol;
1051 if (field.variable_type is ArrayType) {
1052 var array_length = get_current_first_symbol_info ("n_%s".printf (field.name));
1053 if (array_length == null) {
1054 array_length = get_current_first_symbol_info ("%s_length".printf (field.name));
1056 if (array_length != null) {
1057 // array has length
1058 field.set_array_length_cname (array_length.symbol.name);
1059 field.no_array_length = false;
1060 merged.add (array_length);
1066 void postprocess_symbol (Symbol sym, Metadata metadata) {
1067 // deprecation
1068 sym.replacement = metadata.get_string (ArgumentType.REPLACEMENT);
1069 sym.deprecated_since = element_get_string ("deprecated-version", ArgumentType.DEPRECATED_SINCE);
1070 sym.deprecated = metadata.get_bool (ArgumentType.DEPRECATED) || sym.replacement != null || sym.deprecated_since != null;
1072 // mark to be reparented
1073 if (metadata.has_argument (ArgumentType.PARENT)) {
1074 var target_symbol = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
1075 var reparent_list = symbol_reparent_map[target_symbol];
1076 if (reparent_list == null) {
1077 reparent_list = new ArrayList<Symbol>();
1078 symbol_reparent_map[target_symbol] = reparent_list;
1080 reparent_list.add (sym);
1082 // if referenceable, map unresolved references to point to the new place
1083 if (sym is Namespace || sym is TypeSymbol) {
1084 set_symbol_mapping (sym, new UnresolvedSymbol (target_symbol, sym.name));
1088 if (sym is Class) {
1089 var cl = (Class) sym;
1090 if (cl.default_construction_method == null) {
1091 // always provide constructor in generated bindings
1092 // to indicate that implicit Object () chainup is allowed
1093 var cm = new CreationMethod (null, null, cl.source_reference);
1094 cm.has_construct_function = false;
1095 cm.access = SymbolAccessibility.PROTECTED;
1096 cl.add_method (cm);
1101 void merge_add_process (Symbol container) {
1102 var merged = new HashSet<SymbolInfo> ();
1103 foreach (var info in current_symbols_info) {
1104 merge (info, get_colliding_symbols_info (info), merged);
1107 foreach (var info in current_symbols_info) {
1108 if (merged.contains (info) || info.metadata.get_bool (ArgumentType.HIDDEN)) {
1109 continue;
1111 if (!(current_symbol is Namespace && info.symbol is Method) && !info.metadata.has_argument (ArgumentType.PARENT)) {
1112 add_symbol_to_container (container, info.symbol);
1114 postprocess_symbol (info.symbol, info.metadata);
1118 Metadata get_current_metadata () {
1119 var name = reader.name;
1120 var child_name = reader.get_attribute ("name");
1121 if (child_name == null) {
1122 return Metadata.empty;
1125 var type = MetadataType.GENERIC;
1126 if (name == "glib:signal") {
1127 child_name = child_name.replace ("-", "_");
1128 type = MetadataType.SIGNAL;
1129 } else if (name == "property") {
1130 type = MetadataType.PROPERTY;
1133 return metadata.match_child (child_name, type);
1136 bool push_metadata () {
1137 var new_metadata = get_current_metadata ();
1138 // skip ?
1139 if (new_metadata.has_argument (ArgumentType.SKIP)) {
1140 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1141 return false;
1143 } else if (reader.get_attribute ("introspectable") == "0") {
1144 return false;
1147 metadata_stack.add (metadata);
1148 metadata = new_metadata;
1149 girdata_stack.add (girdata);
1150 girdata = new HashMap<string,string> (str_hash, str_equal);
1152 return true;
1155 void pop_metadata () {
1156 metadata = metadata_stack[metadata_stack.size - 1];
1157 metadata_stack.remove_at (metadata_stack.size - 1);
1158 girdata = girdata_stack[girdata_stack.size - 1];
1159 girdata_stack.remove_at (girdata_stack.size - 1);
1162 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1163 int type_arguments_length = (int) type_arguments.length;
1164 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1166 int depth = 0;
1167 for (var c = 0 ; c < type_arguments_length ; c++) {
1168 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1169 depth++;
1170 current.append_unichar (type_arguments[c]);
1171 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1172 depth--;
1173 current.append_unichar (type_arguments[c]);
1174 } else if (type_arguments[c] == ',') {
1175 if (depth == 0) {
1176 var dt = parse_type_from_string (current.str, true, source_reference);
1177 if (dt == null) {
1178 return false;
1180 parent_type.add_type_argument (dt);
1181 current.truncate ();
1182 } else {
1183 current.append_unichar (type_arguments[c]);
1185 } else {
1186 current.append_unichar (type_arguments[c]);
1190 var dt = parse_type_from_string (current.str, true, source_reference);
1191 if (dt == null) {
1192 return false;
1194 parent_type.add_type_argument (dt);
1196 return true;
1199 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1200 if (type_from_string_regex == null) {
1201 try {
1202 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[,*\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1203 } catch (GLib.RegexError e) {
1204 GLib.error ("Unable to compile regex: %s", e.message);
1208 GLib.MatchInfo match;
1209 if (!type_from_string_regex.match (type_string, 0, out match)) {
1210 Report.error (source_reference, "unable to parse type");
1211 return null;
1214 DataType? type = null;
1216 var ownership_data = match.fetch (1);
1217 var type_name = match.fetch (2);
1218 var type_arguments_data = match.fetch (3);
1219 var pointers_data = match.fetch (4);
1220 var array_data = match.fetch (5);
1221 var nullable_data = match.fetch (6);
1223 var nullable = nullable_data != null && nullable_data.length > 0;
1225 if (ownership_data == null && type_name == "void") {
1226 if (array_data == null && !nullable) {
1227 type = new VoidType (source_reference);
1228 if (pointers_data != null) {
1229 for (int i=0; i < pointers_data.length; i++) {
1230 type = new PointerType (type);
1233 return type;
1234 } else {
1235 Report.error (source_reference, "invalid void type");
1236 return null;
1240 bool value_owned = owned_by_default;
1242 if (ownership_data == "owned") {
1243 if (owned_by_default) {
1244 Report.error (source_reference, "unexpected `owned' keyword");
1245 } else {
1246 value_owned = true;
1248 } else if (ownership_data == "unowned") {
1249 if (owned_by_default) {
1250 value_owned = false;
1251 } else {
1252 Report.error (source_reference, "unexpected `unowned' keyword");
1253 return null;
1257 var sym = parse_symbol_from_string (type_name, source_reference);
1258 if (sym == null) {
1259 return null;
1261 type = new UnresolvedType.from_symbol (sym, source_reference);
1263 if (type_arguments_data != null && type_arguments_data.length > 0) {
1264 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1265 return null;
1269 if (pointers_data != null) {
1270 for (int i=0; i < pointers_data.length; i++) {
1271 type = new PointerType (type);
1275 if (array_data != null) {
1276 type = new ArrayType (type, (int) array_data.length - 1, source_reference);
1279 type.nullable = nullable;
1280 type.value_owned = value_owned;
1281 return type;
1284 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1285 if (metadata.has_argument (arg_type)) {
1286 return metadata.get_string (arg_type);
1287 } else {
1288 return reader.get_attribute (attribute_name);
1293 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1294 * If type arguments change, the C declaration is not affected.
1296 DataType? element_get_type (DataType orig_type, bool owned_by_default, out bool changed = null) {
1297 changed = false;
1298 var type = orig_type;
1300 if (metadata.has_argument (ArgumentType.TYPE)) {
1301 var new_type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1302 changed = true;
1303 return new_type;
1306 if (type is VoidType) {
1307 return type;
1310 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1311 type.remove_all_type_arguments ();
1312 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1315 if (metadata.get_bool (ArgumentType.ARRAY)) {
1316 type = new ArrayType (type, 1, type.source_reference);
1317 changed = true;
1320 if (owned_by_default) {
1321 if (metadata.has_argument (ArgumentType.UNOWNED)) {
1322 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED);
1324 } else {
1325 if (metadata.has_argument (ArgumentType.OWNED)) {
1326 type.value_owned = metadata.get_bool (ArgumentType.OWNED);
1329 if (metadata.has_argument (ArgumentType.NULLABLE)) {
1330 type.nullable = metadata.get_bool (ArgumentType.NULLABLE);
1333 return type;
1336 string? element_get_name (bool remap = false) {
1337 var name = reader.get_attribute ("name");
1338 var orig_name = name;
1339 var pattern = metadata.get_string (ArgumentType.NAME);
1340 if (pattern != null) {
1341 try {
1342 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1343 GLib.MatchInfo match;
1344 if (!regex.match (name, 0, out match)) {
1345 name = pattern;
1346 } else {
1347 var matched = match.fetch (1);
1348 if (matched != null && matched.length > 0) {
1349 name = matched;
1350 } else {
1351 name = pattern;
1354 } catch (Error e) {
1355 name = pattern;
1357 } else {
1358 if (name != null && name.has_suffix ("Enum")) {
1359 name = name.substring (0, name.length - "Enum".length);
1362 if (name != orig_name && remap) {
1363 set_symbol_mapping (parse_symbol_from_string (orig_name), parse_symbol_from_string (name));
1366 return name;
1369 void set_array_ccode (Symbol sym, ParameterInfo info) {
1370 if (sym is Method) {
1371 var m = (Method) sym;
1372 m.carray_length_parameter_position = info.vala_idx;
1373 } else if (sym is Delegate) {
1374 var d = (Delegate) sym;
1375 d.carray_length_parameter_position = info.vala_idx;
1376 } else {
1377 var param = (Parameter) sym;
1378 param.carray_length_parameter_position = info.vala_idx;
1379 param.set_array_length_cname (info.param.name);
1381 if (info.param.variable_type.to_qualified_string () != "int") {
1382 var unresolved_type = (UnresolvedType) info.param.variable_type;
1383 var resolved_struct = resolve_symbol (glib_ns.scope, unresolved_type.unresolved_symbol) as Struct;
1384 if (resolved_struct != null) {
1385 if (sym is Method) {
1386 var m = (Method) sym;
1387 m.array_length_type = resolved_struct.get_cname ();
1388 } else {
1389 var param = (Parameter) sym;
1390 param.array_length_type = resolved_struct.get_cname ();
1396 void parse_repository () {
1397 start_element ("repository");
1398 if (reader.get_attribute ("version") != GIR_VERSION) {
1399 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1400 return;
1402 next ();
1403 while (current_token == MarkupTokenType.START_ELEMENT) {
1404 if (reader.name == "namespace") {
1405 var ns = parse_namespace ();
1406 if (ns != null) {
1407 context.root.add_namespace (ns);
1409 } else if (reader.name == "include") {
1410 parse_include ();
1411 } else if (reader.name == "package") {
1412 var pkg = parse_package ();
1413 if (context.has_package (pkg)) {
1414 // package already provided elsewhere, stop parsing this GIR
1415 return;
1416 } else {
1417 context.add_package (pkg);
1419 } else if (reader.name == "c:include") {
1420 parse_c_include ();
1421 } else {
1422 // error
1423 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1424 skip_element ();
1427 end_element ("repository");
1429 report_unused_metadata (metadata);
1432 void parse_include () {
1433 start_element ("include");
1434 var pkg = reader.get_attribute ("name");
1435 var version = reader.get_attribute ("version");
1436 if (version != null) {
1437 pkg = "%s-%s".printf (pkg, version);
1439 // add the package to the queue
1440 context.add_external_package (pkg);
1441 next ();
1442 end_element ("include");
1445 string parse_package () {
1446 start_element ("package");
1447 var pkg = reader.get_attribute ("name");
1448 next ();
1449 end_element ("package");
1450 return pkg;
1453 void parse_c_include () {
1454 start_element ("c:include");
1455 cheader_filenames += reader.get_attribute ("name");
1456 next ();
1457 end_element ("c:include");
1460 void skip_element () {
1461 next ();
1463 int level = 1;
1464 while (level > 0) {
1465 if (current_token == MarkupTokenType.START_ELEMENT) {
1466 level++;
1467 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1468 level--;
1469 } else if (current_token == MarkupTokenType.EOF) {
1470 Report.error (get_current_src (), "unexpected end of file");
1471 break;
1473 next ();
1477 Namespace? parse_namespace () {
1478 start_element ("namespace");
1480 bool new_namespace = false;
1481 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
1482 string namespace_name = cprefix;
1483 string gir_namespace = reader.get_attribute ("name");
1484 string gir_version = reader.get_attribute ("version");
1485 if (namespace_name == null) {
1486 namespace_name = gir_namespace;
1488 current_source_file.gir_namespace = gir_namespace;
1489 current_source_file.gir_version = gir_version;
1491 var ns_metadata = metadata.match_child (gir_namespace);
1492 if (ns_metadata.has_argument (ArgumentType.NAME)) {
1493 namespace_name = ns_metadata.get_string (ArgumentType.NAME);
1496 var ns = context.root.scope.lookup (namespace_name) as Namespace;
1497 if (ns == null) {
1498 ns = new Namespace (namespace_name, get_current_src ());
1499 new_namespace = true;
1500 } else {
1501 if (ns.external_package) {
1502 ns.attributes = null;
1503 ns.source_reference = get_current_src ();
1507 if (gir_namespace != ns.name) {
1508 set_symbol_mapping (new UnresolvedSymbol (null, gir_namespace), ns);
1511 if (cprefix != null) {
1512 ns.add_cprefix (cprefix);
1513 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
1516 if (ns_metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1517 var val = ns_metadata.get_string (ArgumentType.CHEADER_FILENAME);
1518 foreach (string filename in val.split (",")) {
1519 ns.add_cheader_filename (filename);
1521 } else {
1522 foreach (string c_header in cheader_filenames) {
1523 ns.add_cheader_filename (c_header);
1527 next ();
1528 var current_namespace_methods = namespace_methods[ns];
1529 if (current_namespace_methods == null) {
1530 current_namespace_methods = new ArrayList<Method> ();
1531 namespace_methods[ns] = current_namespace_methods;
1533 var old_symbols_info = current_symbols_info;
1534 var old_symbol = current_symbol;
1535 current_symbols_info = new ArrayList<SymbolInfo> ();
1536 current_symbol = ns;
1537 gtype_callbacks = new HashMap<UnresolvedSymbol,ArrayList<Delegate>> (unresolved_symbol_hash, unresolved_symbol_equal);
1538 while (current_token == MarkupTokenType.START_ELEMENT) {
1539 if (!push_metadata ()) {
1540 skip_element ();
1541 continue;
1544 if (reader.name == "alias") {
1545 var alias = parse_alias ();
1546 aliases.add (alias);
1547 } else if (reader.name == "enumeration") {
1548 if (reader.get_attribute ("glib:error-quark") != null) {
1549 add_symbol_info (parse_error_domain ());
1550 } else {
1551 add_symbol_info (parse_enumeration ());
1553 } else if (reader.name == "bitfield") {
1554 add_symbol_info (parse_bitfield ());
1555 } else if (reader.name == "function") {
1556 var method = parse_method ("function");
1557 add_symbol_info (method);
1558 current_namespace_methods.add (method);
1559 } else if (reader.name == "callback") {
1560 add_symbol_info (parse_callback ());
1561 } else if (reader.name == "record") {
1562 if (reader.get_attribute ("glib:get-type") != null) {
1563 add_symbol_info (parse_boxed ("record"));
1564 } else {
1565 if (!reader.get_attribute ("name").has_suffix ("Private")) {
1566 add_symbol_info (parse_record ());
1567 } else {
1568 skip_element ();
1571 } else if (reader.name == "class") {
1572 add_symbol_info (parse_class ());
1573 } else if (reader.name == "interface") {
1574 var iface = parse_interface ();
1575 add_symbol_info (iface);
1576 interfaces.add (iface);
1577 } else if (reader.name == "glib:boxed") {
1578 add_symbol_info (parse_boxed ("glib:boxed"));
1579 } else if (reader.name == "union") {
1580 add_symbol_info (parse_union ());
1581 } else if (reader.name == "constant") {
1582 add_symbol_info (parse_constant ());
1583 } else {
1584 // error
1585 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
1586 skip_element ();
1589 pop_metadata ();
1591 end_element ("namespace");
1593 merge_add_process (ns);
1594 current_symbols_info = old_symbols_info;
1595 current_symbol = old_symbol;
1596 postprocess_gtype_callbacks (ns);
1598 if (!new_namespace) {
1599 ns = null;
1602 return ns;
1605 Alias parse_alias () {
1606 // alias has no type information
1607 start_element ("alias");
1608 var alias = new Alias ();
1609 alias.source_reference = get_current_src ();
1610 alias.name = reader.get_attribute ("name");
1611 alias.cname = reader.get_attribute ("c:type");
1612 alias.parent_symbol = current_symbol;
1613 next ();
1615 alias.base_type = element_get_type (parse_type (null, null, true), true);
1617 end_element ("alias");
1618 return alias;
1621 private void calculate_common_prefix (ref string common_prefix, string cname) {
1622 if (common_prefix == null) {
1623 common_prefix = cname;
1624 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
1625 // FIXME: could easily be made faster
1626 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1628 } else {
1629 while (!cname.has_prefix (common_prefix)) {
1630 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1633 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
1634 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
1635 // enum values may not consist solely of digits
1636 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1640 Symbol parse_enumeration (string element_name = "enumeration", bool error_domain = false) {
1641 start_element (element_name);
1643 Symbol sym;
1644 if (error_domain) {
1645 sym = new ErrorDomain (element_get_name (true), get_current_src ());
1646 } else {
1647 var en = new Enum (element_get_name (), get_current_src ());
1648 if (element_name == "bitfield") {
1649 en.is_flags = true;
1651 sym = en;
1653 sym.access = SymbolAccessibility.PUBLIC;
1655 string cname = reader.get_attribute ("c:type");
1656 string common_prefix = null;
1658 next ();
1660 var old_symbol = current_symbol;
1661 current_symbol = sym;
1662 while (current_token == MarkupTokenType.START_ELEMENT) {
1663 if (!push_metadata ()) {
1664 skip_element ();
1665 continue;
1668 if (reader.name == "member") {
1669 if (error_domain) {
1670 ErrorCode ec = parse_error_member ();
1671 ((ErrorDomain) sym).add_code (ec);
1672 calculate_common_prefix (ref common_prefix, ec.get_cname ());
1673 } else {
1674 var ev = parse_enumeration_member ();
1675 ((Enum) sym).add_value (ev);
1676 calculate_common_prefix (ref common_prefix, ev.get_cname ());
1678 } else {
1679 // error
1680 Report.error (get_current_src (), "unknown child element `%s' in `%s'".printf (reader.name, element_name));
1681 skip_element ();
1684 pop_metadata ();
1687 if (cname != null) {
1688 if (sym is Enum) {
1689 ((Enum) sym).set_cname (cname);
1690 ((Enum) sym).set_cprefix (common_prefix);
1691 } else {
1692 ((ErrorDomain) sym).set_cname (cname);
1693 ((ErrorDomain) sym).set_cprefix (common_prefix);
1697 end_element (element_name);
1698 current_symbol = old_symbol;
1699 return sym;
1702 ErrorDomain parse_error_domain () {
1703 return parse_enumeration ("enumeration", true) as ErrorDomain;
1706 Enum parse_bitfield () {
1707 return parse_enumeration ("bitfield") as Enum;
1710 EnumValue parse_enumeration_member () {
1711 start_element ("member");
1712 var ev = new EnumValue (reader.get_attribute ("name").up ().replace ("-", "_"), null, get_current_src ());
1713 ev.set_cname (reader.get_attribute ("c:identifier"));
1714 next ();
1715 end_element ("member");
1716 return ev;
1719 ErrorCode parse_error_member () {
1720 start_element ("member");
1722 ErrorCode ec;
1723 string name = reader.get_attribute ("name").up ().replace ("-", "_");
1724 string value = reader.get_attribute ("value");
1725 if (value != null) {
1726 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
1727 } else {
1728 ec = new ErrorCode (name);
1730 ec.set_cname (reader.get_attribute ("c:identifier"));
1732 next ();
1733 end_element ("member");
1734 return ec;
1737 DataType parse_return_value (out string? ctype = null) {
1738 start_element ("return-value");
1739 string transfer = reader.get_attribute ("transfer-ownership");
1740 string allow_none = reader.get_attribute ("allow-none");
1741 next ();
1742 var transfer_elements = transfer == "full";
1743 var type = parse_type (out ctype, null, transfer_elements);
1744 if (transfer == "full" || transfer == "container") {
1745 type.value_owned = true;
1747 if (allow_none == "1") {
1748 type.nullable = true;
1750 end_element ("return-value");
1751 return type;
1754 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) {
1755 Parameter param;
1757 array_length_idx = -1;
1758 closure_idx = -1;
1759 destroy_idx = -1;
1761 start_element ("parameter");
1762 string name = reader.get_attribute ("name");
1763 if (name == null) {
1764 name = default_name;
1766 string direction = null;
1767 if (metadata.has_argument (ArgumentType.OUT)) {
1768 if (metadata.get_bool (ArgumentType.OUT)) {
1769 direction = "out";
1770 } // null otherwise
1771 } else if (metadata.has_argument (ArgumentType.REF)) {
1772 if (metadata.get_bool (ArgumentType.REF)) {
1773 direction = "inout";
1774 } // null otherwise
1775 } else {
1776 direction = reader.get_attribute ("direction");
1778 string transfer = reader.get_attribute ("transfer-ownership");
1779 string allow_none = reader.get_attribute ("allow-none");
1781 scope = element_get_string ("scope", ArgumentType.SCOPE);
1783 string closure = reader.get_attribute ("closure");
1784 string destroy = reader.get_attribute ("destroy");
1785 if (closure != null && &closure_idx != null) {
1786 closure_idx = int.parse (closure);
1788 if (destroy != null && &destroy_idx != null) {
1789 destroy_idx = int.parse (destroy);
1792 next ();
1793 if (reader.name == "varargs") {
1794 start_element ("varargs");
1795 next ();
1796 param = new Parameter.with_ellipsis (get_current_src ());
1797 end_element ("varargs");
1798 } else {
1799 string ctype;
1800 var type = parse_type (out ctype, out array_length_idx, transfer == "full");
1801 bool changed;
1802 type = element_get_type (type, false, out changed);
1803 if (!changed) {
1804 // discard ctype, duplicated information
1805 ctype = null;
1808 if (type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
1809 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
1812 if (transfer == "full" || transfer == "container" || destroy != null) {
1813 type.value_owned = true;
1815 if (allow_none == "1") {
1816 type.nullable = true;
1818 param = new Parameter (name, type, get_current_src ());
1819 param.ctype = ctype;
1820 if (direction == "out") {
1821 param.direction = ParameterDirection.OUT;
1822 } else if (direction == "inout") {
1823 param.direction = ParameterDirection.REF;
1825 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
1827 end_element ("parameter");
1828 return param;
1831 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) {
1832 bool is_array = false;
1833 string type_name = reader.get_attribute ("name");
1835 array_length_index = -1;
1837 if (reader.name == "array") {
1838 is_array = true;
1839 start_element ("array");
1841 if (type_name == null) {
1842 if (reader.get_attribute ("length") != null
1843 && &array_length_index != null) {
1844 array_length_index = int.parse (reader.get_attribute ("length"));
1846 next ();
1847 var element_type = parse_type ();
1848 end_element ("array");
1849 return new ArrayType (element_type, 1, null);
1851 } else if (reader.name == "callback"){
1852 var callback = parse_callback ();
1853 return new DelegateType (callback);
1854 } else {
1855 start_element ("type");
1858 ctype = reader.get_attribute("c:type");
1860 next ();
1862 if (type_name == "GLib.PtrArray"
1863 && current_token == MarkupTokenType.START_ELEMENT) {
1864 type_name = "GLib.GenericArray";
1867 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
1869 // type arguments / element types
1870 while (current_token == MarkupTokenType.START_ELEMENT) {
1871 if (type_name == "GLib.ByteArray") {
1872 skip_element ();
1873 continue;
1875 var element_type = parse_type ();
1876 element_type.value_owned = transfer_elements;
1877 type.add_type_argument (element_type);
1880 end_element (is_array ? "array" : "type");
1881 return type;
1884 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
1885 no_array_length = false;
1886 array_null_terminated = false;
1888 DataType type;
1889 if (type_name == "none") {
1890 type = new VoidType (get_current_src ());
1891 } else if (type_name == "gpointer") {
1892 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
1893 } else if (type_name == "GObject.Strv") {
1894 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, get_current_src ());
1895 no_array_length = true;
1896 array_null_terminated = true;
1897 } else {
1898 bool known_type = true;
1899 if (type_name == "utf8") {
1900 type_name = "string";
1901 } else if (type_name == "gboolean") {
1902 type_name = "bool";
1903 } else if (type_name == "gchar") {
1904 type_name = "char";
1905 } else if (type_name == "gshort") {
1906 type_name = "short";
1907 } else if (type_name == "gushort") {
1908 type_name = "ushort";
1909 } else if (type_name == "gint") {
1910 type_name = "int";
1911 } else if (type_name == "guint") {
1912 type_name = "uint";
1913 } else if (type_name == "glong") {
1914 if (ctype != null && ctype.has_prefix ("gssize")) {
1915 type_name = "ssize_t";
1916 } else {
1917 type_name = "long";
1919 } else if (type_name == "gulong") {
1920 if (ctype != null && ctype.has_prefix ("gsize")) {
1921 type_name = "size_t";
1922 } else {
1923 type_name = "ulong";
1925 } else if (type_name == "gint8") {
1926 type_name = "int8";
1927 } else if (type_name == "guint8") {
1928 type_name = "uint8";
1929 } else if (type_name == "gint16") {
1930 type_name = "int16";
1931 } else if (type_name == "guint16") {
1932 type_name = "uint16";
1933 } else if (type_name == "gint32") {
1934 type_name = "int32";
1935 } else if (type_name == "guint32") {
1936 type_name = "uint32";
1937 } else if (type_name == "gint64") {
1938 type_name = "int64";
1939 } else if (type_name == "guint64") {
1940 type_name = "uint64";
1941 } else if (type_name == "gfloat") {
1942 type_name = "float";
1943 } else if (type_name == "gdouble") {
1944 type_name = "double";
1945 } else if (type_name == "filename") {
1946 type_name = "string";
1947 } else if (type_name == "GLib.offset") {
1948 type_name = "int64";
1949 } else if (type_name == "gsize") {
1950 type_name = "size_t";
1951 } else if (type_name == "gssize") {
1952 type_name = "ssize_t";
1953 } else if (type_name == "GType") {
1954 type_name = "GLib.Type";
1955 } else if (type_name == "GLib.String") {
1956 type_name = "GLib.StringBuilder";
1957 } else if (type_name == "GObject.Class") {
1958 type_name = "GLib.ObjectClass";
1959 } else if (type_name == "GLib.unichar") {
1960 type_name = "unichar";
1961 } else if (type_name == "GLib.Data") {
1962 type_name = "GLib.Datalist";
1963 } else if (type_name == "Atk.ImplementorIface") {
1964 type_name = "Atk.Implementor";
1965 } else {
1966 known_type = false;
1968 var sym = parse_symbol_from_string (type_name, get_current_src ());
1969 type = new UnresolvedType.from_symbol (sym, get_current_src ());
1970 if (!known_type) {
1971 unresolved_gir_symbols.add (sym);
1975 return type;
1978 Struct parse_record () {
1979 start_element ("record");
1980 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
1981 st.external = true;
1982 st.access = SymbolAccessibility.PUBLIC;
1984 string cname = reader.get_attribute ("c:type");
1985 if (cname != null) {
1986 st.set_cname (cname);
1989 current_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
1990 if (current_gtype_struct_for != null) {
1991 girdata["glib:is-gtype-struct-for"] = current_gtype_struct_for;
1994 next ();
1995 var old_symbols_info = current_symbols_info;
1996 var old_symbol = current_symbol;
1997 current_symbols_info = new ArrayList<SymbolInfo> ();
1998 current_symbol = st;
1999 while (current_token == MarkupTokenType.START_ELEMENT) {
2000 if (!push_metadata ()) {
2001 skip_element ();
2002 continue;
2005 if (reader.name == "field") {
2006 if (reader.get_attribute ("name") != "priv") {
2007 add_symbol_info (parse_field ());
2008 } else {
2009 skip_element ();
2011 } else if (reader.name == "constructor") {
2012 parse_constructor ();
2013 } else if (reader.name == "method") {
2014 add_symbol_info (parse_method ("method"));
2015 } else if (reader.name == "union") {
2016 Struct s = parse_union ();
2017 var s_fields = s.get_fields ();
2018 foreach (var f in s_fields) {
2019 f.set_cname (s.get_cname () + "." + f.get_cname ());
2020 f.name = s.name + "_" + f.name;
2021 st.add_field (f);
2023 } else {
2024 // error
2025 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
2026 skip_element ();
2029 pop_metadata ();
2031 end_element ("record");
2033 merge_add_process (st);
2034 current_symbols_info = old_symbols_info;
2035 current_symbol = old_symbol;
2036 current_gtype_struct_for = null;
2038 return st;
2041 Class parse_class () {
2042 start_element ("class");
2043 var name = element_get_name ();
2044 string cname = reader.get_attribute ("c:type");
2045 string parent = reader.get_attribute ("parent");
2046 var cl = current_symbol.scope.lookup (name) as Class;
2047 if (cl == null) {
2048 cl = new Class (name, get_current_src ());
2049 cl.access = SymbolAccessibility.PUBLIC;
2050 cl.external = true;
2052 if (cname != null) {
2053 cl.set_cname (cname);
2056 if (parent != null) {
2057 cl.add_base_type (parse_type_from_gir_name (parent));
2060 next ();
2061 var first_field = true;
2062 var old_symbol = current_symbol;
2063 var old_symbols_info = current_symbols_info;
2064 current_symbols_info = new ArrayList<SymbolInfo> ();
2065 current_symbol = cl;
2066 while (current_token == MarkupTokenType.START_ELEMENT) {
2067 if (!push_metadata ()) {
2068 skip_element ();
2069 continue;
2072 if (reader.name == "implements") {
2073 start_element ("implements");
2074 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
2075 next ();
2076 end_element ("implements");
2077 } else if (reader.name == "constant") {
2078 add_symbol_info (parse_constant ());
2079 } else if (reader.name == "field") {
2080 if (first_field && parent != null) {
2081 // first field is guaranteed to be the parent instance
2082 skip_element ();
2083 } else {
2084 if (reader.get_attribute ("name") != "priv") {
2085 add_symbol_info (parse_field ());
2086 } else {
2087 skip_element ();
2090 first_field = false;
2091 } else if (reader.name == "property") {
2092 add_symbol_info (parse_property ());
2093 } else if (reader.name == "constructor") {
2094 add_symbol_info (parse_constructor ());
2095 } else if (reader.name == "function") {
2096 add_symbol_info (parse_method ("function"));
2097 } else if (reader.name == "method") {
2098 add_symbol_info (parse_method ("method"));
2099 } else if (reader.name == "virtual-method") {
2100 add_symbol_info (parse_method ("virtual-method"));
2101 } else if (reader.name == "union") {
2102 Struct s = parse_union ();
2103 var s_fields = s.get_fields ();
2104 foreach (var f in s_fields) {
2105 f.set_cname (s.get_cname () + "." + f.get_cname ());
2106 f.name = s.name + "_" + f.name;
2107 add_symbol_info (f);
2109 } else if (reader.name == "glib:signal") {
2110 add_symbol_info (parse_signal ());
2111 } else {
2112 // error
2113 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2114 skip_element ();
2117 pop_metadata ();
2120 merge_add_process (cl);
2121 current_symbols_info = old_symbols_info;
2122 current_symbol = old_symbol;
2124 end_element ("class");
2125 return cl;
2128 Interface parse_interface () {
2129 start_element ("interface");
2130 var iface = new Interface (element_get_name (), get_current_src ());
2131 iface.access = SymbolAccessibility.PUBLIC;
2132 iface.external = true;
2134 string cname = reader.get_attribute ("c:type");
2135 if (cname != null) {
2136 iface.set_cname (cname);
2139 next ();
2140 var old_symbol = current_symbol;
2141 var old_symbols_info = current_symbols_info;
2142 current_symbol = iface;
2143 current_symbols_info = new ArrayList<SymbolInfo> ();
2144 while (current_token == MarkupTokenType.START_ELEMENT) {
2145 if (!push_metadata ()) {
2146 skip_element ();
2147 continue;
2150 if (reader.name == "prerequisite") {
2151 start_element ("prerequisite");
2152 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2153 next ();
2154 end_element ("prerequisite");
2155 } else if (reader.name == "field") {
2156 add_symbol_info (parse_field ());
2157 } else if (reader.name == "property") {
2158 add_symbol_info (parse_property ());
2159 } else if (reader.name == "virtual-method") {
2160 add_symbol_info (parse_method ("virtual-method"));
2161 } else if (reader.name == "function") {
2162 add_symbol_info (parse_method ("function"));
2163 } else if (reader.name == "method") {
2164 add_symbol_info (parse_method ("method"));
2165 } else if (reader.name == "glib:signal") {
2166 add_symbol_info (parse_signal ());
2167 } else {
2168 // error
2169 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2170 skip_element ();
2173 pop_metadata ();
2176 merge_add_process (iface);
2177 current_symbol = old_symbol;
2178 current_symbols_info = old_symbols_info;
2180 end_element ("interface");
2181 return iface;
2184 Field parse_field () {
2185 start_element ("field");
2186 string name = reader.get_attribute ("name");
2187 string allow_none = reader.get_attribute ("allow-none");
2188 next ();
2189 var type = parse_type ();
2190 type = element_get_type (type, true);
2191 if (type is DelegateType && current_gtype_struct_for != null) {
2192 // virtual
2193 var gtype_struct_for = parse_symbol_from_string (current_gtype_struct_for);
2194 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2195 if (callbacks == null) {
2196 callbacks = new ArrayList<Delegate> ();
2197 gtype_callbacks.set (gtype_struct_for, callbacks);
2199 callbacks.add (((DelegateType) type).delegate_symbol);
2201 var field = new Field (name, type, null, get_current_src ());
2202 field.access = SymbolAccessibility.PUBLIC;
2203 field.no_array_length = true;
2204 field.array_null_terminated = true;
2205 if (allow_none == "1") {
2206 type.nullable = true;
2208 end_element ("field");
2209 return field;
2212 Property parse_property () {
2213 start_element ("property");
2214 string name = reader.get_attribute ("name").replace ("-", "_");
2215 string readable = reader.get_attribute ("readable");
2216 string writable = reader.get_attribute ("writable");
2217 string construct_ = reader.get_attribute ("construct");
2218 string construct_only = reader.get_attribute ("construct-only");
2219 next ();
2220 bool no_array_length;
2221 bool array_null_terminated;
2222 var type = parse_type (null, null, false, out no_array_length, out array_null_terminated);
2223 var prop = new Property (name, type, null, null, get_current_src ());
2224 prop.access = SymbolAccessibility.PUBLIC;
2225 prop.external = true;
2226 prop.no_accessor_method = true;
2227 prop.no_array_length = no_array_length;
2228 prop.array_null_terminated = array_null_terminated;
2229 if (readable != "0") {
2230 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
2231 prop.get_accessor.value_type.value_owned = true;
2233 if (writable == "1" || construct_only == "1") {
2234 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
2236 end_element ("property");
2237 return prop;
2240 Delegate parse_callback () {
2241 return this.parse_function ("callback") as Delegate;
2244 CreationMethod parse_constructor () {
2245 return parse_function ("constructor") as CreationMethod;
2248 class ParameterInfo {
2249 public ParameterInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx) {
2250 this.param = param;
2251 this.array_length_idx = array_length_idx;
2252 this.closure_idx = closure_idx;
2253 this.destroy_idx = destroy_idx;
2254 this.vala_idx = 0.0F;
2255 this.keep = true;
2258 public Parameter param;
2259 public float vala_idx;
2260 public int array_length_idx;
2261 public int closure_idx;
2262 public int destroy_idx;
2263 public bool keep;
2266 Symbol parse_function (string element_name) {
2267 start_element (element_name);
2268 string name = element_get_name ();
2269 string cname = reader.get_attribute ("c:identifier");
2270 string throws_string = reader.get_attribute ("throws");
2271 string invoker = reader.get_attribute ("invoker");
2273 next ();
2274 DataType return_type;
2275 string return_ctype = null;
2276 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2277 return_type = parse_return_value (out return_ctype);
2278 } else {
2279 return_type = new VoidType ();
2281 return_type = element_get_type (return_type, true);
2283 Symbol s;
2285 if (element_name == "callback") {
2286 s = new Delegate (name, return_type, get_current_src ());
2287 } else if (element_name == "constructor") {
2288 if (name == "new") {
2289 name = null;
2290 } else if (name.has_prefix ("new_")) {
2291 name = name.substring ("new_".length);
2293 var m = new CreationMethod (null, name, get_current_src ());
2294 m.has_construct_function = false;
2296 string parent_ctype = null;
2297 if (current_symbol is Class) {
2298 parent_ctype = ((Class) current_symbol).get_cname ();
2300 if (return_ctype != null && (parent_ctype == null || return_ctype != parent_ctype + "*")) {
2301 m.custom_return_type_cname = return_ctype;
2303 s = m;
2304 } else {
2305 s = new Method (name, return_type, get_current_src ());
2308 s.access = SymbolAccessibility.PUBLIC;
2309 if (cname != null) {
2310 if (s is Method) {
2311 ((Method) s).set_cname (cname);
2312 } else {
2313 ((Delegate) s).set_cname (cname);
2317 s.external = true;
2319 if (element_name == "virtual-method" || element_name == "callback") {
2320 if (s is Method) {
2321 ((Method) s).is_virtual = true;
2322 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
2323 s.attributes.append (new Attribute ("NoWrapper", s.source_reference));
2327 if (invoker != null) {
2328 s.name = invoker;
2330 } else if (element_name == "function") {
2331 ((Method) s).binding = MemberBinding.STATIC;
2334 if (s is Method && !(s is CreationMethod)) {
2335 var method = (Method) s;
2336 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
2337 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
2338 method.is_abstract = false;
2339 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
2340 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
2341 method.is_virtual = false;
2343 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
2344 method.vfunc_name = metadata.get_string (ArgumentType.VFUNC_NAME);
2345 method.is_virtual = true;
2349 var parameters = new ArrayList<ParameterInfo> ();
2350 var array_length_parameters = new ArrayList<int> ();
2351 var closure_parameters = new ArrayList<int> ();
2352 var destroy_parameters = new ArrayList<int> ();
2353 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2354 start_element ("parameters");
2355 next ();
2357 while (current_token == MarkupTokenType.START_ELEMENT) {
2358 if (!push_metadata ()) {
2359 skip_element ();
2360 continue;
2363 int array_length_idx, closure_idx, destroy_idx;
2364 string scope;
2365 string default_param_name = null;
2366 default_param_name = "arg%d".printf (parameters.size);
2367 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, default_param_name);
2368 if (array_length_idx != -1) {
2369 array_length_parameters.add (array_length_idx);
2371 if (closure_idx != -1) {
2372 closure_parameters.add (closure_idx);
2374 if (destroy_idx != -1) {
2375 destroy_parameters.add (destroy_idx);
2378 var info = new ParameterInfo(param, array_length_idx, closure_idx, destroy_idx);
2380 if (s is Method && scope == "async") {
2381 var unresolved_type = param.variable_type as UnresolvedType;
2382 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
2383 // GAsync-style method
2384 ((Method) s).coroutine = true;
2385 info.keep = false;
2389 parameters.add (info);
2390 pop_metadata ();
2392 end_element ("parameters");
2394 var array_length_idx = -1;
2395 if (return_type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
2396 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
2397 parameters[array_length_idx].keep = false;
2398 array_length_parameters.add (array_length_idx);
2401 int i = 0, j=1;
2403 int last = -1;
2404 foreach (ParameterInfo info in parameters) {
2405 if (s is Delegate && info.closure_idx == i) {
2406 var d = (Delegate) s;
2407 d.has_target = true;
2408 d.cinstance_parameter_position = (float) j - 0.1;
2409 info.keep = false;
2410 } else if (info.keep
2411 && !array_length_parameters.contains (i)
2412 && !closure_parameters.contains (i)
2413 && !destroy_parameters.contains (i)) {
2414 info.vala_idx = (float) j;
2415 info.keep = true;
2417 /* interpolate for vala_idx between this and last*/
2418 float last_idx = 0.0F;
2419 if (last != -1) {
2420 last_idx = parameters[last].vala_idx;
2422 for (int k=last+1; k < i; k++) {
2423 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
2425 last = i;
2426 j++;
2427 } else {
2428 info.keep = false;
2429 // make sure that vala_idx is always set
2430 // the above if branch does not set vala_idx for
2431 // hidden parameters at the end of the parameter list
2432 info.vala_idx = (j - 1) + (i - last) * 0.1F;
2434 i++;
2437 foreach (ParameterInfo info in parameters) {
2438 if (info.keep) {
2440 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
2441 so do it first*/
2442 if (s is Method) {
2443 ((Method) s).add_parameter (info.param);
2444 } else {
2445 ((Delegate) s).add_parameter (info.param);
2448 if (info.array_length_idx != -1) {
2449 if ((info.array_length_idx) >= parameters.size) {
2450 Report.error (get_current_src (), "invalid array_length index");
2451 continue;
2453 set_array_ccode (info.param, parameters[info.array_length_idx]);
2454 } else if (info.param.variable_type is ArrayType) {
2455 info.param.no_array_length = true;
2456 info.param.array_null_terminated = true;
2459 if (info.closure_idx != -1) {
2460 if ((info.closure_idx) >= parameters.size) {
2461 Report.error (get_current_src (), "invalid closure index");
2462 continue;
2464 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
2466 if (info.destroy_idx != -1) {
2467 if (info.destroy_idx >= parameters.size) {
2468 Report.error (get_current_src (), "invalid destroy index");
2469 continue;
2471 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
2475 if (array_length_idx != -1) {
2476 if (array_length_idx >= parameters.size) {
2477 Report.error (get_current_src (), "invalid array_length index");
2478 } else {
2479 set_array_ccode (s, parameters[array_length_idx]);
2481 } else if (return_type is ArrayType) {
2482 if (s is Method) {
2483 var m = (Method) s;
2484 m.no_array_length = true;
2485 m.array_null_terminated = true;
2486 } else {
2487 var d = (Delegate) s;
2488 d.no_array_length = true;
2489 d.array_null_terminated = true;
2493 if (throws_string == "1") {
2494 s.add_error_type (new ErrorType (null, null));
2496 end_element (element_name);
2497 return s;
2500 Method parse_method (string element_name) {
2501 return this.parse_function (element_name) as Method;
2504 Signal parse_signal () {
2505 start_element ("glib:signal");
2506 string name = reader.get_attribute ("name").replace ("-", "_");
2507 next ();
2508 DataType return_type;
2509 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2510 return_type = parse_return_value ();
2511 } else {
2512 return_type = new VoidType ();
2514 var sig = new Signal (name, return_type, get_current_src ());
2515 sig.access = SymbolAccessibility.PUBLIC;
2516 sig.external = true;
2517 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2518 start_element ("parameters");
2519 next ();
2520 while (current_token == MarkupTokenType.START_ELEMENT) {
2521 if (!push_metadata ()) {
2522 skip_element ();
2523 continue;
2526 sig.add_parameter (parse_parameter ());
2528 pop_metadata ();
2530 end_element ("parameters");
2532 end_element ("glib:signal");
2533 return sig;
2536 Class parse_boxed (string element_name) {
2537 start_element (element_name);
2538 string name = reader.get_attribute ("name");
2539 if (name == null) {
2540 name = reader.get_attribute ("glib:name");
2542 var cl = new Class (name, get_current_src ());
2543 cl.access = SymbolAccessibility.PUBLIC;
2544 cl.external = true;
2545 cl.is_compact = true;
2547 string cname = reader.get_attribute ("c:type");
2548 if (cname != null) {
2549 cl.set_cname (cname);
2552 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
2553 cl.set_free_function ("g_boxed_free");
2554 cl.set_dup_function ("g_boxed_copy");
2556 next ();
2557 var old_symbols_info = current_symbols_info;
2558 var old_symbol = current_symbol;
2559 current_symbols_info = new ArrayList<SymbolInfo> ();
2560 current_symbol = cl;
2561 while (current_token == MarkupTokenType.START_ELEMENT) {
2562 if (!push_metadata ()) {
2563 skip_element ();
2564 continue;
2567 if (reader.name == "field") {
2568 add_symbol_info (parse_field ());
2569 } else if (reader.name == "constructor") {
2570 parse_constructor ();
2571 } else if (reader.name == "method") {
2572 add_symbol_info (parse_method ("method"));
2573 } else {
2574 // error
2575 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2576 skip_element ();
2579 pop_metadata ();
2581 end_element (element_name);
2583 merge_add_process (cl);
2584 current_symbols_info = old_symbols_info;
2585 current_symbol = old_symbol;
2587 return cl;
2590 Struct parse_union () {
2591 start_element ("union");
2592 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2593 st.access = SymbolAccessibility.PUBLIC;
2594 st.external = true;
2595 next ();
2597 var old_symbol = current_symbol;
2598 current_symbol = st;
2599 while (current_token == MarkupTokenType.START_ELEMENT) {
2600 if (!push_metadata ()) {
2601 skip_element ();
2602 continue;
2605 if (reader.name == "field") {
2606 st.add_field (parse_field ());
2607 } else if (reader.name == "constructor") {
2608 parse_constructor ();
2609 } else if (reader.name == "method") {
2610 st.add_method (parse_method ("method"));
2611 } else if (reader.name == "record") {
2612 Struct s = parse_record ();
2613 var fs = s.get_fields ();
2614 foreach (var f in fs) {
2615 f.set_cname (s.get_cname () + "." + f.get_cname ());
2616 f.name = s.name + "_" + f.name;
2617 st.add_field (f);
2619 } else {
2620 // error
2621 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
2622 skip_element ();
2625 pop_metadata ();
2628 end_element ("union");
2629 current_symbol = old_symbol;
2631 return st;
2634 Constant parse_constant () {
2635 start_element ("constant");
2636 string name = element_get_name ();
2637 next ();
2638 var type = parse_type ();
2639 var c = new Constant (name, type, null, get_current_src ());
2640 c.access = SymbolAccessibility.PUBLIC;
2641 c.external = true;
2642 end_element ("constant");
2643 return c;
2646 /* Reporting */
2647 void report_unused_metadata (Metadata metadata) {
2648 if (metadata == Metadata.empty) {
2649 return;
2652 if (metadata.args.size == 0 && metadata.children.size == 0) {
2653 Report.warning (metadata.source_reference, "empty metadata");
2654 return;
2657 foreach (var arg_type in metadata.args.get_keys ()) {
2658 var arg = metadata.args[arg_type];
2659 if (!arg.used) {
2660 // if metadata is used and argument is not, then it's a unexpected argument
2661 Report.warning (arg.source_reference, "argument never used");
2665 foreach (var child in metadata.children) {
2666 if (!child.used) {
2667 Report.warning (child.source_reference, "metadata never used");
2668 } else {
2669 report_unused_metadata (child);
2674 /* Post-parsing */
2676 void resolve_gir_symbols () {
2677 // we are remapping unresolved symbols, so create them from concrete symbols
2678 foreach (var map_from in concrete_symbols_map.get_keys ()) {
2679 unresolved_symbols_map[get_unresolved_symbol(map_from)] = concrete_symbols_map[map_from];
2682 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
2683 foreach (var map_from in unresolved_gir_symbols) {
2684 while (map_from != null) {
2685 var map_to = unresolved_symbols_map[map_from];
2686 if (map_to != null) {
2687 // remap the original symbol to match the target
2688 map_from.inner = null;
2689 map_from.name = map_to.name;
2690 if (map_to is UnresolvedSymbol) {
2691 var umap_to = (UnresolvedSymbol) map_to;
2692 while (umap_to.inner != null) {
2693 umap_to = umap_to.inner;
2694 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
2695 map_from = map_from.inner;
2697 } else {
2698 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
2699 map_to = map_to.parent_symbol;
2700 map_from.inner = new UnresolvedSymbol (null, map_to.name);
2701 map_from = map_from.inner;
2704 break;
2706 map_from = map_from.inner;
2711 Symbol? resolve_symbol (Scope parent_scope, UnresolvedSymbol unresolved_symbol) {
2712 // simple symbol resolver, enough for gir
2713 if (unresolved_symbol.inner == null) {
2714 var scope = parent_scope;
2715 while (scope != null) {
2716 var sym = scope.lookup (unresolved_symbol.name);
2717 if (sym != null) {
2718 return sym;
2720 scope = scope.parent_scope;
2722 } else {
2723 var inner = resolve_symbol (parent_scope, unresolved_symbol.inner);
2724 if (inner != null) {
2725 return inner.scope.lookup (unresolved_symbol.name);
2728 return null;
2731 void postprocess_interfaces () {
2732 foreach (var iface in interfaces) {
2733 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
2734 ensure we have at least one instantiable prerequisite */
2735 bool has_instantiable_prereq = false;
2736 foreach (DataType prereq in iface.get_prerequisites ()) {
2737 Symbol sym = null;
2738 if (prereq is UnresolvedType) {
2739 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
2740 sym = resolve_symbol (iface.parent_symbol.scope, unresolved_symbol);
2741 } else {
2742 sym = prereq.data_type;
2744 if (sym is Class) {
2745 has_instantiable_prereq = true;
2746 break;
2750 if (!has_instantiable_prereq) {
2751 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
2756 void postprocess_reparenting () {
2757 foreach (UnresolvedSymbol target_unresolved_symbol in symbol_reparent_map.get_keys ()) {
2758 var target_symbol = resolve_symbol (context.root.scope, target_unresolved_symbol);
2759 if (target_symbol == null) {
2760 // create namespaces backward
2761 var sym = target_unresolved_symbol;
2762 var ns = new Namespace (sym.name, sym.source_reference);
2763 var result = ns;
2764 sym = sym.inner;
2765 while (sym != null) {
2766 var res = resolve_symbol (context.root.scope, sym);
2767 if (res != null && !(res is Namespace)) {
2768 result = null;
2769 break;
2771 var parent = res as Namespace;
2772 if (res == null) {
2773 parent = new Namespace (sym.name, sym.source_reference);
2775 if (parent.scope.lookup (ns.name) == null) {
2776 parent.add_namespace (ns);
2778 ns = parent;
2779 sym = sym.inner;
2781 if (result != null && sym == null && context.root.scope.lookup (ns.name) == null) {
2782 // a new root namespace, helpful for a possible non-gobject gir?
2783 context.root.add_namespace (ns);
2785 target_symbol = result;
2787 if (target_symbol == null) {
2788 Report.error (null, "unable to reparent into `%s'".printf (target_unresolved_symbol.to_string ()));
2789 continue;
2791 var symbols = symbol_reparent_map[target_unresolved_symbol];
2792 foreach (var symbol in symbols) {
2793 add_symbol_to_container (target_symbol, symbol);
2798 void postprocess_gtype_callbacks (Namespace ns) {
2799 foreach (UnresolvedSymbol gtype_struct_for in gtype_callbacks.get_keys ()) {
2800 // parent symbol is the record, therefore use parent of parent symbol
2801 var gtype = resolve_symbol (ns.scope, gtype_struct_for) as ObjectTypeSymbol;
2802 if (gtype == null) {
2803 Report.error (null, "unknown symbol `%s' while postprocessing callbacks".printf (gtype_struct_for.name));
2804 continue;
2806 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2807 foreach (Delegate d in callbacks) {
2808 var symbol = gtype.scope.lookup (d.name);
2809 if (symbol == null) {
2810 continue;
2811 } else if (symbol is Method) {
2812 var meth = (Method) symbol;
2813 if (gtype is Class) {
2814 meth.is_virtual = true;
2815 } else if (gtype is Interface) {
2816 meth.is_abstract = true;
2818 } else if (symbol is Signal) {
2819 var sig = (Signal) symbol;
2820 sig.is_virtual = true;
2821 assume_parameter_names (sig, d, true);
2822 } else if (symbol is Property) {
2823 var prop = (Property) symbol;
2824 prop.is_virtual = true;
2825 } else {
2826 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (d.name, gtype.name));
2832 void postprocess_aliases () {
2833 /* this is unfortunate because <alias> tag has no type information, thus we have
2834 to guess it from the base type */
2835 foreach (var alias in aliases) {
2836 DataType base_type = null;
2837 Symbol type_sym = null;
2838 bool simple_type = false;
2839 if (alias.base_type is UnresolvedType) {
2840 base_type = alias.base_type;
2841 type_sym = resolve_symbol (alias.parent_symbol.scope, ((UnresolvedType) base_type).unresolved_symbol);
2842 } else if (alias.base_type is PointerType && ((PointerType) alias.base_type).base_type is VoidType) {
2843 // gpointer, if it's a struct make it a simpletype
2844 simple_type = true;
2845 } else {
2846 base_type = alias.base_type;
2847 type_sym = base_type.data_type;
2850 if (type_sym is Struct && ((Struct) type_sym).is_simple_type ()) {
2851 simple_type = true;
2854 if (base_type == null || type_sym == null || type_sym is Struct) {
2855 var st = new Struct (alias.name, alias.source_reference);
2856 st.access = SymbolAccessibility.PUBLIC;
2857 if (base_type != null) {
2858 // threat target="none" as a new struct
2859 st.base_type = base_type;
2861 st.external = true;
2862 if (alias.cname != null) {
2863 st.set_cname (alias.cname);
2865 if (simple_type) {
2866 st.set_simple_type (true);
2868 add_symbol_to_container (alias.parent_symbol, st);
2869 } else if (type_sym is Class) {
2870 var cl = new Class (alias.name, alias.source_reference);
2871 cl.access = SymbolAccessibility.PUBLIC;
2872 if (base_type != null) {
2873 cl.add_base_type (base_type);
2875 cl.external = true;
2876 if (alias.cname != null) {
2877 cl.set_cname (alias.cname);
2879 add_symbol_to_container (alias.parent_symbol, cl);
2884 void find_static_method_parent (string cname, Symbol current, ref Symbol best, ref double match, double match_char) {
2885 var old_best = best;
2886 if (current.scope.get_symbol_table () != null) {
2887 foreach (var child in current.scope.get_symbol_table().get_values ()) {
2888 if (child is Struct || child is ObjectTypeSymbol || child is Namespace) {
2889 find_static_method_parent (cname, child, ref best, ref match, match_char);
2893 if (best != old_best) {
2894 // child is better
2895 return;
2898 var current_cprefix = current.get_lower_case_cprefix ();
2899 if (cname.has_prefix (current_cprefix)) {
2900 var current_match = match_char * current_cprefix.length;
2901 if (current_match > match) {
2902 match = current_match;
2903 best = current;
2908 void postprocess_namespace_methods () {
2909 /* transform static methods into instance methods if possible.
2910 In most of cases this is a .gir fault we are going to fix */
2911 foreach (var ns in namespace_methods.get_keys ()) {
2912 var ns_cprefix = ns.get_lower_case_cprefix ();
2913 var methods = namespace_methods[ns];
2914 foreach (var method in methods) {
2915 if (method.parent_symbol != null) {
2916 // fixed earlier by metadata
2917 continue;
2920 var cname = method.get_cname ();
2922 Parameter first_param = null;
2923 if (method.get_parameters ().size > 0) {
2924 first_param = method.get_parameters()[0];
2926 if (first_param != null && first_param.variable_type is UnresolvedType) {
2927 // check if it's a missed instance method (often happens for structs)
2928 var parent = resolve_symbol (ns.scope, ((UnresolvedType) first_param.variable_type).unresolved_symbol);
2929 if (parent != null && (parent is Struct || parent is ObjectTypeSymbol || parent is Namespace)
2930 && cname.has_prefix (parent.get_lower_case_cprefix ())) {
2931 // instance method
2932 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2933 if (parent.scope.lookup (new_name) == null) {
2934 method.name = new_name;
2935 method.get_parameters().remove_at (0);
2936 method.binding = MemberBinding.INSTANCE;
2937 add_symbol_to_container (parent, method);
2938 } else {
2939 ns.add_method (method);
2941 continue;
2945 double match = 0;
2946 Symbol parent = ns;
2947 find_static_method_parent (cname, ns, ref parent, ref match, 1.0/cname.length);
2948 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2949 if (parent.scope.lookup (new_name) == null) {
2950 method.name = new_name;
2951 add_symbol_to_container (parent, method);
2952 } else {
2953 ns.add_method (method);
2959 /* Hash and equal functions */
2961 static uint unresolved_symbol_hash (void *ptr) {
2962 var sym = (UnresolvedSymbol) ptr;
2963 var builder = new StringBuilder ();
2964 while (sym != null) {
2965 builder.append (sym.name);
2966 sym = sym.inner;
2968 return builder.str.hash ();
2971 static bool unresolved_symbol_equal (void *ptr1, void *ptr2) {
2972 var sym1 = (UnresolvedSymbol) ptr1;
2973 var sym2 = (UnresolvedSymbol) ptr2;
2974 while (sym1 != sym2) {
2975 if (sym1 == null || sym2 == null) {
2976 return false;
2978 if (sym1.name != sym2.name) {
2979 return false;
2981 sym1 = sym1.inner;
2982 sym2 = sym2.inner;
2984 return true;