girparser: Fix parsing arrays in metadata types
[vala-lang.git] / vala / valagirparser.vala
blob31cc0216d776dcceb8de5104cb4ac6195c67bd63
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_POS,
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 lit = get_expression (arg) as IntegerLiteral;
204 if (lit != null) {
205 return int.parse (lit.value);
208 return 0;
211 public bool get_bool (ArgumentType arg) {
212 var lit = get_expression (arg) as BooleanLiteral;
213 if (lit != null) {
214 return lit.value;
216 return false;
219 public SourceReference? get_source_reference (ArgumentType arg) {
220 var val = args.get (arg);
221 if (val != null) {
222 return val.source_reference;
224 return null;
228 class MetadataParser {
230 * Grammar:
231 * metadata ::= [ rule [ '\n' relativerule ]* ]
232 * rule ::= pattern ' ' [ args ]
233 * relativerule ::= [ access ] rule
234 * pattern ::= identifier [ access identifier ]*
235 * access ::= '.' | ':' | '::'
237 private Metadata tree = new Metadata ("");
238 private Scanner scanner;
239 private SourceLocation begin;
240 private SourceLocation end;
241 private SourceLocation old_end;
242 private TokenType current;
243 private Metadata parent_metadata;
245 public MetadataParser () {
246 tree.used = true;
249 SourceReference get_current_src () {
250 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
253 SourceReference get_src (SourceLocation begin) {
254 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
257 public Metadata parse_metadata (SourceFile metadata_file) {
258 scanner = new Scanner (metadata_file);
259 next ();
260 while (current != TokenType.EOF) {
261 if (!parse_rule ()) {
262 return Metadata.empty;
265 return tree;
268 TokenType next () {
269 old_end = end;
270 current = scanner.read_token (out begin, out end);
271 return current;
274 bool has_space () {
275 return old_end.pos != begin.pos;
278 bool has_newline () {
279 return old_end.line != begin.line;
282 string get_string () {
283 return ((string) begin.pos).substring (0, (int) (end.pos - begin.pos));
286 MetadataType? parse_metadata_access () {
287 switch (current) {
288 case TokenType.DOT:
289 next ();
290 return MetadataType.GENERIC;
291 case TokenType.COLON:
292 next ();
293 return MetadataType.PROPERTY;
294 case TokenType.DOUBLE_COLON:
295 next ();
296 return MetadataType.SIGNAL;
297 default:
298 return null;
302 string? parse_identifier (out SourceReference source_reference, bool is_glob) {
303 var begin = this.begin;
304 var builder = new StringBuilder ();
305 do {
306 if (is_glob && current == TokenType.STAR) {
307 builder.append_c ('*');
308 } else {
309 string str = null;
310 switch (current) {
311 case TokenType.IDENTIFIER:
312 case TokenType.UNOWNED:
313 case TokenType.OWNED:
314 case TokenType.GET:
315 case TokenType.NEW:
316 case TokenType.DEFAULT:
317 case TokenType.OUT:
318 case TokenType.REF:
319 case TokenType.VIRTUAL:
320 case TokenType.ABSTRACT:
321 str = get_string ();
322 break;
324 if (str == null) {
325 break;
327 builder.append (str);
329 source_reference = get_src (begin);
330 next ();
331 } while (!has_space ());
333 if (builder.str == "") {
334 if (is_glob) {
335 Report.error (get_src (begin), "expected pattern");
336 } else {
337 Report.error (get_src (begin), "expected identifier");
339 return null;
341 return builder.str;
344 Metadata? parse_pattern () {
345 Metadata metadata;
346 bool is_relative = false;
347 MetadataType? type = MetadataType.GENERIC;
348 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
349 // absolute pattern
350 parent_metadata = tree;
351 } else {
352 // relative pattern
353 type = parse_metadata_access ();
354 is_relative = true;
357 if (type == null) {
358 Report.error (get_current_src (), "expected pattern, `.', `:' or `::'");
359 return null;
362 if (parent_metadata == null) {
363 Report.error (get_current_src (), "cannot determinate parent metadata");
364 return null;
367 SourceReference src;
368 var pattern = parse_identifier (out src, true);
369 if (pattern == null) {
370 return null;
372 metadata = parent_metadata.get_child (pattern, type);
373 if (metadata == null) {
374 metadata = new Metadata (pattern, type, src);
375 parent_metadata.add_child (metadata);
378 while (current != TokenType.EOF && !has_space ()) {
379 type = parse_metadata_access ();
380 if (type == null) {
381 Report.error (get_current_src (), "expected `.', `:' or `::'");
382 return null;
385 pattern = parse_identifier (out src, true);
386 if (pattern == null) {
387 return null;
389 var child = metadata.get_child (pattern, type);
390 if (child == null) {
391 child = new Metadata (pattern, type, src);
392 metadata.add_child (child);
394 metadata = child;
396 if (!is_relative) {
397 parent_metadata = metadata;
400 return metadata;
403 Expression? parse_expression () {
404 var src = get_current_src ();
405 Expression expr = null;
406 switch (current) {
407 case TokenType.NULL:
408 expr = new NullLiteral (src);
409 break;
410 case TokenType.TRUE:
411 expr = new BooleanLiteral (true, src);
412 break;
413 case TokenType.FALSE:
414 expr = new BooleanLiteral (false, src);
415 break;
416 case TokenType.INTEGER_LITERAL:
417 expr = new IntegerLiteral (get_string (), src);
418 break;
419 case TokenType.REAL_LITERAL:
420 expr = new RealLiteral (get_string (), src);
421 break;
422 case TokenType.STRING_LITERAL:
423 expr = new StringLiteral (get_string (), src);
424 break;
425 case TokenType.IDENTIFIER:
426 expr = new MemberAccess (null, get_string (), src);
427 while (next () == TokenType.DOT) {
428 if (next () != TokenType.IDENTIFIER) {
429 Report.error (get_current_src (), "expected identifier got `%s'".printf (current.to_string ()));
430 break;
432 expr = new MemberAccess (expr, get_string (), get_current_src ());
434 return expr;
435 default:
436 Report.error (src, "expected literal or symbol got `%s'".printf (current.to_string ()));
437 break;
439 next ();
440 return expr;
443 bool parse_args (Metadata metadata) {
444 while (current != TokenType.EOF && has_space () && !has_newline ()) {
445 SourceReference src;
446 var id = parse_identifier (out src, false);
447 if (id == null) {
448 return false;
450 var arg_type = ArgumentType.from_string (id);
451 if (arg_type == null) {
452 Report.error (src, "unknown argument");
453 return false;
456 if (current != TokenType.ASSIGN) {
457 // threat as `true'
458 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, src), src));
459 continue;
461 next ();
463 Expression expr = parse_expression ();
464 if (expr == null) {
465 return false;
467 metadata.add_argument (arg_type, new Argument (expr, src));
470 return true;
473 bool parse_rule () {
474 var old_end = end;
475 var metadata = parse_pattern ();
476 if (metadata == null) {
477 return false;
480 if (current == TokenType.EOF || old_end.line != end.line) {
481 // eof or new rule
482 return true;
484 return parse_args (metadata);
488 class SymbolInfo {
489 public Symbol symbol;
490 public Metadata metadata;
491 // additional information from GIR
492 public HashMap<string,string> girdata;
495 class Alias {
496 public string name;
497 public DataType base_type;
498 public Symbol parent_symbol;
499 public SourceReference source_reference;
502 static GLib.Regex type_from_string_regex;
504 MarkupReader reader;
506 CodeContext context;
507 Namespace glib_ns;
509 SourceFile current_source_file;
510 Symbol current_symbol;
512 string current_gtype_struct_for;
513 SourceLocation begin;
514 SourceLocation end;
515 MarkupTokenType current_token;
517 string[] cheader_filenames;
519 ArrayList<Metadata> metadata_stack;
520 Metadata metadata;
521 ArrayList<HashMap<string,string>> girdata_stack;
522 HashMap<string,string> girdata;
524 HashMap<string,ArrayList<SymbolInfo>> current_symbols_info;
526 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
527 HashMap<Symbol,Symbol> concrete_symbols_map = new HashMap<Symbol,Symbol> ();
529 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
530 HashMap<UnresolvedSymbol,ArrayList<Symbol>> symbol_reparent_map = new HashMap<UnresolvedSymbol,ArrayList<Symbol>> (unresolved_symbol_hash, unresolved_symbol_equal);
531 HashMap<Namespace,ArrayList<Method>> namespace_methods = new HashMap<Namespace,ArrayList<Method>> ();
532 ArrayList<Alias> aliases = new ArrayList<Alias> ();
533 ArrayList<Interface> interfaces = new ArrayList<Interface> ();
535 HashMap<UnresolvedSymbol,ArrayList<Delegate>> gtype_callbacks;
538 * Parses all .gir source files in the specified code
539 * context and builds a code tree.
541 * @param context a code context
543 public void parse (CodeContext context) {
544 this.context = context;
545 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
546 context.accept (this);
548 resolve_gir_symbols ();
550 postprocess_interfaces ();
551 postprocess_reparenting ();
552 postprocess_aliases ();
553 postprocess_namespace_methods ();
556 public override void visit_source_file (SourceFile source_file) {
557 // collect gir namespaces
558 foreach (var node in source_file.get_nodes ()) {
559 if (node is Namespace) {
560 var ns = (Namespace) node;
561 var gir_namespace = source_file.gir_namespace;
562 if (gir_namespace == null) {
563 var a = ns.get_attribute ("CCode");
564 if (a != null && a.has_argument ("gir_namespace")) {
565 gir_namespace = a.get_string ("gir_namespace");
568 if (gir_namespace != null && gir_namespace != ns.name) {
569 var map_from = new UnresolvedSymbol (null, gir_namespace);
570 set_symbol_mapping (map_from, ns);
571 break;
576 if (source_file.filename.has_suffix (".gir")) {
577 parse_file (source_file);
581 public void parse_file (SourceFile source_file) {
582 metadata_stack = new ArrayList<Metadata> ();
583 metadata = Metadata.empty;
584 girdata_stack = new ArrayList<HashMap<string,string>> ();
586 // load metadata, first look into metadata directories then in the same directory of the .gir.
587 string? metadata_filename = context.get_metadata_path (source_file.filename);
588 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
589 var metadata_parser = new MetadataParser ();
590 var metadata_file = new SourceFile (context, source_file.file_type, metadata_filename);
591 context.add_source_file (metadata_file);
592 metadata = metadata_parser.parse_metadata (metadata_file);
595 this.current_source_file = source_file;
596 reader = new MarkupReader (source_file.filename);
598 // xml prolog
599 next ();
600 next ();
602 next ();
603 parse_repository ();
605 report_unused_metadata (metadata);
607 reader = null;
608 this.current_source_file = null;
611 void next () {
612 current_token = reader.read_token (out begin, out end);
614 // Skip *all* <doc> tags
615 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
616 skip_element();
619 void start_element (string name) {
620 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
621 // error
622 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
626 void end_element (string name) {
627 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
628 // error
629 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
631 next ();
634 SourceReference get_current_src () {
635 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
638 const string GIR_VERSION = "1.2";
640 void add_symbol_to_container (Symbol container, Symbol sym) {
641 var name = sym.name;
642 if (name == null && sym is CreationMethod) {
643 name = ".new";
645 if (container.scope.lookup (name) != null) {
646 // overridden by -custom.vala
647 return;
650 if (container is Class) {
651 unowned Class cl = (Class) container;
653 if (sym is Class) {
654 cl.add_class ((Class) sym);
655 } else if (sym is Constant) {
656 cl.add_constant ((Constant) sym);
657 } else if (sym is Enum) {
658 cl.add_enum ((Enum) sym);
659 } else if (sym is Field) {
660 cl.add_field ((Field) sym);
661 } else if (sym is Method) {
662 cl.add_method ((Method) sym);
663 } else if (sym is Property) {
664 cl.add_property ((Property) sym);
665 } else if (sym is Signal) {
666 cl.add_signal ((Signal) sym);
667 } else if (sym is Struct) {
668 cl.add_struct ((Struct) sym);
670 } else if (container is Enum) {
671 unowned Enum en = (Enum) container;
673 if (sym is EnumValue) {
674 en.add_value ((EnumValue) sym);
675 } else if (sym is Constant) {
676 en.add_constant ((Constant) sym);
677 } else if (sym is Method) {
678 en.add_method ((Method) sym);
680 } else if (container is Interface) {
681 unowned Interface iface = (Interface) container;
683 if (sym is Class) {
684 iface.add_class ((Class) sym);
685 } else if (sym is Constant) {
686 iface.add_constant ((Constant) sym);
687 } else if (sym is Enum) {
688 iface.add_enum ((Enum) sym);
689 } else if (sym is Field) {
690 iface.add_field ((Field) sym);
691 } else if (sym is Method) {
692 iface.add_method ((Method) sym);
693 } else if (sym is Property) {
694 iface.add_property ((Property) sym);
695 } else if (sym is Signal) {
696 iface.add_signal ((Signal) sym);
697 } else if (sym is Struct) {
698 iface.add_struct ((Struct) sym);
700 } else if (container is Namespace) {
701 unowned Namespace ns = (Namespace) container;
703 if (sym is Namespace) {
704 ns.add_namespace ((Namespace) sym);
705 } else if (sym is Class) {
706 ns.add_class ((Class) sym);
707 } else if (sym is Constant) {
708 ns.add_constant ((Constant) sym);
709 } else if (sym is Delegate) {
710 ns.add_delegate ((Delegate) sym);
711 } else if (sym is Enum) {
712 ns.add_enum ((Enum) sym);
713 } else if (sym is ErrorDomain) {
714 ns.add_error_domain ((ErrorDomain) sym);
715 } else if (sym is Field) {
716 ns.add_field ((Field) sym);
717 } else if (sym is Interface) {
718 ns.add_interface ((Interface) sym);
719 } else if (sym is Method) {
720 ns.add_method ((Method) sym);
721 } else if (sym is Namespace) {
722 ns.add_namespace ((Namespace) sym);
723 } else if (sym is Struct) {
724 ns.add_struct ((Struct) sym);
726 } else if (container is Struct) {
727 unowned Struct st = (Struct) container;
729 if (sym is Constant) {
730 st.add_constant ((Constant) sym);
731 } else if (sym is Field) {
732 st.add_field ((Field) sym);
733 } else if (sym is Method) {
734 st.add_method ((Method) sym);
735 } else if (sym is Property) {
736 st.add_property ((Property) sym);
738 } else {
739 Report.error (sym.source_reference, "impossible to add to container `%s'".printf (container.name));
743 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
744 UnresolvedSymbol? sym = null;
745 foreach (unowned string s in symbol_string.split (".")) {
746 sym = new UnresolvedSymbol (sym, s, source_reference);
748 if (sym == null) {
749 Report.error (source_reference, "a symbol must be specified");
751 return sym;
754 UnresolvedSymbol get_unresolved_symbol (Symbol symbol) {
755 if (symbol is UnresolvedSymbol) {
756 return (UnresolvedSymbol) symbol;
758 var sym = new UnresolvedSymbol (null, symbol.name);
759 var result = sym;
760 var cur = symbol.parent_node as Symbol;
761 while (cur != null && cur.name != null) {
762 sym = new UnresolvedSymbol (sym, cur.name);
763 cur = cur.parent_node as Symbol;
765 return result;
768 void set_symbol_mapping (Symbol map_from, Symbol map_to) {
769 // last mapping is the most up-to-date
770 if (map_from is UnresolvedSymbol) {
771 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
772 } else {
773 concrete_symbols_map[map_from] = map_to;
777 void assume_parameter_names (Signal sig, Symbol sym) {
778 Iterator<Parameter> iter;
779 if (sym is Method) {
780 iter = ((Method) sym).get_parameters ().iterator ();
781 } else {
782 iter = ((Delegate) sym).get_parameters ().iterator ();
784 foreach (var param in sig.get_parameters ()) {
785 if (!iter.next ()) {
786 // unreachable for valid GIR
787 break;
789 param.name = iter.get ().name;
793 SymbolInfo? add_symbol_info (Symbol symbol) {
794 var name = symbol.name;
795 if (symbol is CreationMethod && name == null) {
796 name = ".new";
799 var info = new SymbolInfo ();
800 info.symbol = symbol;
801 info.metadata = metadata;
802 info.girdata = girdata;
803 var colliding = current_symbols_info[name];
804 if (colliding == null) {
805 colliding = new ArrayList<SymbolInfo> ();
806 current_symbols_info[name] = colliding;
808 colliding.add (info);
809 return info;
812 SymbolInfo? find_invoker (Method method) {
813 /* most common use case is invoker has at least the given method prefix
814 and the same parameter names */
815 var prefix = "%s_".printf (method.name);
816 foreach (var name in current_symbols_info.get_keys ()) {
817 if (!name.has_prefix (prefix)) {
818 continue;
820 var infos = current_symbols_info[name];
821 foreach (var cinfo in infos) {
822 Method? invoker = cinfo.symbol as Method;
823 if (invoker == null || (method.get_parameters ().size != invoker.get_parameters ().size)) {
824 continue;
826 var iter = invoker.get_parameters ().iterator ();
827 foreach (var param in method.get_parameters ()) {
828 assert (iter.next ());
829 if (param.name != iter.get ().name) {
830 invoker = null;
831 break;
834 if (invoker != null) {
835 return cinfo;
840 return null;
843 void merge (SymbolInfo info, ArrayList<SymbolInfo> colliding, ArrayList<SymbolInfo> merged) {
844 if (info.symbol is Struct) {
845 var gtype_struct_for = info.girdata["glib:is-gtype-struct-for"];
846 if (gtype_struct_for != null && current_symbols_info.contains (gtype_struct_for)) {
847 var iface = current_symbols_info.get (gtype_struct_for).get (0).symbol as Interface;
848 if (iface != null) {
849 // set the interface struct name
850 iface.set_type_cname (((Struct) info.symbol).get_cname ());
852 merged.add (info);
854 } else if (info.symbol is Property) {
855 foreach (var cinfo in colliding) {
856 var sym = cinfo.symbol;
857 if (sym is Signal || sym is Field) {
858 // properties take precedence
859 merged.add (cinfo);
860 } else if (sym is Method) {
861 // assume method is getter
862 merged.add (cinfo);
865 var getter_name = "get_%s".printf (info.symbol.name);
866 var setter_name = "set_%s".printf (info.symbol.name);
867 if (current_symbols_info.contains (getter_name) || current_symbols_info.contains (setter_name)) {
868 ((Property) info.symbol).no_accessor_method = false;
870 } else if (info.symbol is Signal) {
871 var sig = (Signal) info.symbol;
872 foreach (var cinfo in colliding) {
873 var sym = cinfo.symbol;
874 if (sym is Method) {
875 var method = (Method) sym;
876 if (method.is_virtual) {
877 sig.is_virtual = true;
878 } else {
879 sig.has_emitter = true;
881 assume_parameter_names (sig, method);
882 merged.add (cinfo);
883 } else if (sym is Field) {
884 merged.add (cinfo);
887 } else if (info.symbol is Method && !(info.symbol is CreationMethod)) {
888 var method = (Method) info.symbol;
889 foreach (var cinfo in colliding) {
890 var sym = cinfo.symbol;
891 if (sym != method && method.is_virtual && sym is Method) {
892 bool different_invoker = false;
893 foreach (var attr in method.attributes) {
894 if (attr.name == "NoWrapper") {
895 /* no invoker but this method has the same name,
896 most probably the invoker has a different name
897 and g-ir-scanner missed it */
898 var invoker = find_invoker (method);
899 if (invoker != null) {
900 method.vfunc_name = method.name;
901 method.name = invoker.symbol.name;
902 method.attributes.remove (attr);
903 merged.add (invoker);
904 different_invoker = true;
905 break;
909 if (!different_invoker) {
910 merged.add (cinfo);
914 } else if (info.symbol is Field) {
915 foreach (var cinfo in colliding) {
916 var sym = cinfo.symbol;
917 if (sym is Method) {
918 // assume method is getter
919 merged.add (cinfo);
923 var field = (Field) info.symbol;
924 if (field.variable_type is ArrayType) {
925 SymbolInfo array_length = null;
926 if (current_symbols_info.contains ("n_%s".printf (field.name))) {
927 array_length = current_symbols_info.get ("n_%s".printf (field.name)).get (0);
928 } else if (current_symbols_info.contains ("%s_length".printf (field.name))) {
929 array_length = current_symbols_info.get ("%s_length".printf (field.name)).get (0);
931 if (array_length != null) {
932 // array has length
933 field.set_array_length_cname (array_length.symbol.name);
934 field.no_array_length = false;
935 merged.add (array_length);
941 void postprocess_symbol (Symbol symbol, Metadata metadata) {
942 // deprecation
943 symbol.replacement = metadata.get_string (ArgumentType.REPLACEMENT);
944 symbol.deprecated_since = element_get_string ("deprecated-version", ArgumentType.DEPRECATED_SINCE);
945 symbol.deprecated = metadata.get_bool (ArgumentType.DEPRECATED) || symbol.replacement != null || symbol.deprecated_since != null;
947 // mark to be reparented
948 if (metadata.has_argument (ArgumentType.PARENT)) {
949 var target_symbol = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
950 var reparent_list = symbol_reparent_map[target_symbol];
951 if (reparent_list == null) {
952 reparent_list = new ArrayList<Symbol>();
953 symbol_reparent_map[target_symbol] = reparent_list;
955 reparent_list.add (symbol);
957 // if referenceable, map unresolved references to point to the new place
958 if (symbol is Namespace || symbol is TypeSymbol) {
959 set_symbol_mapping (symbol, new UnresolvedSymbol (target_symbol, symbol.name));
964 void merge_add_process (Symbol container) {
965 var merged = new ArrayList<SymbolInfo> ();
966 foreach (var name in current_symbols_info.get_keys ()) {
967 var colliding = current_symbols_info[name];
968 foreach (var info in colliding) {
969 merge (info, colliding, merged);
973 foreach (var infos in current_symbols_info.get_values ()) {
974 foreach (var info in infos) {
975 if (merged.contains (info) || info.metadata.get_bool (ArgumentType.HIDDEN)) {
976 continue;
978 if (!(current_symbol is Namespace && info.symbol is Method) && !info.metadata.has_argument (ArgumentType.PARENT)) {
979 add_symbol_to_container (container, info.symbol);
981 postprocess_symbol (info.symbol, info.metadata);
986 Metadata get_current_metadata () {
987 var name = reader.name;
988 var child_name = reader.get_attribute ("name");
989 if (child_name == null) {
990 return Metadata.empty;
993 var type = MetadataType.GENERIC;
994 if (name == "glib:signal") {
995 child_name = child_name.replace ("-", "_");
996 type = MetadataType.SIGNAL;
997 } else if (name == "property") {
998 type = MetadataType.PROPERTY;
1001 return metadata.match_child (child_name, type);
1004 bool push_metadata () {
1005 // skip?
1006 if (reader.get_attribute ("introspectable") == "0") {
1007 return false;
1009 var new_metadata = get_current_metadata ();
1010 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1011 return false;
1014 metadata_stack.add (metadata);
1015 metadata = new_metadata;
1016 girdata_stack.add (girdata);
1017 girdata = new HashMap<string,string> (str_hash, str_equal);
1019 return true;
1022 void pop_metadata () {
1023 metadata = metadata_stack[metadata_stack.size - 1];
1024 metadata_stack.remove_at (metadata_stack.size - 1);
1025 girdata = girdata_stack[girdata_stack.size - 1];
1026 girdata_stack.remove_at (girdata_stack.size - 1);
1029 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1030 int type_arguments_length = (int) type_arguments.length;
1031 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1033 int depth = 0;
1034 for (var c = 0 ; c < type_arguments_length ; c++) {
1035 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1036 depth++;
1037 current.append_unichar (type_arguments[c]);
1038 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1039 depth--;
1040 current.append_unichar (type_arguments[c]);
1041 } else if (type_arguments[c] == ',') {
1042 if (depth == 0) {
1043 var dt = parse_type_from_string (current.str, true, source_reference);
1044 if (dt == null) {
1045 return false;
1047 parent_type.add_type_argument (dt);
1048 current.truncate ();
1049 } else {
1050 current.append_unichar (type_arguments[c]);
1052 } else {
1053 current.append_unichar (type_arguments[c]);
1057 var dt = parse_type_from_string (current.str, true, source_reference);
1058 if (dt == null) {
1059 return false;
1061 parent_type.add_type_argument (dt);
1063 return true;
1066 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1067 if (type_from_string_regex == null) {
1068 try {
1069 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[,*\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1070 } catch (GLib.RegexError e) {
1071 GLib.error ("Unable to compile regex: %s", e.message);
1075 GLib.MatchInfo match;
1076 if (!type_from_string_regex.match (type_string, 0, out match)) {
1077 Report.error (source_reference, "unable to parse type");
1078 return null;
1081 DataType? type = null;
1083 var ownership_data = match.fetch (1);
1084 var type_name = match.fetch (2);
1085 var type_arguments_data = match.fetch (3);
1086 var pointers_data = match.fetch (4);
1087 var array_data = match.fetch (5);
1088 var nullable_data = match.fetch (6);
1090 var nullable = nullable_data != null && nullable_data.length > 0;
1092 if (ownership_data == null && type_name == "void") {
1093 if (array_data == null && !nullable) {
1094 type = new VoidType (source_reference);
1095 if (pointers_data != null) {
1096 for (int i=0; i < pointers_data.length; i++) {
1097 type = new PointerType (type);
1100 return type;
1101 } else {
1102 Report.error (source_reference, "invalid void type");
1103 return null;
1107 bool value_owned = owned_by_default;
1109 if (ownership_data == "owned") {
1110 if (owned_by_default) {
1111 Report.error (source_reference, "unexpected `owned' keyword");
1112 } else {
1113 value_owned = true;
1115 } else if (ownership_data == "unowned") {
1116 if (owned_by_default) {
1117 value_owned = true;
1118 } else {
1119 Report.error (source_reference, "unexpected `unowned' keyword");
1120 return null;
1124 var sym = parse_symbol_from_string (type_name, source_reference);
1125 if (sym == null) {
1126 return null;
1128 type = new UnresolvedType.from_symbol (sym, source_reference);
1130 if (type_arguments_data != null && type_arguments_data.length > 0) {
1131 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1132 return null;
1136 if (pointers_data != null) {
1137 for (int i=0; i < pointers_data.length; i++) {
1138 type = new PointerType (type);
1142 if (array_data != null) {
1143 type = new ArrayType (type, (int) array_data.length - 1, source_reference);
1146 type.nullable = nullable;
1147 type.value_owned = value_owned;
1148 return type;
1151 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1152 var str = metadata.get_string (arg_type);
1153 if (str == null) {
1154 str = reader.get_attribute (attribute_name);
1156 return str;
1160 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1161 * If type arguments change, the C declaration is not affected.
1163 DataType? element_get_type (DataType orig_type, bool owned_by_default, out bool changed = null) {
1164 changed = false;
1165 var type = orig_type;
1167 if (metadata.has_argument (ArgumentType.TYPE)) {
1168 var new_type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1169 changed = true;
1170 return new_type;
1173 if (type is VoidType) {
1174 return type;
1177 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1178 type.remove_all_type_arguments ();
1179 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1182 if (metadata.get_bool (ArgumentType.ARRAY)) {
1183 type = new ArrayType (type, 1, type.source_reference);
1184 changed = true;
1187 if (owned_by_default) {
1188 if (metadata.has_argument (ArgumentType.UNOWNED)) {
1189 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED);
1191 } else {
1192 if (metadata.has_argument (ArgumentType.OWNED)) {
1193 type.value_owned = metadata.get_bool (ArgumentType.OWNED);
1196 if (metadata.has_argument (ArgumentType.NULLABLE)) {
1197 type.nullable = metadata.get_bool (ArgumentType.NULLABLE);
1200 return type;
1203 string? element_get_name (bool remap = false) {
1204 var name = reader.get_attribute ("name");
1205 var orig_name = name;
1206 var pattern = metadata.get_string (ArgumentType.NAME);
1207 if (pattern != null) {
1208 try {
1209 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1210 GLib.MatchInfo match;
1211 if (!regex.match (name, 0, out match)) {
1212 name = pattern;
1213 } else {
1214 var matched = match.fetch (1);
1215 if (matched != null && matched.length > 0) {
1216 name = matched;
1217 } else {
1218 name = pattern;
1221 } catch (Error e) {
1222 name = pattern;
1224 } else {
1225 if (name != null && name.has_suffix ("Enum")) {
1226 name = name.substring (0, name.length - "Enum".length);
1229 if (name != orig_name && remap) {
1230 set_symbol_mapping (parse_symbol_from_string (orig_name), parse_symbol_from_string (name));
1233 return name;
1236 void parse_repository () {
1237 start_element ("repository");
1238 if (reader.get_attribute ("version") != GIR_VERSION) {
1239 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1240 return;
1242 next ();
1243 while (current_token == MarkupTokenType.START_ELEMENT) {
1244 if (reader.name == "namespace") {
1245 var ns = parse_namespace ();
1246 if (ns != null) {
1247 context.root.add_namespace (ns);
1249 } else if (reader.name == "include") {
1250 parse_include ();
1251 } else if (reader.name == "package") {
1252 var pkg = parse_package ();
1253 if (context.has_package (pkg)) {
1254 // package already provided elsewhere, stop parsing this GIR
1255 return;
1256 } else {
1257 context.add_package (pkg);
1259 } else if (reader.name == "c:include") {
1260 parse_c_include ();
1261 } else {
1262 // error
1263 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1264 skip_element ();
1267 end_element ("repository");
1270 void parse_include () {
1271 start_element ("include");
1272 var pkg = reader.get_attribute ("name");
1273 var version = reader.get_attribute ("version");
1274 if (version != null) {
1275 pkg = "%s-%s".printf (pkg, version);
1277 // add the package to the queue
1278 context.add_external_package (pkg);
1279 next ();
1280 end_element ("include");
1283 string parse_package () {
1284 start_element ("package");
1285 var pkg = reader.get_attribute ("name");
1286 next ();
1287 end_element ("package");
1288 return pkg;
1291 void parse_c_include () {
1292 start_element ("c:include");
1293 cheader_filenames += reader.get_attribute ("name");
1294 next ();
1295 end_element ("c:include");
1298 void skip_element () {
1299 next ();
1301 int level = 1;
1302 while (level > 0) {
1303 if (current_token == MarkupTokenType.START_ELEMENT) {
1304 level++;
1305 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1306 level--;
1307 } else if (current_token == MarkupTokenType.EOF) {
1308 Report.error (get_current_src (), "unexpected end of file");
1309 break;
1311 next ();
1315 Namespace? parse_namespace () {
1316 start_element ("namespace");
1318 bool new_namespace = false;
1319 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
1320 string namespace_name = cprefix;
1321 string gir_namespace = reader.get_attribute ("name");
1322 string gir_version = reader.get_attribute ("version");
1323 if (namespace_name == null) {
1324 namespace_name = gir_namespace;
1326 current_source_file.gir_namespace = gir_namespace;
1327 current_source_file.gir_version = gir_version;
1329 var ns_metadata = metadata.match_child (gir_namespace);
1330 if (ns_metadata.has_argument (ArgumentType.NAME)) {
1331 namespace_name = ns_metadata.get_string (ArgumentType.NAME);
1334 var ns = context.root.scope.lookup (namespace_name) as Namespace;
1335 if (ns == null) {
1336 ns = new Namespace (namespace_name, get_current_src ());
1337 new_namespace = true;
1338 } else {
1339 if (ns.external_package) {
1340 ns.attributes = null;
1341 ns.source_reference = get_current_src ();
1345 if (gir_namespace != ns.name) {
1346 set_symbol_mapping (new UnresolvedSymbol (null, gir_namespace), ns);
1349 if (cprefix != null) {
1350 ns.add_cprefix (cprefix);
1351 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
1354 if (ns_metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1355 var val = ns_metadata.get_string (ArgumentType.CHEADER_FILENAME);
1356 foreach (string filename in val.split (",")) {
1357 ns.add_cheader_filename (filename);
1359 } else {
1360 foreach (string c_header in cheader_filenames) {
1361 ns.add_cheader_filename (c_header);
1365 next ();
1366 var current_namespace_methods = namespace_methods[ns];
1367 if (current_namespace_methods == null) {
1368 current_namespace_methods = new ArrayList<Method> ();
1369 namespace_methods[ns] = current_namespace_methods;
1371 var old_symbols_info = current_symbols_info;
1372 var old_symbol = current_symbol;
1373 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1374 current_symbol = ns;
1375 gtype_callbacks = new HashMap<UnresolvedSymbol,ArrayList<Delegate>> (unresolved_symbol_hash, unresolved_symbol_equal);
1376 while (current_token == MarkupTokenType.START_ELEMENT) {
1377 if (!push_metadata ()) {
1378 skip_element ();
1379 continue;
1382 if (reader.name == "alias") {
1383 var alias = parse_alias ();
1384 aliases.add (alias);
1385 } else if (reader.name == "enumeration") {
1386 if (reader.get_attribute ("glib:error-quark") != null) {
1387 add_symbol_info (parse_error_domain ());
1388 } else {
1389 add_symbol_info (parse_enumeration ());
1391 } else if (reader.name == "bitfield") {
1392 add_symbol_info (parse_bitfield ());
1393 } else if (reader.name == "function") {
1394 var method = parse_method ("function");
1395 add_symbol_info (method);
1396 current_namespace_methods.add (method);
1397 } else if (reader.name == "callback") {
1398 add_symbol_info (parse_callback ());
1399 } else if (reader.name == "record") {
1400 if (reader.get_attribute ("glib:get-type") != null) {
1401 add_symbol_info (parse_boxed ("record"));
1402 } else {
1403 add_symbol_info (parse_record ());
1405 } else if (reader.name == "class") {
1406 add_symbol_info (parse_class ());
1407 } else if (reader.name == "interface") {
1408 var iface = parse_interface ();
1409 add_symbol_info (iface);
1410 interfaces.add (iface);
1411 } else if (reader.name == "glib:boxed") {
1412 add_symbol_info (parse_boxed ("glib:boxed"));
1413 } else if (reader.name == "union") {
1414 add_symbol_info (parse_union ());
1415 } else if (reader.name == "constant") {
1416 add_symbol_info (parse_constant ());
1417 } else {
1418 // error
1419 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
1420 skip_element ();
1423 pop_metadata ();
1425 end_element ("namespace");
1427 merge_add_process (ns);
1428 current_symbols_info = old_symbols_info;
1429 current_symbol = old_symbol;
1430 postprocess_gtype_callbacks (ns);
1432 if (!new_namespace) {
1433 ns = null;
1436 return ns;
1439 Alias parse_alias () {
1440 // alias has no type information
1441 start_element ("alias");
1442 var alias = new Alias ();
1443 alias.source_reference = get_current_src ();
1444 alias.name = reader.get_attribute ("name");
1445 alias.parent_symbol = current_symbol;
1446 next ();
1448 alias.base_type = element_get_type (parse_type (null, null, true), true);
1450 end_element ("alias");
1451 return alias;
1454 private void calculate_common_prefix (ref string common_prefix, string cname) {
1455 if (common_prefix == null) {
1456 common_prefix = cname;
1457 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
1458 // FIXME: could easily be made faster
1459 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1461 } else {
1462 while (!cname.has_prefix (common_prefix)) {
1463 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1466 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
1467 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
1468 // enum values may not consist solely of digits
1469 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1473 Enum parse_enumeration () {
1474 start_element ("enumeration");
1476 var en = new Enum (element_get_name (), get_current_src ());
1477 en.access = SymbolAccessibility.PUBLIC;
1479 string enum_cname = reader.get_attribute ("c:type");
1480 if (enum_cname != null) {
1481 en.set_cname (enum_cname);
1484 next ();
1486 string common_prefix = null;
1488 var old_symbol = current_symbol;
1489 current_symbol = en;
1490 while (current_token == MarkupTokenType.START_ELEMENT) {
1491 if (!push_metadata ()) {
1492 skip_element ();
1493 continue;
1496 if (reader.name == "member") {
1497 var ev = parse_enumeration_member ();
1498 en.add_value (ev);
1499 calculate_common_prefix (ref common_prefix, ev.get_cname ());
1500 } else {
1501 // error
1502 Report.error (get_current_src (), "unknown child element `%s' in `enumaration'".printf (reader.name));
1503 skip_element ();
1506 pop_metadata ();
1509 en.set_cprefix (common_prefix);
1511 end_element ("enumeration");
1512 current_symbol = old_symbol;
1513 return en;
1516 ErrorDomain parse_error_domain () {
1517 start_element ("enumeration");
1519 var ed = new ErrorDomain (element_get_name (true), get_current_src ());
1520 ed.access = SymbolAccessibility.PUBLIC;
1522 string enum_cname = reader.get_attribute ("c:type");
1523 if (enum_cname != null) {
1524 ed.set_cname (enum_cname);
1527 next ();
1529 string common_prefix = null;
1530 var old_symbol = current_symbol;
1531 current_symbol = ed;
1532 while (current_token == MarkupTokenType.START_ELEMENT) {
1533 if (!push_metadata ()) {
1534 skip_element ();
1535 continue;
1538 if (reader.name == "member") {
1539 ErrorCode ec = parse_error_member ();
1540 ed.add_code (ec);
1541 calculate_common_prefix (ref common_prefix, ec.get_cname ());
1542 } else {
1543 // error
1544 Report.error (get_current_src (), "unknown child element `%s' in `enumeration'".printf (reader.name));
1545 skip_element ();
1548 pop_metadata ();
1551 ed.set_cprefix (common_prefix);
1553 end_element ("enumeration");
1554 current_symbol = old_symbol;
1555 return ed;
1558 Enum parse_bitfield () {
1559 start_element ("bitfield");
1560 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1561 en.access = SymbolAccessibility.PUBLIC;
1562 next ();
1563 var old_symbol = current_symbol;
1564 current_symbol = en;
1565 while (current_token == MarkupTokenType.START_ELEMENT) {
1566 if (!push_metadata ()) {
1567 skip_element ();
1568 continue;
1571 if (reader.name == "member") {
1572 en.add_value (parse_enumeration_member ());
1573 } else {
1574 // error
1575 Report.error (get_current_src (), "unknown child element `%s' in `bitfield'".printf (reader.name));
1576 skip_element ();
1579 pop_metadata ();
1581 end_element ("bitfield");
1582 current_symbol = en;
1583 return en;
1586 EnumValue parse_enumeration_member () {
1587 start_element ("member");
1588 var ev = new EnumValue (reader.get_attribute ("name").up ().replace ("-", "_"), null, get_current_src ());
1589 ev.set_cname (reader.get_attribute ("c:identifier"));
1590 next ();
1591 end_element ("member");
1592 return ev;
1595 ErrorCode parse_error_member () {
1596 start_element ("member");
1598 ErrorCode ec;
1599 string name = reader.get_attribute ("name").up ().replace ("-", "_");
1600 string value = reader.get_attribute ("value");
1601 if (value != null) {
1602 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
1603 } else {
1604 ec = new ErrorCode (name);
1607 next ();
1608 end_element ("member");
1609 return ec;
1612 DataType parse_return_value (out string? ctype = null) {
1613 start_element ("return-value");
1614 string transfer = reader.get_attribute ("transfer-ownership");
1615 string allow_none = reader.get_attribute ("allow-none");
1616 next ();
1617 var transfer_elements = transfer == "full";
1618 var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
1619 if (transfer == "full" || transfer == "container") {
1620 type.value_owned = true;
1622 if (allow_none == "1") {
1623 type.nullable = true;
1625 end_element ("return-value");
1626 return type;
1629 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) {
1630 Parameter param;
1632 if (&array_length_idx != null) {
1633 array_length_idx = -1;
1635 if (&closure_idx != null) {
1636 closure_idx = -1;
1638 if (&destroy_idx != null) {
1639 destroy_idx = -1;
1642 start_element ("parameter");
1643 string name = reader.get_attribute ("name");
1644 if (name == null) {
1645 name = default_name;
1647 string direction = null;
1648 if (metadata.has_argument (ArgumentType.OUT)) {
1649 if (metadata.get_bool (ArgumentType.OUT)) {
1650 direction = "out";
1651 } // null otherwise
1652 } else if (metadata.has_argument (ArgumentType.REF)) {
1653 if (metadata.get_bool (ArgumentType.REF)) {
1654 direction = "inout";
1655 } // null otherwise
1656 } else {
1657 direction = reader.get_attribute ("direction");
1659 string transfer = reader.get_attribute ("transfer-ownership");
1660 string allow_none = reader.get_attribute ("allow-none");
1662 if (&scope != null) {
1663 scope = reader.get_attribute ("scope");
1666 string closure = reader.get_attribute ("closure");
1667 string destroy = reader.get_attribute ("destroy");
1668 if (closure != null && &closure_idx != null) {
1669 closure_idx = int.parse (closure);
1671 if (destroy != null && &destroy_idx != null) {
1672 destroy_idx = int.parse (destroy);
1675 next ();
1676 if (reader.name == "varargs") {
1677 start_element ("varargs");
1678 next ();
1679 param = new Parameter.with_ellipsis (get_current_src ());
1680 end_element ("varargs");
1681 } else {
1682 string ctype;
1683 var type = parse_type (out ctype, out array_length_idx, transfer == "full");
1684 bool changed;
1685 type = element_get_type (type, false, out changed);
1686 if (!changed) {
1687 // discard ctype, duplicated information
1688 ctype = null;
1691 if (type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_POS)) {
1692 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_POS);
1695 if (transfer == "full" || transfer == "container" || destroy != null) {
1696 type.value_owned = true;
1698 if (allow_none == "1") {
1699 type.nullable = true;
1701 param = new Parameter (name, type, get_current_src ());
1702 param.ctype = ctype;
1703 if (direction == "out") {
1704 param.direction = ParameterDirection.OUT;
1705 } else if (direction == "inout") {
1706 param.direction = ParameterDirection.REF;
1708 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
1710 end_element ("parameter");
1711 return param;
1714 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) {
1715 bool is_array = false;
1716 string type_name = reader.get_attribute ("name");
1718 if (&array_length_index != null) {
1719 array_length_index = -1;
1722 if (reader.name == "array") {
1723 is_array = true;
1724 start_element ("array");
1726 if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
1727 if (reader.get_attribute ("length") != null
1728 && &array_length_index != null) {
1729 array_length_index = int.parse (reader.get_attribute ("length"));
1731 next ();
1732 var element_type = parse_type ();
1733 end_element ("array");
1734 return new ArrayType (element_type, 1, null);
1736 } else if (reader.name == "callback"){
1737 var callback = parse_callback ();
1738 return new DelegateType (callback);
1739 } else {
1740 start_element ("type");
1743 if (&ctype != null) {
1744 ctype = reader.get_attribute("c:type");
1747 next ();
1749 if (type_name == "GLib.PtrArray"
1750 && current_token == MarkupTokenType.START_ELEMENT) {
1751 type_name = "GLib.GenericArray";
1754 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
1756 // type arguments / element types
1757 while (current_token == MarkupTokenType.START_ELEMENT) {
1758 var element_type = parse_type ();
1759 element_type.value_owned = transfer_elements;
1760 type.add_type_argument (element_type);
1763 end_element (is_array ? "array" : "type");
1764 return type;
1767 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
1768 if (&no_array_length != null) {
1769 no_array_length = false;
1771 if (&array_null_terminated != null) {
1772 array_null_terminated = false;
1775 DataType type;
1776 if (type_name == "none") {
1777 type = new VoidType (get_current_src ());
1778 } else if (type_name == "gpointer") {
1779 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
1780 } else if (type_name == "GObject.Strv") {
1781 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, get_current_src ());
1782 if (&no_array_length != null) {
1783 no_array_length = true;
1785 if (&array_null_terminated != null) {
1786 array_null_terminated = true;
1788 } else {
1789 bool known_type = true;
1790 if (type_name == "utf8") {
1791 type_name = "string";
1792 } else if (type_name == "gboolean") {
1793 type_name = "bool";
1794 } else if (type_name == "gchar") {
1795 type_name = "char";
1796 } else if (type_name == "gshort") {
1797 type_name = "short";
1798 } else if (type_name == "gushort") {
1799 type_name = "ushort";
1800 } else if (type_name == "gint") {
1801 type_name = "int";
1802 } else if (type_name == "guint") {
1803 type_name = "uint";
1804 } else if (type_name == "glong") {
1805 if (ctype == "gssize") {
1806 type_name = "ssize_t";
1807 } else {
1808 type_name = "long";
1810 } else if (type_name == "gulong") {
1811 if (ctype == "gsize") {
1812 type_name = "size_t";
1813 } else {
1814 type_name = "ulong";
1816 } else if (type_name == "gint8") {
1817 type_name = "int8";
1818 } else if (type_name == "guint8") {
1819 type_name = "uint8";
1820 } else if (type_name == "gint16") {
1821 type_name = "int16";
1822 } else if (type_name == "guint16") {
1823 type_name = "uint16";
1824 } else if (type_name == "gint32") {
1825 type_name = "int32";
1826 } else if (type_name == "guint32") {
1827 type_name = "uint32";
1828 } else if (type_name == "gint64") {
1829 type_name = "int64";
1830 } else if (type_name == "guint64") {
1831 type_name = "uint64";
1832 } else if (type_name == "gfloat") {
1833 type_name = "float";
1834 } else if (type_name == "gdouble") {
1835 type_name = "double";
1836 } else if (type_name == "filename") {
1837 type_name = "string";
1838 } else if (type_name == "GLib.offset") {
1839 type_name = "int64";
1840 } else if (type_name == "gsize") {
1841 type_name = "size_t";
1842 } else if (type_name == "gssize") {
1843 type_name = "ssize_t";
1844 } else if (type_name == "GType") {
1845 type_name = "GLib.Type";
1846 } else if (type_name == "GLib.String") {
1847 type_name = "GLib.StringBuilder";
1848 } else if (type_name == "GObject.Class") {
1849 type_name = "GLib.ObjectClass";
1850 } else if (type_name == "GLib.unichar") {
1851 type_name = "unichar";
1852 } else if (type_name == "GLib.Data") {
1853 type_name = "GLib.Datalist";
1854 } else if (type_name == "Atk.ImplementorIface") {
1855 type_name = "Atk.Implementor";
1856 } else {
1857 known_type = false;
1859 var sym = parse_symbol_from_string (type_name, get_current_src ());
1860 type = new UnresolvedType.from_symbol (sym, get_current_src ());
1861 if (!known_type) {
1862 unresolved_gir_symbols.add (sym);
1866 return type;
1869 Struct parse_record () {
1870 start_element ("record");
1871 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
1872 st.external = true;
1873 st.access = SymbolAccessibility.PUBLIC;
1875 string cname = reader.get_attribute ("c:type");
1876 if (cname != null) {
1877 st.set_cname (cname);
1880 current_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
1881 if (current_gtype_struct_for != null) {
1882 girdata["glib:is-gtype-struct-for"] = current_gtype_struct_for;
1885 next ();
1886 var old_symbols_info = current_symbols_info;
1887 var old_symbol = current_symbol;
1888 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1889 current_symbol = st;
1890 while (current_token == MarkupTokenType.START_ELEMENT) {
1891 if (!push_metadata ()) {
1892 skip_element ();
1893 continue;
1896 if (reader.name == "field") {
1897 if (reader.get_attribute ("name") != "priv") {
1898 add_symbol_info (parse_field ());
1899 } else {
1900 skip_element ();
1902 } else if (reader.name == "constructor") {
1903 parse_constructor ();
1904 } else if (reader.name == "method") {
1905 add_symbol_info (parse_method ("method"));
1906 } else if (reader.name == "union") {
1907 Struct s = parse_union ();
1908 var s_fields = s.get_fields ();
1909 foreach (var f in s_fields) {
1910 f.set_cname (s.get_cname () + "." + f.get_cname ());
1911 f.name = s.name + "_" + f.name;
1912 st.add_field (f);
1914 } else {
1915 // error
1916 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
1917 skip_element ();
1920 pop_metadata ();
1922 end_element ("record");
1924 merge_add_process (st);
1925 current_symbols_info = old_symbols_info;
1926 current_symbol = old_symbol;
1927 current_gtype_struct_for = null;
1929 return st;
1932 Class parse_class () {
1933 start_element ("class");
1934 var name = element_get_name ();
1935 string cname = reader.get_attribute ("c:type");
1936 string parent = reader.get_attribute ("parent");
1937 var cl = current_symbol.scope.lookup (name) as Class;
1938 if (cl == null) {
1939 cl = new Class (name, get_current_src ());
1940 cl.access = SymbolAccessibility.PUBLIC;
1941 cl.external = true;
1943 if (cname != null) {
1944 cl.set_cname (cname);
1947 if (parent != null) {
1948 cl.add_base_type (parse_type_from_gir_name (parent));
1951 next ();
1952 var first_field = true;
1953 var old_symbols_info = current_symbols_info;
1954 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1955 while (current_token == MarkupTokenType.START_ELEMENT) {
1956 if (!push_metadata ()) {
1957 skip_element ();
1958 continue;
1961 if (reader.name == "implements") {
1962 start_element ("implements");
1963 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
1964 next ();
1965 end_element ("implements");
1966 } else if (reader.name == "constant") {
1967 add_symbol_info (parse_constant ());
1968 } else if (reader.name == "field") {
1969 if (first_field && parent != null) {
1970 // first field is guaranteed to be the parent instance
1971 skip_element ();
1972 } else {
1973 var field = parse_field ();
1974 if (field.name != "priv") {
1975 add_symbol_info (field);
1978 first_field = false;
1979 } else if (reader.name == "property") {
1980 add_symbol_info (parse_property ());
1981 } else if (reader.name == "constructor") {
1982 add_symbol_info (parse_constructor (cname));
1983 } else if (reader.name == "function") {
1984 add_symbol_info (parse_method ("function"));
1985 } else if (reader.name == "method") {
1986 add_symbol_info (parse_method ("method"));
1987 } else if (reader.name == "virtual-method") {
1988 add_symbol_info (parse_method ("virtual-method"));
1989 } else if (reader.name == "union") {
1990 Struct s = parse_union ();
1991 var s_fields = s.get_fields ();
1992 foreach (var f in s_fields) {
1993 f.set_cname (s.get_cname () + "." + f.get_cname ());
1994 f.name = s.name + "_" + f.name;
1995 add_symbol_info (f);
1997 } else if (reader.name == "glib:signal") {
1998 add_symbol_info (parse_signal ());
1999 } else {
2000 // error
2001 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2002 skip_element ();
2005 pop_metadata ();
2008 merge_add_process (cl);
2009 current_symbols_info = old_symbols_info;
2011 handle_async_methods (cl);
2013 end_element ("class");
2014 return cl;
2017 Interface parse_interface () {
2018 start_element ("interface");
2019 var iface = new Interface (element_get_name (), get_current_src ());
2020 iface.access = SymbolAccessibility.PUBLIC;
2021 iface.external = true;
2023 string cname = reader.get_attribute ("c:type");
2024 if (cname != null) {
2025 iface.set_cname (cname);
2028 next ();
2029 var old_symbol = current_symbol;
2030 var old_symbols_info = current_symbols_info;
2031 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2032 current_symbol = iface;
2033 while (current_token == MarkupTokenType.START_ELEMENT) {
2034 if (!push_metadata ()) {
2035 skip_element ();
2036 continue;
2039 if (reader.name == "prerequisite") {
2040 start_element ("prerequisite");
2041 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2042 next ();
2043 end_element ("prerequisite");
2044 } else if (reader.name == "field") {
2045 add_symbol_info (parse_field ());
2046 } else if (reader.name == "property") {
2047 add_symbol_info (parse_property ());
2048 } else if (reader.name == "virtual-method") {
2049 add_symbol_info (parse_method ("virtual-method"));
2050 } else if (reader.name == "function") {
2051 add_symbol_info (parse_method ("function"));
2052 } else if (reader.name == "method") {
2053 add_symbol_info (parse_method ("method"));
2054 } else if (reader.name == "glib:signal") {
2055 add_symbol_info (parse_signal ());
2056 } else {
2057 // error
2058 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2059 skip_element ();
2062 pop_metadata ();
2065 merge_add_process (iface);
2066 current_symbol = old_symbol;
2067 current_symbols_info = old_symbols_info;
2069 handle_async_methods (iface);
2071 end_element ("interface");
2072 return iface;
2075 void handle_async_methods (ObjectTypeSymbol type_symbol) {
2076 var methods = type_symbol.get_methods ();
2077 for (int method_n = 0 ; method_n < methods.size ; method_n++) {
2078 var m = methods.get (method_n);
2080 if (m.coroutine) {
2081 string finish_method_base;
2082 if (m.name.has_suffix ("_async")) {
2083 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
2084 } else {
2085 finish_method_base = m.name;
2087 var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method;
2089 // check if the method is using non-standard finish method name
2090 if (finish_method == null) {
2091 var method_cname = m.get_finish_cname ();
2092 foreach (Method method in type_symbol.get_methods ()) {
2093 if (method.get_cname () == method_cname) {
2094 finish_method = method;
2095 break;
2100 if (finish_method != null) {
2101 m.return_type = finish_method.return_type.copy ();
2102 m.no_array_length = finish_method.no_array_length;
2103 m.array_null_terminated = finish_method.array_null_terminated;
2104 foreach (var param in finish_method.get_parameters ()) {
2105 if (param.direction == ParameterDirection.OUT) {
2106 var async_param = param.copy ();
2107 if (m.scope.lookup (param.name) != null) {
2108 // parameter name conflict
2109 async_param.name += "_out";
2111 m.add_parameter (async_param);
2114 foreach (DataType error_type in finish_method.get_error_types ()) {
2115 m.add_error_type (error_type.copy ());
2117 if (methods.index_of (finish_method) < method_n) {
2118 method_n--;
2120 type_symbol.scope.remove (finish_method.name);
2121 methods.remove (finish_method);
2127 Field parse_field () {
2128 start_element ("field");
2129 string name = reader.get_attribute ("name");
2130 string allow_none = reader.get_attribute ("allow-none");
2131 next ();
2132 var type = parse_type ();
2133 type = element_get_type (type, true);
2134 if (type is DelegateType && current_gtype_struct_for != null) {
2135 // virtual
2136 var gtype_struct_for = parse_symbol_from_string (current_gtype_struct_for);
2137 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2138 if (callbacks == null) {
2139 callbacks = new ArrayList<Delegate> ();
2140 gtype_callbacks.set (gtype_struct_for, callbacks);
2142 callbacks.add (((DelegateType) type).delegate_symbol);
2144 var field = new Field (name, type, null, get_current_src ());
2145 field.access = SymbolAccessibility.PUBLIC;
2146 field.no_array_length = true;
2147 if (allow_none == "1") {
2148 type.nullable = true;
2150 end_element ("field");
2151 return field;
2154 Property parse_property () {
2155 start_element ("property");
2156 string name = reader.get_attribute ("name").replace ("-", "_");
2157 string readable = reader.get_attribute ("readable");
2158 string writable = reader.get_attribute ("writable");
2159 string construct_ = reader.get_attribute ("construct");
2160 string construct_only = reader.get_attribute ("construct-only");
2161 next ();
2162 bool no_array_length;
2163 bool array_null_terminated;
2164 var type = parse_type (null, null, false, out no_array_length, out array_null_terminated);
2165 var prop = new Property (name, type, null, null, get_current_src ());
2166 prop.access = SymbolAccessibility.PUBLIC;
2167 prop.external = true;
2168 prop.no_accessor_method = true;
2169 prop.no_array_length = no_array_length;
2170 prop.array_null_terminated = array_null_terminated;
2171 if (readable != "0") {
2172 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
2174 if (writable == "1" || construct_only == "1") {
2175 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
2177 end_element ("property");
2178 return prop;
2181 Delegate parse_callback () {
2182 return this.parse_function ("callback") as Delegate;
2185 Method parse_constructor (string? parent_ctype = null) {
2186 start_element ("constructor");
2188 string throws_string = reader.get_attribute ("throws");
2189 string cname = reader.get_attribute ("c:identifier");
2190 var m = new CreationMethod (null, element_get_name (), get_current_src ());
2191 m.access = SymbolAccessibility.PUBLIC;
2192 m.has_construct_function = false;
2194 if (m.name == "new") {
2195 m.name = null;
2196 } else if (m.name.has_prefix ("new_")) {
2197 m.name = m.name.substring ("new_".length);
2199 if (cname != null) {
2200 m.set_cname (cname);
2203 next ();
2204 string? ctype;
2205 parse_return_value (out ctype);
2206 if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
2207 m.custom_return_type_cname = ctype;
2210 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2211 start_element ("parameters");
2212 next ();
2213 while (current_token == MarkupTokenType.START_ELEMENT) {
2214 if (!push_metadata ()) {
2215 skip_element ();
2216 continue;
2219 m.add_parameter (parse_parameter ());
2221 pop_metadata ();
2223 end_element ("parameters");
2226 if (throws_string == "1") {
2227 m.add_error_type (new ErrorType (null, null));
2229 end_element ("constructor");
2230 return m;
2233 class MethodInfo {
2234 public MethodInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx) {
2235 this.param = param;
2236 this.array_length_idx = array_length_idx;
2237 this.closure_idx = closure_idx;
2238 this.destroy_idx = destroy_idx;
2239 this.vala_idx = 0.0F;
2240 this.keep = true;
2243 public Parameter param;
2244 public float vala_idx;
2245 public int array_length_idx;
2246 public int closure_idx;
2247 public int destroy_idx;
2248 public bool keep;
2251 Symbol parse_function (string element_name) {
2252 start_element (element_name);
2253 string name = element_get_name ();
2254 string cname = reader.get_attribute ("c:identifier");
2255 string throws_string = reader.get_attribute ("throws");
2256 string invoker = reader.get_attribute ("invoker");
2257 next ();
2258 DataType return_type;
2259 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2260 return_type = parse_return_value ();
2261 } else {
2262 return_type = new VoidType ();
2264 return_type = element_get_type (return_type, true);
2266 Symbol s;
2268 if (element_name == "callback") {
2269 s = new Delegate (name, return_type, get_current_src ());
2270 } else {
2271 s = new Method (name, return_type, get_current_src ());
2274 s.access = SymbolAccessibility.PUBLIC;
2275 if (cname != null) {
2276 if (s is Method) {
2277 ((Method) s).set_cname (cname);
2278 } else {
2279 ((Delegate) s).set_cname (cname);
2283 s.external = true;
2285 if (element_name == "virtual-method" || element_name == "callback") {
2286 if (s is Method) {
2287 ((Method) s).is_virtual = true;
2288 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
2289 s.attributes.append (new Attribute ("NoWrapper", s.source_reference));
2293 if (invoker != null) {
2294 s.name = invoker;
2296 } else if (element_name == "function") {
2297 ((Method) s).binding = MemberBinding.STATIC;
2300 if (s is Method) {
2301 var method = (Method) s;
2302 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
2303 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
2304 method.is_abstract = false;
2305 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
2306 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
2307 method.is_virtual = false;
2309 method.vfunc_name = metadata.get_string (ArgumentType.VFUNC_NAME);
2312 var parameters = new ArrayList<MethodInfo> ();
2313 var array_length_parameters = new ArrayList<int> ();
2314 var closure_parameters = new ArrayList<int> ();
2315 var destroy_parameters = new ArrayList<int> ();
2316 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2317 start_element ("parameters");
2318 next ();
2320 while (current_token == MarkupTokenType.START_ELEMENT) {
2321 if (!push_metadata ()) {
2322 skip_element ();
2323 continue;
2326 int array_length_idx, closure_idx, destroy_idx;
2327 string scope;
2328 string default_param_name = null;
2329 default_param_name = "arg%d".printf (parameters.size);
2330 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, default_param_name);
2331 if (array_length_idx != -1) {
2332 array_length_parameters.add (array_length_idx);
2334 if (closure_idx != -1) {
2335 closure_parameters.add (closure_idx);
2337 if (destroy_idx != -1) {
2338 destroy_parameters.add (destroy_idx);
2341 var info = new MethodInfo(param, array_length_idx, closure_idx, destroy_idx);
2343 if (s is Method && scope == "async") {
2344 var unresolved_type = param.variable_type as UnresolvedType;
2345 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
2346 // GAsync-style method
2347 ((Method) s).coroutine = true;
2348 info.keep = false;
2352 parameters.add (info);
2353 pop_metadata ();
2355 end_element ("parameters");
2357 int i = 0, j=1;
2359 int last = -1;
2360 foreach (MethodInfo info in parameters) {
2361 if (s is Delegate && info.closure_idx == i) {
2362 var d = (Delegate) s;
2363 d.has_target = true;
2364 d.cinstance_parameter_position = (float) j - 0.1;
2365 info.keep = false;
2366 } else if (info.keep
2367 && !array_length_parameters.contains (i)
2368 && !closure_parameters.contains (i)
2369 && !destroy_parameters.contains (i)) {
2370 info.vala_idx = (float) j;
2371 info.keep = true;
2373 /* interpolate for vala_idx between this and last*/
2374 float last_idx = 0.0F;
2375 if (last != -1) {
2376 last_idx = parameters[last].vala_idx;
2378 for (int k=last+1; k < i; k++) {
2379 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
2381 last = i;
2382 j++;
2383 } else {
2384 info.keep = false;
2385 // make sure that vala_idx is always set
2386 // the above if branch does not set vala_idx for
2387 // hidden parameters at the end of the parameter list
2388 info.vala_idx = (j - 1) + (i - last) * 0.1F;
2390 i++;
2393 foreach (MethodInfo info in parameters) {
2394 if (info.keep) {
2396 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
2397 so do it first*/
2398 if (s is Method) {
2399 ((Method) s).add_parameter (info.param);
2400 } else {
2401 ((Delegate) s).add_parameter (info.param);
2404 if (info.array_length_idx != -1) {
2405 if ((info.array_length_idx) >= parameters.size) {
2406 Report.error (get_current_src (), "invalid array_length index");
2407 continue;
2409 info.param.carray_length_parameter_position = parameters[info.array_length_idx].vala_idx;
2410 info.param.set_array_length_cname (parameters[info.array_length_idx].param.name);
2412 if (info.param.variable_type is ArrayType && info.array_length_idx == -1) {
2413 info.param.no_array_length = true;
2416 if (info.closure_idx != -1) {
2417 if ((info.closure_idx) >= parameters.size) {
2418 Report.error (get_current_src (), "invalid closure index");
2419 continue;
2421 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
2423 if (info.destroy_idx != -1) {
2424 if (info.destroy_idx >= parameters.size) {
2425 Report.error (get_current_src (), "invalid destroy index");
2426 continue;
2428 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
2433 if (throws_string == "1") {
2434 s.add_error_type (new ErrorType (null, null));
2436 end_element (element_name);
2437 return s;
2440 Method parse_method (string element_name) {
2441 return this.parse_function (element_name) as Method;
2444 Signal parse_signal () {
2445 start_element ("glib:signal");
2446 string name = reader.get_attribute ("name").replace ("-", "_");
2447 next ();
2448 DataType return_type;
2449 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2450 return_type = parse_return_value ();
2451 } else {
2452 return_type = new VoidType ();
2454 var sig = new Signal (name, return_type, get_current_src ());
2455 sig.access = SymbolAccessibility.PUBLIC;
2456 sig.external = true;
2457 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2458 start_element ("parameters");
2459 next ();
2460 while (current_token == MarkupTokenType.START_ELEMENT) {
2461 if (!push_metadata ()) {
2462 skip_element ();
2463 continue;
2466 sig.add_parameter (parse_parameter ());
2468 pop_metadata ();
2470 end_element ("parameters");
2472 end_element ("glib:signal");
2473 return sig;
2476 Class parse_boxed (string element_name) {
2477 start_element (element_name);
2478 string name = reader.get_attribute ("name");
2479 if (name == null) {
2480 name = reader.get_attribute ("glib:name");
2482 var cl = new Class (name, get_current_src ());
2483 cl.access = SymbolAccessibility.PUBLIC;
2484 cl.external = true;
2485 cl.is_compact = true;
2487 string cname = reader.get_attribute ("c:type");
2488 if (cname != null) {
2489 cl.set_cname (cname);
2492 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
2493 cl.set_free_function ("g_boxed_free");
2494 cl.set_dup_function ("g_boxed_copy");
2496 next ();
2497 var old_symbols_info = current_symbols_info;
2498 var old_symbol = current_symbol;
2499 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2500 current_symbol = cl;
2501 while (current_token == MarkupTokenType.START_ELEMENT) {
2502 if (!push_metadata ()) {
2503 skip_element ();
2504 continue;
2507 if (reader.name == "field") {
2508 add_symbol_info (parse_field ());
2509 } else if (reader.name == "constructor") {
2510 parse_constructor ();
2511 } else if (reader.name == "method") {
2512 add_symbol_info (parse_method ("method"));
2513 } else {
2514 // error
2515 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2516 skip_element ();
2519 pop_metadata ();
2521 end_element (element_name);
2523 merge_add_process (cl);
2524 current_symbols_info = old_symbols_info;
2525 current_symbol = old_symbol;
2527 return cl;
2530 Struct parse_union () {
2531 start_element ("union");
2532 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2533 st.access = SymbolAccessibility.PUBLIC;
2534 st.external = true;
2535 next ();
2537 var old_symbol = current_symbol;
2538 current_symbol = st;
2539 while (current_token == MarkupTokenType.START_ELEMENT) {
2540 if (!push_metadata ()) {
2541 skip_element ();
2542 continue;
2545 if (reader.name == "field") {
2546 st.add_field (parse_field ());
2547 } else if (reader.name == "constructor") {
2548 parse_constructor ();
2549 } else if (reader.name == "method") {
2550 st.add_method (parse_method ("method"));
2551 } else if (reader.name == "record") {
2552 Struct s = parse_record ();
2553 var fs = s.get_fields ();
2554 foreach (var f in fs) {
2555 f.set_cname (s.get_cname () + "." + f.get_cname ());
2556 f.name = s.name + "_" + f.name;
2557 st.add_field (f);
2559 } else {
2560 // error
2561 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
2562 skip_element ();
2565 pop_metadata ();
2568 end_element ("union");
2569 current_symbol = old_symbol;
2571 return st;
2574 Constant parse_constant () {
2575 start_element ("constant");
2576 string name = element_get_name ();
2577 next ();
2578 var type = parse_type ();
2579 var c = new Constant (name, type, null, get_current_src ());
2580 c.access = SymbolAccessibility.PUBLIC;
2581 c.external = true;
2582 end_element ("constant");
2583 return c;
2586 /* Reporting */
2587 void report_unused_metadata (Metadata metadata) {
2588 if (metadata == Metadata.empty) {
2589 return;
2592 if (metadata.args.size == 0 && metadata.children.size == 0) {
2593 Report.warning (metadata.source_reference, "empty metadata");
2594 return;
2597 foreach (var arg_type in metadata.args.get_keys ()) {
2598 var arg = metadata.args[arg_type];
2599 if (!arg.used) {
2600 // if metadata is used and argument is not, then it's a unexpected argument
2601 Report.warning (arg.source_reference, "argument never used");
2605 foreach (var child in metadata.children) {
2606 if (!child.used) {
2607 Report.warning (child.source_reference, "metadata never used");
2608 } else {
2609 report_unused_metadata (child);
2614 /* Post-parsing */
2616 void resolve_gir_symbols () {
2617 // we are remapping unresolved symbols, so create them from concrete symbols
2618 foreach (var map_from in concrete_symbols_map.get_keys ()) {
2619 unresolved_symbols_map[get_unresolved_symbol(map_from)] = concrete_symbols_map[map_from];
2622 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
2623 foreach (var map_from in unresolved_gir_symbols) {
2624 while (map_from != null) {
2625 var map_to = unresolved_symbols_map[map_from];
2626 if (map_to != null) {
2627 // remap the original symbol to match the target
2628 map_from.inner = null;
2629 map_from.name = map_to.name;
2630 if (map_to is UnresolvedSymbol) {
2631 var umap_to = (UnresolvedSymbol) map_to;
2632 while (umap_to.inner != null) {
2633 umap_to = umap_to.inner;
2634 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
2635 map_from = map_from.inner;
2637 } else {
2638 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
2639 map_to = map_to.parent_symbol;
2640 map_from.inner = new UnresolvedSymbol (null, map_to.name);
2641 map_from = map_from.inner;
2644 break;
2646 map_from = map_from.inner;
2651 Symbol? resolve_symbol (Scope parent_scope, UnresolvedSymbol unresolved_symbol) {
2652 // simple symbol resolver, enough for gir
2653 if (unresolved_symbol.inner == null) {
2654 var scope = parent_scope;
2655 while (scope != null) {
2656 var sym = scope.lookup (unresolved_symbol.name);
2657 if (sym != null) {
2658 return sym;
2660 scope = scope.parent_scope;
2662 } else {
2663 var inner = resolve_symbol (parent_scope, unresolved_symbol.inner);
2664 if (inner != null) {
2665 return inner.scope.lookup (unresolved_symbol.name);
2668 return null;
2671 void postprocess_interfaces () {
2672 foreach (var iface in interfaces) {
2673 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
2674 ensure we have at least one instantiable prerequisite */
2675 bool has_instantiable_prereq = false;
2676 foreach (DataType prereq in iface.get_prerequisites ()) {
2677 Symbol sym = null;
2678 if (prereq is UnresolvedType) {
2679 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
2680 sym = resolve_symbol (iface.parent_symbol.scope, unresolved_symbol);
2681 } else {
2682 sym = prereq.data_type;
2684 if (sym is Class) {
2685 has_instantiable_prereq = true;
2686 break;
2690 if (!has_instantiable_prereq) {
2691 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
2696 void postprocess_reparenting () {
2697 foreach (UnresolvedSymbol target_unresolved_symbol in symbol_reparent_map.get_keys ()) {
2698 var target_symbol = resolve_symbol (context.root.scope, target_unresolved_symbol);
2699 if (target_symbol == null) {
2700 // create namespaces backward
2701 var sym = target_unresolved_symbol;
2702 var ns = new Namespace (sym.name, sym.source_reference);
2703 var result = ns;
2704 sym = sym.inner;
2705 while (sym != null) {
2706 var res = resolve_symbol (context.root.scope, sym);
2707 if (res != null && !(res is Namespace)) {
2708 result = null;
2709 break;
2711 var parent = res as Namespace;
2712 if (res == null) {
2713 parent = new Namespace (sym.name, sym.source_reference);
2715 if (parent.scope.lookup (ns.name) == null) {
2716 parent.add_namespace (ns);
2718 ns = parent;
2719 sym = sym.inner;
2721 if (result != null && sym == null && context.root.scope.lookup (ns.name) == null) {
2722 // a new root namespace, helpful for a possible non-gobject gir?
2723 context.root.add_namespace (ns);
2725 target_symbol = result;
2727 if (target_symbol == null) {
2728 Report.error (null, "unable to reparent into `%s'".printf (target_unresolved_symbol.to_string ()));
2729 continue;
2731 var symbols = symbol_reparent_map[target_unresolved_symbol];
2732 foreach (var symbol in symbols) {
2733 add_symbol_to_container (target_symbol, symbol);
2738 void postprocess_gtype_callbacks (Namespace ns) {
2739 foreach (UnresolvedSymbol gtype_struct_for in gtype_callbacks.get_keys ()) {
2740 // parent symbol is the record, therefore use parent of parent symbol
2741 var gtype = resolve_symbol (ns.scope, gtype_struct_for) as ObjectTypeSymbol;
2742 if (gtype == null) {
2743 Report.error (null, "unknown symbol `%s' while postprocessing callbacks".printf (gtype_struct_for.name));
2744 continue;
2746 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2747 foreach (Delegate d in callbacks) {
2748 var symbol = gtype.scope.lookup (d.name);
2749 if (symbol == null) {
2750 continue;
2751 } else if (symbol is Method) {
2752 var meth = (Method) symbol;
2753 if (gtype is Class) {
2754 meth.is_virtual = true;
2755 } else if (gtype is Interface) {
2756 meth.is_abstract = true;
2758 } else if (symbol is Signal) {
2759 var sig = (Signal) symbol;
2760 sig.is_virtual = true;
2761 assume_parameter_names (sig, d);
2762 } else if (symbol is Property) {
2763 var prop = (Property) symbol;
2764 prop.is_virtual = true;
2765 } else {
2766 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (d.name, gtype.name));
2772 void postprocess_aliases () {
2773 /* this is unfortunate because <alias> tag has no type information, thus we have
2774 to guess it from the target */
2775 foreach (var alias in aliases) {
2776 DataType base_type = null;
2777 Symbol type_sym = null;
2778 if (alias.base_type is UnresolvedType) {
2779 base_type = alias.base_type;
2780 type_sym = resolve_symbol (alias.parent_symbol.scope, ((UnresolvedType) base_type).unresolved_symbol);
2781 } else if (!(alias.base_type is VoidType)) {
2782 base_type = alias.base_type;
2783 type_sym = base_type.data_type;
2786 if (base_type == null || type_sym == null || type_sym is Struct) {
2787 var st = new Struct (alias.name, alias.source_reference);
2788 st.access = SymbolAccessibility.PUBLIC;
2789 if (base_type != null) {
2790 // threat target="none" as a new struct
2791 st.base_type = base_type;
2793 st.external = true;
2794 add_symbol_to_container (alias.parent_symbol, st);
2795 } else if (type_sym is Class) {
2796 var cl = new Class (alias.name, alias.source_reference);
2797 cl.access = SymbolAccessibility.PUBLIC;
2798 if (base_type != null) {
2799 cl.add_base_type (base_type);
2801 cl.external = true;
2802 add_symbol_to_container (alias.parent_symbol, cl);
2807 void find_static_method_parent (string cname, Symbol current, ref Symbol best, ref double match, double match_char) {
2808 var old_best = best;
2809 if (current.scope.get_symbol_table () != null) {
2810 foreach (var child in current.scope.get_symbol_table().get_values ()) {
2811 if (child is Struct || child is ObjectTypeSymbol || child is Namespace) {
2812 find_static_method_parent (cname, child, ref best, ref match, match_char);
2816 if (best != old_best) {
2817 // child is better
2818 return;
2821 var current_cprefix = current.get_lower_case_cprefix ();
2822 if (cname.has_prefix (current_cprefix)) {
2823 var current_match = match_char * current_cprefix.length;
2824 if (current_match > match) {
2825 match = current_match;
2826 best = current;
2831 void postprocess_namespace_methods () {
2832 /* transform static methods into instance methods if possible.
2833 In most of cases this is a .gir fault we are going to fix */
2834 foreach (var ns in namespace_methods.get_keys ()) {
2835 var ns_cprefix = ns.get_lower_case_cprefix ();
2836 var methods = namespace_methods[ns];
2837 foreach (var method in methods) {
2838 if (method.parent_symbol != null) {
2839 // fixed earlier by metadata
2840 continue;
2843 var cname = method.get_cname ();
2845 Parameter first_param = null;
2846 if (method.get_parameters ().size > 0) {
2847 first_param = method.get_parameters()[0];
2849 if (first_param != null && first_param.variable_type is UnresolvedType) {
2850 // check if it's a missed instance method (often happens for structs)
2851 var parent = resolve_symbol (ns.scope, ((UnresolvedType) first_param.variable_type).unresolved_symbol);
2852 if (parent != null && (parent is Struct || parent is ObjectTypeSymbol || parent is Namespace)
2853 && cname.has_prefix (parent.get_lower_case_cprefix ())) {
2854 // instance method
2855 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2856 if (parent.scope.lookup (new_name) == null) {
2857 method.name = new_name;
2858 method.get_parameters().remove_at (0);
2859 method.binding = MemberBinding.INSTANCE;
2860 add_symbol_to_container (parent, method);
2861 } else {
2862 ns.add_method (method);
2864 continue;
2868 double match = 0;
2869 Symbol parent = ns;
2870 find_static_method_parent (cname, ns, ref parent, ref match, 1.0/cname.length);
2871 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2872 if (parent.scope.lookup (new_name) == null) {
2873 method.name = new_name;
2874 add_symbol_to_container (parent, method);
2875 } else {
2876 ns.add_method (method);
2882 /* Hash and equal functions */
2884 static uint unresolved_symbol_hash (void *ptr) {
2885 var sym = (UnresolvedSymbol) ptr;
2886 var builder = new StringBuilder ();
2887 while (sym != null) {
2888 builder.append (sym.name);
2889 sym = sym.inner;
2891 return builder.str.hash ();
2894 static bool unresolved_symbol_equal (void *ptr1, void *ptr2) {
2895 var sym1 = (UnresolvedSymbol) ptr1;
2896 var sym2 = (UnresolvedSymbol) ptr2;
2897 while (sym1 != sym2) {
2898 if (sym1 == null || sym2 == null) {
2899 return false;
2901 if (sym1.name != sym2.name) {
2902 return false;
2904 sym1 = sym1.inner;
2905 sym2 = sym2.inner;
2907 return true;