girparser: Fix support for signals metadata.
[vala-lang.git] / vala / valagirparser.vala
blob6e3fd12cb20402a43a627b8ee1491d616d208d88
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_literal () {
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 default:
426 Report.error (src, "expected literal");
427 break;
429 next ();
430 return expr;
433 bool parse_args (Metadata metadata) {
434 while (current != TokenType.EOF && has_space () && !has_newline ()) {
435 SourceReference src;
436 var id = parse_identifier (out src, false);
437 if (id == null) {
438 return false;
440 var arg_type = ArgumentType.from_string (id);
441 if (arg_type == null) {
442 Report.error (src, "unknown argument");
443 return false;
446 if (current != TokenType.ASSIGN) {
447 // threat as `true'
448 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, src), src));
449 continue;
451 next ();
453 Expression expr = parse_literal ();
454 if (expr == null) {
455 return false;
457 metadata.add_argument (arg_type, new Argument (expr, src));
460 return true;
463 bool parse_rule () {
464 var old_end = end;
465 var metadata = parse_pattern ();
466 if (metadata == null) {
467 return false;
470 if (current == TokenType.EOF || old_end.line != end.line) {
471 // eof or new rule
472 return true;
474 return parse_args (metadata);
478 class SymbolInfo {
479 public Symbol symbol;
480 public Metadata metadata;
481 // additional information from GIR
482 public HashMap<string,string> girdata;
485 class Alias {
486 public string name;
487 public DataType base_type;
488 public Symbol parent_symbol;
489 public SourceReference source_reference;
492 static GLib.Regex type_from_string_regex;
494 MarkupReader reader;
496 CodeContext context;
497 Namespace glib_ns;
499 SourceFile current_source_file;
500 Symbol current_symbol;
502 string current_gtype_struct_for;
503 SourceLocation begin;
504 SourceLocation end;
505 MarkupTokenType current_token;
507 string[] cheader_filenames;
509 ArrayList<Metadata> metadata_stack;
510 Metadata metadata;
511 ArrayList<HashMap<string,string>> girdata_stack;
512 HashMap<string,string> girdata;
514 HashMap<string,ArrayList<SymbolInfo>> current_symbols_info;
516 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
517 HashMap<Symbol,Symbol> concrete_symbols_map = new HashMap<Symbol,Symbol> ();
519 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
520 HashMap<UnresolvedSymbol,ArrayList<Symbol>> symbol_reparent_map = new HashMap<UnresolvedSymbol,ArrayList<Symbol>> (unresolved_symbol_hash, unresolved_symbol_equal);
521 HashMap<Namespace,ArrayList<Method>> namespace_methods = new HashMap<Namespace,ArrayList<Method>> ();
522 ArrayList<Alias> aliases = new ArrayList<Alias> ();
523 ArrayList<Interface> interfaces = new ArrayList<Interface> ();
525 HashMap<UnresolvedSymbol,ArrayList<Delegate>> gtype_callbacks;
528 * Parses all .gir source files in the specified code
529 * context and builds a code tree.
531 * @param context a code context
533 public void parse (CodeContext context) {
534 this.context = context;
535 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
536 context.accept (this);
538 resolve_gir_symbols ();
540 postprocess_interfaces ();
541 postprocess_reparenting ();
542 postprocess_aliases ();
543 postprocess_namespace_methods ();
546 public override void visit_source_file (SourceFile source_file) {
547 // collect gir namespaces
548 foreach (var node in source_file.get_nodes ()) {
549 if (node is Namespace) {
550 var ns = (Namespace) node;
551 var gir_namespace = source_file.gir_namespace;
552 if (gir_namespace == null) {
553 var a = ns.get_attribute ("CCode");
554 if (a != null && a.has_argument ("gir_namespace")) {
555 gir_namespace = a.get_string ("gir_namespace");
558 if (gir_namespace != null && gir_namespace != ns.name) {
559 var map_from = new UnresolvedSymbol (null, gir_namespace);
560 set_symbol_mapping (map_from, ns);
561 break;
566 if (source_file.filename.has_suffix (".gir")) {
567 parse_file (source_file);
571 public void parse_file (SourceFile source_file) {
572 metadata_stack = new ArrayList<Metadata> ();
573 metadata = Metadata.empty;
574 girdata_stack = new ArrayList<HashMap<string,string>> ();
576 // load metadata, first look into metadata directories then in the same directory of the .gir.
577 string? metadata_filename = context.get_metadata_path (source_file.filename);
578 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
579 var metadata_parser = new MetadataParser ();
580 var metadata_file = new SourceFile (context, source_file.file_type, metadata_filename);
581 context.add_source_file (metadata_file);
582 metadata = metadata_parser.parse_metadata (metadata_file);
585 this.current_source_file = source_file;
586 reader = new MarkupReader (source_file.filename);
588 // xml prolog
589 next ();
590 next ();
592 next ();
593 parse_repository ();
595 report_unused_metadata (metadata);
597 reader = null;
598 this.current_source_file = null;
601 void next () {
602 current_token = reader.read_token (out begin, out end);
604 // Skip *all* <doc> tags
605 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
606 skip_element();
609 void start_element (string name) {
610 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
611 // error
612 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
616 void end_element (string name) {
617 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
618 // error
619 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
621 next ();
624 SourceReference get_current_src () {
625 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
628 const string GIR_VERSION = "1.2";
630 void add_symbol_to_container (Symbol container, Symbol sym) {
631 var name = sym.name;
632 if (name == null && sym is CreationMethod) {
633 name = ".new";
635 if (container.scope.lookup (name) != null) {
636 // overridden by -custom.vala
637 return;
640 if (container is Class) {
641 unowned Class cl = (Class) container;
643 if (sym is Class) {
644 cl.add_class ((Class) sym);
645 } else if (sym is Constant) {
646 cl.add_constant ((Constant) sym);
647 } else if (sym is Enum) {
648 cl.add_enum ((Enum) sym);
649 } else if (sym is Field) {
650 cl.add_field ((Field) sym);
651 } else if (sym is Method) {
652 cl.add_method ((Method) sym);
653 } else if (sym is Property) {
654 cl.add_property ((Property) sym);
655 } else if (sym is Signal) {
656 cl.add_signal ((Signal) sym);
657 } else if (sym is Struct) {
658 cl.add_struct ((Struct) sym);
660 } else if (container is Enum) {
661 unowned Enum en = (Enum) container;
663 if (sym is EnumValue) {
664 en.add_value ((EnumValue) sym);
665 } else if (sym is Constant) {
666 en.add_constant ((Constant) sym);
667 } else if (sym is Method) {
668 en.add_method ((Method) sym);
670 } else if (container is Interface) {
671 unowned Interface iface = (Interface) container;
673 if (sym is Class) {
674 iface.add_class ((Class) sym);
675 } else if (sym is Constant) {
676 iface.add_constant ((Constant) sym);
677 } else if (sym is Enum) {
678 iface.add_enum ((Enum) sym);
679 } else if (sym is Field) {
680 iface.add_field ((Field) sym);
681 } else if (sym is Method) {
682 iface.add_method ((Method) sym);
683 } else if (sym is Property) {
684 iface.add_property ((Property) sym);
685 } else if (sym is Signal) {
686 iface.add_signal ((Signal) sym);
687 } else if (sym is Struct) {
688 iface.add_struct ((Struct) sym);
690 } else if (container is Namespace) {
691 unowned Namespace ns = (Namespace) container;
693 if (sym is Namespace) {
694 ns.add_namespace ((Namespace) sym);
695 } else if (sym is Class) {
696 ns.add_class ((Class) sym);
697 } else if (sym is Constant) {
698 ns.add_constant ((Constant) sym);
699 } else if (sym is Delegate) {
700 ns.add_delegate ((Delegate) sym);
701 } else if (sym is Enum) {
702 ns.add_enum ((Enum) sym);
703 } else if (sym is ErrorDomain) {
704 ns.add_error_domain ((ErrorDomain) sym);
705 } else if (sym is Field) {
706 ns.add_field ((Field) sym);
707 } else if (sym is Interface) {
708 ns.add_interface ((Interface) sym);
709 } else if (sym is Method) {
710 ns.add_method ((Method) sym);
711 } else if (sym is Namespace) {
712 ns.add_namespace ((Namespace) sym);
713 } else if (sym is Struct) {
714 ns.add_struct ((Struct) sym);
716 } else if (container is Struct) {
717 unowned Struct st = (Struct) container;
719 if (sym is Constant) {
720 st.add_constant ((Constant) sym);
721 } else if (sym is Field) {
722 st.add_field ((Field) sym);
723 } else if (sym is Method) {
724 st.add_method ((Method) sym);
725 } else if (sym is Property) {
726 st.add_property ((Property) sym);
728 } else {
729 Report.error (sym.source_reference, "impossible to add to container `%s'".printf (container.name));
733 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
734 UnresolvedSymbol? sym = null;
735 foreach (unowned string s in symbol_string.split (".")) {
736 sym = new UnresolvedSymbol (sym, s, source_reference);
738 if (sym == null) {
739 Report.error (source_reference, "a symbol must be specified");
741 return sym;
744 UnresolvedSymbol get_unresolved_symbol (Symbol symbol) {
745 if (symbol is UnresolvedSymbol) {
746 return (UnresolvedSymbol) symbol;
748 var sym = new UnresolvedSymbol (null, symbol.name);
749 var result = sym;
750 var cur = symbol.parent_node as Symbol;
751 while (cur != null && cur.name != null) {
752 sym = new UnresolvedSymbol (sym, cur.name);
753 cur = cur.parent_node as Symbol;
755 return result;
758 void set_symbol_mapping (Symbol map_from, Symbol map_to) {
759 // last mapping is the most up-to-date
760 if (map_from is UnresolvedSymbol) {
761 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
762 } else {
763 concrete_symbols_map[map_from] = map_to;
767 void assume_parameter_names (Signal sig, Symbol sym) {
768 Iterator<Parameter> iter;
769 if (sym is Method) {
770 iter = ((Method) sym).get_parameters ().iterator ();
771 } else {
772 iter = ((Delegate) sym).get_parameters ().iterator ();
774 foreach (var param in sig.get_parameters ()) {
775 if (!iter.next ()) {
776 // unreachable for valid GIR
777 break;
779 param.name = iter.get ().name;
783 SymbolInfo? add_symbol_info (Symbol symbol) {
784 var name = symbol.name;
785 if (symbol is CreationMethod && name == null) {
786 name = ".new";
789 var info = new SymbolInfo ();
790 info.symbol = symbol;
791 info.metadata = metadata;
792 info.girdata = girdata;
793 var colliding = current_symbols_info[name];
794 if (colliding == null) {
795 colliding = new ArrayList<SymbolInfo> ();
796 current_symbols_info[name] = colliding;
798 colliding.add (info);
799 return info;
802 SymbolInfo? find_invoker (Method method) {
803 /* most common use case is invoker has at least the given method prefix
804 and the same parameter names */
805 var prefix = "%s_".printf (method.name);
806 foreach (var name in current_symbols_info.get_keys ()) {
807 if (!name.has_prefix (prefix)) {
808 continue;
810 var infos = current_symbols_info[name];
811 foreach (var cinfo in infos) {
812 Method? invoker = cinfo.symbol as Method;
813 if (invoker == null || (method.get_parameters ().size != invoker.get_parameters ().size)) {
814 continue;
816 var iter = invoker.get_parameters ().iterator ();
817 foreach (var param in method.get_parameters ()) {
818 assert (iter.next ());
819 if (param.name != iter.get ().name) {
820 invoker = null;
821 break;
824 if (invoker != null) {
825 return cinfo;
830 return null;
833 void merge (SymbolInfo info, ArrayList<SymbolInfo> colliding, ArrayList<SymbolInfo> merged) {
834 if (info.symbol is Struct) {
835 var gtype_struct_for = info.girdata["glib:is-gtype-struct-for"];
836 if (gtype_struct_for != null && current_symbols_info.contains (gtype_struct_for)) {
837 var iface = current_symbols_info.get (gtype_struct_for).get (0).symbol as Interface;
838 if (iface != null) {
839 // set the interface struct name
840 iface.set_type_cname (((Struct) info.symbol).get_cname ());
842 merged.add (info);
844 } else if (info.symbol is Property) {
845 foreach (var cinfo in colliding) {
846 var sym = cinfo.symbol;
847 if (sym is Signal || sym is Field) {
848 // properties take precedence
849 merged.add (cinfo);
850 } else if (sym is Method) {
851 // assume method is getter
852 merged.add (cinfo);
855 var getter_name = "get_%s".printf (info.symbol.name);
856 var setter_name = "set_%s".printf (info.symbol.name);
857 if (current_symbols_info.contains (getter_name) || current_symbols_info.contains (setter_name)) {
858 ((Property) info.symbol).no_accessor_method = false;
860 } else if (info.symbol is Signal) {
861 var sig = (Signal) info.symbol;
862 foreach (var cinfo in colliding) {
863 var sym = cinfo.symbol;
864 if (sym is Method) {
865 var method = (Method) sym;
866 if (method.is_virtual) {
867 sig.is_virtual = true;
868 } else {
869 sig.has_emitter = true;
871 assume_parameter_names (sig, method);
872 merged.add (cinfo);
873 } else if (sym is Field) {
874 merged.add (cinfo);
877 } else if (info.symbol is Method && !(info.symbol is CreationMethod)) {
878 var method = (Method) info.symbol;
879 foreach (var cinfo in colliding) {
880 var sym = cinfo.symbol;
881 if (sym != method && method.is_virtual && sym is Method) {
882 bool different_invoker = false;
883 foreach (var attr in method.attributes) {
884 if (attr.name == "NoWrapper") {
885 /* no invoker but this method has the same name,
886 most probably the invoker has a different name
887 and g-ir-scanner missed it */
888 var invoker = find_invoker (method);
889 if (invoker != null) {
890 method.vfunc_name = method.name;
891 method.name = invoker.symbol.name;
892 method.attributes.remove (attr);
893 merged.add (invoker);
894 different_invoker = true;
895 break;
899 if (!different_invoker) {
900 merged.add (cinfo);
904 } else if (info.symbol is Field) {
905 foreach (var cinfo in colliding) {
906 var sym = cinfo.symbol;
907 if (sym is Method) {
908 // assume method is getter
909 merged.add (cinfo);
913 var field = (Field) info.symbol;
914 if (field.variable_type is ArrayType) {
915 SymbolInfo array_length = null;
916 if (current_symbols_info.contains ("n_%s".printf (field.name))) {
917 array_length = current_symbols_info.get ("n_%s".printf (field.name)).get (0);
918 } else if (current_symbols_info.contains ("%s_length".printf (field.name))) {
919 array_length = current_symbols_info.get ("%s_length".printf (field.name)).get (0);
921 if (array_length != null) {
922 // array has length
923 field.set_array_length_cname (array_length.symbol.name);
924 field.no_array_length = false;
925 merged.add (array_length);
931 void postprocess_symbol (Symbol symbol, Metadata metadata) {
932 // deprecation
933 symbol.replacement = metadata.get_string (ArgumentType.REPLACEMENT);
934 symbol.deprecated_since = element_get_string ("deprecated-version", ArgumentType.DEPRECATED_SINCE);
935 symbol.deprecated = metadata.get_bool (ArgumentType.DEPRECATED) || symbol.replacement != null || symbol.deprecated_since != null;
937 // mark to be reparented
938 if (metadata.has_argument (ArgumentType.PARENT)) {
939 var target_symbol = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
940 var reparent_list = symbol_reparent_map[target_symbol];
941 if (reparent_list == null) {
942 reparent_list = new ArrayList<Symbol>();
943 symbol_reparent_map[target_symbol] = reparent_list;
945 reparent_list.add (symbol);
947 // if referenceable, map unresolved references to point to the new place
948 if (symbol is Namespace || symbol is TypeSymbol) {
949 set_symbol_mapping (symbol, new UnresolvedSymbol (target_symbol, symbol.name));
954 void merge_add_process (Symbol container) {
955 var merged = new ArrayList<SymbolInfo> ();
956 foreach (var name in current_symbols_info.get_keys ()) {
957 var colliding = current_symbols_info[name];
958 foreach (var info in colliding) {
959 merge (info, colliding, merged);
963 foreach (var infos in current_symbols_info.get_values ()) {
964 foreach (var info in infos) {
965 if (merged.contains (info) || info.metadata.get_bool (ArgumentType.HIDDEN)) {
966 continue;
968 if (!(current_symbol is Namespace && info.symbol is Method) && !info.metadata.has_argument (ArgumentType.PARENT)) {
969 add_symbol_to_container (container, info.symbol);
971 postprocess_symbol (info.symbol, info.metadata);
976 Metadata get_current_metadata () {
977 var name = reader.name;
978 var child_name = reader.get_attribute ("name");
979 if (child_name == null) {
980 return Metadata.empty;
983 var type = MetadataType.GENERIC;
984 if (name == "glib:signal") {
985 child_name = child_name.replace ("-", "_");
986 type = MetadataType.SIGNAL;
987 } else if (name == "property") {
988 type = MetadataType.PROPERTY;
991 return metadata.match_child (child_name, type);
994 bool push_metadata () {
995 // skip?
996 if (reader.get_attribute ("introspectable") == "0") {
997 return false;
999 var new_metadata = get_current_metadata ();
1000 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1001 return false;
1004 metadata_stack.add (metadata);
1005 metadata = new_metadata;
1006 girdata_stack.add (girdata);
1007 girdata = new HashMap<string,string> (str_hash, str_equal);
1009 return true;
1012 void pop_metadata () {
1013 metadata = metadata_stack[metadata_stack.size - 1];
1014 metadata_stack.remove_at (metadata_stack.size - 1);
1015 girdata = girdata_stack[girdata_stack.size - 1];
1016 girdata_stack.remove_at (girdata_stack.size - 1);
1019 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1020 int type_arguments_length = (int) type_arguments.length;
1021 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1023 int depth = 0;
1024 for (var c = 0 ; c < type_arguments_length ; c++) {
1025 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1026 depth++;
1027 current.append_unichar (type_arguments[c]);
1028 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1029 depth--;
1030 current.append_unichar (type_arguments[c]);
1031 } else if (type_arguments[c] == ',') {
1032 if (depth == 0) {
1033 var dt = parse_type_from_string (current.str, true, source_reference);
1034 if (dt == null) {
1035 return false;
1037 parent_type.add_type_argument (dt);
1038 current.truncate ();
1039 } else {
1040 current.append_unichar (type_arguments[c]);
1042 } else {
1043 current.append_unichar (type_arguments[c]);
1047 var dt = parse_type_from_string (current.str, true, source_reference);
1048 if (dt == null) {
1049 return false;
1051 parent_type.add_type_argument (dt);
1053 return true;
1056 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1057 if (type_from_string_regex == null) {
1058 try {
1059 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[(,*)?\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1060 } catch (GLib.RegexError e) {
1061 GLib.error ("Unable to compile regex: %s", e.message);
1065 GLib.MatchInfo match;
1066 if (!type_from_string_regex.match (type_string, 0, out match)) {
1067 Report.error (source_reference, "unable to parse type");
1068 return null;
1071 DataType? type = null;
1073 var ownership_data = match.fetch (1);
1074 var type_name = match.fetch (2);
1075 var type_arguments_data = match.fetch (3);
1076 var pointers_data = match.fetch (4);
1077 var array_data = match.fetch (5);
1078 var nullable_data = match.fetch (6);
1080 var nullable = nullable_data != null && nullable_data.length > 0;
1082 if (ownership_data == null && type_name == "void") {
1083 if (array_data == null && !nullable) {
1084 type = new VoidType (source_reference);
1085 if (pointers_data != null) {
1086 for (int i=0; i < pointers_data.length; i++) {
1087 type = new PointerType (type);
1090 return type;
1091 } else {
1092 Report.error (source_reference, "invalid void type");
1093 return null;
1097 bool value_owned = owned_by_default;
1099 if (ownership_data == "owned") {
1100 if (owned_by_default) {
1101 Report.error (source_reference, "unexpected `owned' keyword");
1102 } else {
1103 value_owned = true;
1105 } else if (ownership_data == "unowned") {
1106 if (owned_by_default) {
1107 value_owned = true;
1108 } else {
1109 Report.error (source_reference, "unexpected `unowned' keyword");
1110 return null;
1114 var sym = parse_symbol_from_string (type_name, source_reference);
1115 if (sym == null) {
1116 return null;
1118 type = new UnresolvedType.from_symbol (sym, source_reference);
1120 if (type_arguments_data != null && type_arguments_data.length > 0) {
1121 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1122 return null;
1126 if (pointers_data != null) {
1127 for (int i=0; i < pointers_data.length; i++) {
1128 type = new PointerType (type);
1132 if (array_data != null) {
1133 type = new ArrayType (type, (int) array_data.length + 1, source_reference);
1136 type.nullable = nullable;
1137 type.value_owned = value_owned;
1138 return type;
1141 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1142 var str = metadata.get_string (arg_type);
1143 if (str == null) {
1144 str = reader.get_attribute (attribute_name);
1146 return str;
1150 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1151 * If type arguments change, the C declaration is not affected.
1153 DataType? element_get_type (DataType orig_type, bool owned_by_default, out bool changed = null) {
1154 changed = false;
1155 var type = orig_type;
1157 if (metadata.has_argument (ArgumentType.TYPE)) {
1158 var new_type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1159 changed = true;
1160 return new_type;
1163 if (type is VoidType) {
1164 return type;
1167 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1168 type.remove_all_type_arguments ();
1169 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1172 if (metadata.get_bool (ArgumentType.ARRAY)) {
1173 type = new ArrayType (type, 1, type.source_reference);
1174 changed = true;
1177 if (owned_by_default) {
1178 if (metadata.has_argument (ArgumentType.UNOWNED)) {
1179 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED);
1181 } else {
1182 if (metadata.has_argument (ArgumentType.OWNED)) {
1183 type.value_owned = metadata.get_bool (ArgumentType.OWNED);
1186 if (metadata.has_argument (ArgumentType.NULLABLE)) {
1187 type.nullable = metadata.get_bool (ArgumentType.NULLABLE);
1190 return type;
1193 string? element_get_name (bool remap = false) {
1194 var name = reader.get_attribute ("name");
1195 var orig_name = name;
1196 var pattern = metadata.get_string (ArgumentType.NAME);
1197 if (pattern != null) {
1198 try {
1199 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1200 GLib.MatchInfo match;
1201 if (!regex.match (name, 0, out match)) {
1202 name = pattern;
1203 } else {
1204 var matched = match.fetch (1);
1205 if (matched != null && matched.length > 0) {
1206 name = matched;
1207 } else {
1208 name = pattern;
1211 } catch (Error e) {
1212 name = pattern;
1214 } else {
1215 if (name != null && name.has_suffix ("Enum")) {
1216 name = name.substring (0, name.length - "Enum".length);
1219 if (name != orig_name && remap) {
1220 set_symbol_mapping (parse_symbol_from_string (orig_name), parse_symbol_from_string (name));
1223 return name;
1226 void parse_repository () {
1227 start_element ("repository");
1228 if (reader.get_attribute ("version") != GIR_VERSION) {
1229 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1230 return;
1232 next ();
1233 while (current_token == MarkupTokenType.START_ELEMENT) {
1234 if (reader.name == "namespace") {
1235 var ns = parse_namespace ();
1236 if (ns != null) {
1237 context.root.add_namespace (ns);
1239 } else if (reader.name == "include") {
1240 parse_include ();
1241 } else if (reader.name == "package") {
1242 var pkg = parse_package ();
1243 if (context.has_package (pkg)) {
1244 // package already provided elsewhere, stop parsing this GIR
1245 return;
1246 } else {
1247 context.add_package (pkg);
1249 } else if (reader.name == "c:include") {
1250 parse_c_include ();
1251 } else {
1252 // error
1253 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1254 skip_element ();
1257 end_element ("repository");
1260 void parse_include () {
1261 start_element ("include");
1262 var pkg = reader.get_attribute ("name");
1263 var version = reader.get_attribute ("version");
1264 if (version != null) {
1265 pkg = "%s-%s".printf (pkg, version);
1267 // add the package to the queue
1268 context.add_external_package (pkg);
1269 next ();
1270 end_element ("include");
1273 string parse_package () {
1274 start_element ("package");
1275 var pkg = reader.get_attribute ("name");
1276 next ();
1277 end_element ("package");
1278 return pkg;
1281 void parse_c_include () {
1282 start_element ("c:include");
1283 cheader_filenames += reader.get_attribute ("name");
1284 next ();
1285 end_element ("c:include");
1288 void skip_element () {
1289 next ();
1291 int level = 1;
1292 while (level > 0) {
1293 if (current_token == MarkupTokenType.START_ELEMENT) {
1294 level++;
1295 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1296 level--;
1297 } else if (current_token == MarkupTokenType.EOF) {
1298 Report.error (get_current_src (), "unexpected end of file");
1299 break;
1301 next ();
1305 Namespace? parse_namespace () {
1306 start_element ("namespace");
1308 bool new_namespace = false;
1309 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
1310 string namespace_name = cprefix;
1311 string gir_namespace = reader.get_attribute ("name");
1312 string gir_version = reader.get_attribute ("version");
1313 if (namespace_name == null) {
1314 namespace_name = gir_namespace;
1316 current_source_file.gir_namespace = gir_namespace;
1317 current_source_file.gir_version = gir_version;
1319 var ns_metadata = metadata.match_child (gir_namespace);
1320 if (ns_metadata.has_argument (ArgumentType.NAME)) {
1321 namespace_name = ns_metadata.get_string (ArgumentType.NAME);
1324 var ns = context.root.scope.lookup (namespace_name) as Namespace;
1325 if (ns == null) {
1326 ns = new Namespace (namespace_name, get_current_src ());
1327 new_namespace = true;
1328 } else {
1329 if (ns.external_package) {
1330 ns.attributes = null;
1331 ns.source_reference = get_current_src ();
1335 if (gir_namespace != ns.name) {
1336 set_symbol_mapping (new UnresolvedSymbol (null, gir_namespace), ns);
1339 if (cprefix != null) {
1340 ns.add_cprefix (cprefix);
1341 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
1344 if (ns_metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1345 var val = ns_metadata.get_string (ArgumentType.CHEADER_FILENAME);
1346 foreach (string filename in val.split (",")) {
1347 ns.add_cheader_filename (filename);
1349 } else {
1350 foreach (string c_header in cheader_filenames) {
1351 ns.add_cheader_filename (c_header);
1355 next ();
1356 var current_namespace_methods = namespace_methods[ns];
1357 if (current_namespace_methods == null) {
1358 current_namespace_methods = new ArrayList<Method> ();
1359 namespace_methods[ns] = current_namespace_methods;
1361 var old_symbols_info = current_symbols_info;
1362 var old_symbol = current_symbol;
1363 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1364 current_symbol = ns;
1365 gtype_callbacks = new HashMap<UnresolvedSymbol,ArrayList<Delegate>> (unresolved_symbol_hash, unresolved_symbol_equal);
1366 while (current_token == MarkupTokenType.START_ELEMENT) {
1367 if (!push_metadata ()) {
1368 skip_element ();
1369 continue;
1372 if (reader.name == "alias") {
1373 var alias = parse_alias ();
1374 aliases.add (alias);
1375 } else if (reader.name == "enumeration") {
1376 if (reader.get_attribute ("glib:error-quark") != null) {
1377 add_symbol_info (parse_error_domain ());
1378 } else {
1379 add_symbol_info (parse_enumeration ());
1381 } else if (reader.name == "bitfield") {
1382 add_symbol_info (parse_bitfield ());
1383 } else if (reader.name == "function") {
1384 var method = parse_method ("function");
1385 add_symbol_info (method);
1386 current_namespace_methods.add (method);
1387 } else if (reader.name == "callback") {
1388 add_symbol_info (parse_callback ());
1389 } else if (reader.name == "record") {
1390 if (reader.get_attribute ("glib:get-type") != null) {
1391 add_symbol_info (parse_boxed ("record"));
1392 } else {
1393 add_symbol_info (parse_record ());
1395 } else if (reader.name == "class") {
1396 add_symbol_info (parse_class ());
1397 } else if (reader.name == "interface") {
1398 var iface = parse_interface ();
1399 add_symbol_info (iface);
1400 interfaces.add (iface);
1401 } else if (reader.name == "glib:boxed") {
1402 add_symbol_info (parse_boxed ("glib:boxed"));
1403 } else if (reader.name == "union") {
1404 add_symbol_info (parse_union ());
1405 } else if (reader.name == "constant") {
1406 add_symbol_info (parse_constant ());
1407 } else {
1408 // error
1409 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
1410 skip_element ();
1413 pop_metadata ();
1415 end_element ("namespace");
1417 merge_add_process (ns);
1418 current_symbols_info = old_symbols_info;
1419 current_symbol = old_symbol;
1420 postprocess_gtype_callbacks (ns);
1422 if (!new_namespace) {
1423 ns = null;
1426 return ns;
1429 Alias parse_alias () {
1430 // alias has no type information
1431 start_element ("alias");
1432 var alias = new Alias ();
1433 alias.source_reference = get_current_src ();
1434 alias.name = reader.get_attribute ("name");
1435 alias.parent_symbol = current_symbol;
1436 next ();
1438 alias.base_type = element_get_type (parse_type (null, null, true), true);
1440 end_element ("alias");
1441 return alias;
1444 private void calculate_common_prefix (ref string common_prefix, string cname) {
1445 if (common_prefix == null) {
1446 common_prefix = cname;
1447 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
1448 // FIXME: could easily be made faster
1449 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1451 } else {
1452 while (!cname.has_prefix (common_prefix)) {
1453 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1456 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
1457 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
1458 // enum values may not consist solely of digits
1459 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1463 Enum parse_enumeration () {
1464 start_element ("enumeration");
1466 var en = new Enum (element_get_name (), get_current_src ());
1467 en.access = SymbolAccessibility.PUBLIC;
1469 string enum_cname = reader.get_attribute ("c:type");
1470 if (enum_cname != null) {
1471 en.set_cname (enum_cname);
1474 next ();
1476 string common_prefix = null;
1478 var old_symbol = current_symbol;
1479 current_symbol = en;
1480 while (current_token == MarkupTokenType.START_ELEMENT) {
1481 if (!push_metadata ()) {
1482 skip_element ();
1483 continue;
1486 if (reader.name == "member") {
1487 var ev = parse_enumeration_member ();
1488 en.add_value (ev);
1489 calculate_common_prefix (ref common_prefix, ev.get_cname ());
1490 } else {
1491 // error
1492 Report.error (get_current_src (), "unknown child element `%s' in `enumaration'".printf (reader.name));
1493 skip_element ();
1496 pop_metadata ();
1499 en.set_cprefix (common_prefix);
1501 end_element ("enumeration");
1502 current_symbol = old_symbol;
1503 return en;
1506 ErrorDomain parse_error_domain () {
1507 start_element ("enumeration");
1509 var ed = new ErrorDomain (element_get_name (true), get_current_src ());
1510 ed.access = SymbolAccessibility.PUBLIC;
1512 string enum_cname = reader.get_attribute ("c:type");
1513 if (enum_cname != null) {
1514 ed.set_cname (enum_cname);
1517 next ();
1519 string common_prefix = null;
1520 var old_symbol = current_symbol;
1521 current_symbol = ed;
1522 while (current_token == MarkupTokenType.START_ELEMENT) {
1523 if (!push_metadata ()) {
1524 skip_element ();
1525 continue;
1528 if (reader.name == "member") {
1529 ErrorCode ec = parse_error_member ();
1530 ed.add_code (ec);
1531 calculate_common_prefix (ref common_prefix, ec.get_cname ());
1532 } else {
1533 // error
1534 Report.error (get_current_src (), "unknown child element `%s' in `enumeration'".printf (reader.name));
1535 skip_element ();
1538 pop_metadata ();
1541 ed.set_cprefix (common_prefix);
1543 end_element ("enumeration");
1544 current_symbol = old_symbol;
1545 return ed;
1548 Enum parse_bitfield () {
1549 start_element ("bitfield");
1550 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1551 en.access = SymbolAccessibility.PUBLIC;
1552 next ();
1553 var old_symbol = current_symbol;
1554 current_symbol = en;
1555 while (current_token == MarkupTokenType.START_ELEMENT) {
1556 if (!push_metadata ()) {
1557 skip_element ();
1558 continue;
1561 if (reader.name == "member") {
1562 en.add_value (parse_enumeration_member ());
1563 } else {
1564 // error
1565 Report.error (get_current_src (), "unknown child element `%s' in `bitfield'".printf (reader.name));
1566 skip_element ();
1569 pop_metadata ();
1571 end_element ("bitfield");
1572 current_symbol = en;
1573 return en;
1576 EnumValue parse_enumeration_member () {
1577 start_element ("member");
1578 var ev = new EnumValue (reader.get_attribute ("name").up ().replace ("-", "_"), null, get_current_src ());
1579 ev.set_cname (reader.get_attribute ("c:identifier"));
1580 next ();
1581 end_element ("member");
1582 return ev;
1585 ErrorCode parse_error_member () {
1586 start_element ("member");
1588 ErrorCode ec;
1589 string name = reader.get_attribute ("name").up ().replace ("-", "_");
1590 string value = reader.get_attribute ("value");
1591 if (value != null) {
1592 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
1593 } else {
1594 ec = new ErrorCode (name);
1597 next ();
1598 end_element ("member");
1599 return ec;
1602 DataType parse_return_value (out string? ctype = null) {
1603 start_element ("return-value");
1604 string transfer = reader.get_attribute ("transfer-ownership");
1605 string allow_none = reader.get_attribute ("allow-none");
1606 next ();
1607 var transfer_elements = transfer == "full";
1608 var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
1609 if (transfer == "full" || transfer == "container") {
1610 type.value_owned = true;
1612 if (allow_none == "1") {
1613 type.nullable = true;
1615 end_element ("return-value");
1616 return type;
1619 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) {
1620 Parameter param;
1622 if (&array_length_idx != null) {
1623 array_length_idx = -1;
1625 if (&closure_idx != null) {
1626 closure_idx = -1;
1628 if (&destroy_idx != null) {
1629 destroy_idx = -1;
1632 start_element ("parameter");
1633 string name = reader.get_attribute ("name");
1634 if (name == null) {
1635 name = default_name;
1637 string direction = null;
1638 if (metadata.has_argument (ArgumentType.OUT)) {
1639 if (metadata.get_bool (ArgumentType.OUT)) {
1640 direction = "out";
1641 } // null otherwise
1642 } else if (metadata.has_argument (ArgumentType.REF)) {
1643 if (metadata.get_bool (ArgumentType.REF)) {
1644 direction = "inout";
1645 } // null otherwise
1646 } else {
1647 direction = reader.get_attribute ("direction");
1649 string transfer = reader.get_attribute ("transfer-ownership");
1650 string allow_none = reader.get_attribute ("allow-none");
1652 if (&scope != null) {
1653 scope = reader.get_attribute ("scope");
1656 string closure = reader.get_attribute ("closure");
1657 string destroy = reader.get_attribute ("destroy");
1658 if (closure != null && &closure_idx != null) {
1659 closure_idx = int.parse (closure);
1661 if (destroy != null && &destroy_idx != null) {
1662 destroy_idx = int.parse (destroy);
1665 next ();
1666 if (reader.name == "varargs") {
1667 start_element ("varargs");
1668 next ();
1669 param = new Parameter.with_ellipsis (get_current_src ());
1670 end_element ("varargs");
1671 } else {
1672 string ctype;
1673 var type = parse_type (out ctype, out array_length_idx, transfer == "full");
1674 bool changed;
1675 type = element_get_type (type, false, out changed);
1676 if (!changed) {
1677 // discard ctype, duplicated information
1678 ctype = null;
1681 if (type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_POS)) {
1682 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_POS);
1685 if (transfer == "full" || transfer == "container" || destroy != null) {
1686 type.value_owned = true;
1688 if (allow_none == "1") {
1689 type.nullable = true;
1691 param = new Parameter (name, type, get_current_src ());
1692 param.ctype = ctype;
1693 if (direction == "out") {
1694 param.direction = ParameterDirection.OUT;
1695 } else if (direction == "inout") {
1696 param.direction = ParameterDirection.REF;
1698 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
1700 end_element ("parameter");
1701 return param;
1704 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) {
1705 bool is_array = false;
1706 string type_name = reader.get_attribute ("name");
1708 if (&array_length_index != null) {
1709 array_length_index = -1;
1712 if (reader.name == "array") {
1713 is_array = true;
1714 start_element ("array");
1716 if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
1717 if (reader.get_attribute ("length") != null
1718 && &array_length_index != null) {
1719 array_length_index = int.parse (reader.get_attribute ("length"));
1721 next ();
1722 var element_type = parse_type ();
1723 end_element ("array");
1724 return new ArrayType (element_type, 1, null);
1726 } else if (reader.name == "callback"){
1727 var callback = parse_callback ();
1728 return new DelegateType (callback);
1729 } else {
1730 start_element ("type");
1733 if (&ctype != null) {
1734 ctype = reader.get_attribute("c:type");
1737 next ();
1739 if (type_name == "GLib.PtrArray"
1740 && current_token == MarkupTokenType.START_ELEMENT) {
1741 type_name = "GLib.GenericArray";
1744 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
1746 // type arguments / element types
1747 while (current_token == MarkupTokenType.START_ELEMENT) {
1748 var element_type = parse_type ();
1749 element_type.value_owned = transfer_elements;
1750 type.add_type_argument (element_type);
1753 end_element (is_array ? "array" : "type");
1754 return type;
1757 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
1758 if (&no_array_length != null) {
1759 no_array_length = false;
1761 if (&array_null_terminated != null) {
1762 array_null_terminated = false;
1765 DataType type;
1766 if (type_name == "none") {
1767 type = new VoidType (get_current_src ());
1768 } else if (type_name == "gpointer") {
1769 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
1770 } else if (type_name == "GObject.Strv") {
1771 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, get_current_src ());
1772 if (&no_array_length != null) {
1773 no_array_length = true;
1775 if (&array_null_terminated != null) {
1776 array_null_terminated = true;
1778 } else {
1779 bool known_type = true;
1780 if (type_name == "utf8") {
1781 type_name = "string";
1782 } else if (type_name == "gboolean") {
1783 type_name = "bool";
1784 } else if (type_name == "gchar") {
1785 type_name = "char";
1786 } else if (type_name == "gshort") {
1787 type_name = "short";
1788 } else if (type_name == "gushort") {
1789 type_name = "ushort";
1790 } else if (type_name == "gint") {
1791 type_name = "int";
1792 } else if (type_name == "guint") {
1793 type_name = "uint";
1794 } else if (type_name == "glong") {
1795 if (ctype == "gssize") {
1796 type_name = "ssize_t";
1797 } else {
1798 type_name = "long";
1800 } else if (type_name == "gulong") {
1801 if (ctype == "gsize") {
1802 type_name = "size_t";
1803 } else {
1804 type_name = "ulong";
1806 } else if (type_name == "gint8") {
1807 type_name = "int8";
1808 } else if (type_name == "guint8") {
1809 type_name = "uint8";
1810 } else if (type_name == "gint16") {
1811 type_name = "int16";
1812 } else if (type_name == "guint16") {
1813 type_name = "uint16";
1814 } else if (type_name == "gint32") {
1815 type_name = "int32";
1816 } else if (type_name == "guint32") {
1817 type_name = "uint32";
1818 } else if (type_name == "gint64") {
1819 type_name = "int64";
1820 } else if (type_name == "guint64") {
1821 type_name = "uint64";
1822 } else if (type_name == "gfloat") {
1823 type_name = "float";
1824 } else if (type_name == "gdouble") {
1825 type_name = "double";
1826 } else if (type_name == "filename") {
1827 type_name = "string";
1828 } else if (type_name == "GLib.offset") {
1829 type_name = "int64";
1830 } else if (type_name == "gsize") {
1831 type_name = "size_t";
1832 } else if (type_name == "gssize") {
1833 type_name = "ssize_t";
1834 } else if (type_name == "GType") {
1835 type_name = "GLib.Type";
1836 } else if (type_name == "GLib.String") {
1837 type_name = "GLib.StringBuilder";
1838 } else if (type_name == "GObject.Class") {
1839 type_name = "GLib.ObjectClass";
1840 } else if (type_name == "GLib.unichar") {
1841 type_name = "unichar";
1842 } else if (type_name == "GLib.Data") {
1843 type_name = "GLib.Datalist";
1844 } else if (type_name == "Atk.ImplementorIface") {
1845 type_name = "Atk.Implementor";
1846 } else {
1847 known_type = false;
1849 var sym = parse_symbol_from_string (type_name, get_current_src ());
1850 type = new UnresolvedType.from_symbol (sym, get_current_src ());
1851 if (!known_type) {
1852 unresolved_gir_symbols.add (sym);
1856 return type;
1859 Struct parse_record () {
1860 start_element ("record");
1861 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
1862 st.external = true;
1863 st.access = SymbolAccessibility.PUBLIC;
1865 string cname = reader.get_attribute ("c:type");
1866 if (cname != null) {
1867 st.set_cname (cname);
1870 current_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
1871 if (current_gtype_struct_for != null) {
1872 girdata["glib:is-gtype-struct-for"] = current_gtype_struct_for;
1875 next ();
1876 var old_symbols_info = current_symbols_info;
1877 var old_symbol = current_symbol;
1878 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1879 current_symbol = st;
1880 while (current_token == MarkupTokenType.START_ELEMENT) {
1881 if (!push_metadata ()) {
1882 skip_element ();
1883 continue;
1886 if (reader.name == "field") {
1887 if (reader.get_attribute ("name") != "priv") {
1888 add_symbol_info (parse_field ());
1889 } else {
1890 skip_element ();
1892 } else if (reader.name == "constructor") {
1893 parse_constructor ();
1894 } else if (reader.name == "method") {
1895 add_symbol_info (parse_method ("method"));
1896 } else if (reader.name == "union") {
1897 Struct s = parse_union ();
1898 var s_fields = s.get_fields ();
1899 foreach (var f in s_fields) {
1900 f.set_cname (s.get_cname () + "." + f.get_cname ());
1901 f.name = s.name + "_" + f.name;
1902 st.add_field (f);
1904 } else {
1905 // error
1906 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
1907 skip_element ();
1910 pop_metadata ();
1912 end_element ("record");
1914 merge_add_process (st);
1915 current_symbols_info = old_symbols_info;
1916 current_symbol = old_symbol;
1917 current_gtype_struct_for = null;
1919 return st;
1922 Class parse_class () {
1923 start_element ("class");
1924 var name = element_get_name ();
1925 string cname = reader.get_attribute ("c:type");
1926 string parent = reader.get_attribute ("parent");
1927 var cl = current_symbol.scope.lookup (name) as Class;
1928 if (cl == null) {
1929 cl = new Class (name, get_current_src ());
1930 cl.access = SymbolAccessibility.PUBLIC;
1931 cl.external = true;
1933 if (cname != null) {
1934 cl.set_cname (cname);
1937 if (parent != null) {
1938 cl.add_base_type (parse_type_from_gir_name (parent));
1941 next ();
1942 var first_field = true;
1943 var old_symbols_info = current_symbols_info;
1944 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1945 while (current_token == MarkupTokenType.START_ELEMENT) {
1946 if (!push_metadata ()) {
1947 skip_element ();
1948 continue;
1951 if (reader.name == "implements") {
1952 start_element ("implements");
1953 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
1954 next ();
1955 end_element ("implements");
1956 } else if (reader.name == "constant") {
1957 add_symbol_info (parse_constant ());
1958 } else if (reader.name == "field") {
1959 if (first_field && parent != null) {
1960 // first field is guaranteed to be the parent instance
1961 skip_element ();
1962 } else {
1963 var field = parse_field ();
1964 if (field.name != "priv") {
1965 add_symbol_info (field);
1968 first_field = false;
1969 } else if (reader.name == "property") {
1970 add_symbol_info (parse_property ());
1971 } else if (reader.name == "constructor") {
1972 add_symbol_info (parse_constructor (cname));
1973 } else if (reader.name == "function") {
1974 add_symbol_info (parse_method ("function"));
1975 } else if (reader.name == "method") {
1976 add_symbol_info (parse_method ("method"));
1977 } else if (reader.name == "virtual-method") {
1978 add_symbol_info (parse_method ("virtual-method"));
1979 } else if (reader.name == "union") {
1980 Struct s = parse_union ();
1981 var s_fields = s.get_fields ();
1982 foreach (var f in s_fields) {
1983 f.set_cname (s.get_cname () + "." + f.get_cname ());
1984 f.name = s.name + "_" + f.name;
1985 add_symbol_info (f);
1987 } else if (reader.name == "glib:signal") {
1988 add_symbol_info (parse_signal ());
1989 } else {
1990 // error
1991 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
1992 skip_element ();
1995 pop_metadata ();
1998 merge_add_process (cl);
1999 current_symbols_info = old_symbols_info;
2001 handle_async_methods (cl);
2003 end_element ("class");
2004 return cl;
2007 Interface parse_interface () {
2008 start_element ("interface");
2009 var iface = new Interface (element_get_name (), get_current_src ());
2010 iface.access = SymbolAccessibility.PUBLIC;
2011 iface.external = true;
2013 string cname = reader.get_attribute ("c:type");
2014 if (cname != null) {
2015 iface.set_cname (cname);
2018 next ();
2019 var old_symbol = current_symbol;
2020 var old_symbols_info = current_symbols_info;
2021 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2022 current_symbol = iface;
2023 while (current_token == MarkupTokenType.START_ELEMENT) {
2024 if (!push_metadata ()) {
2025 skip_element ();
2026 continue;
2029 if (reader.name == "prerequisite") {
2030 start_element ("prerequisite");
2031 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2032 next ();
2033 end_element ("prerequisite");
2034 } else if (reader.name == "field") {
2035 add_symbol_info (parse_field ());
2036 } else if (reader.name == "property") {
2037 add_symbol_info (parse_property ());
2038 } else if (reader.name == "virtual-method") {
2039 add_symbol_info (parse_method ("virtual-method"));
2040 } else if (reader.name == "function") {
2041 add_symbol_info (parse_method ("function"));
2042 } else if (reader.name == "method") {
2043 add_symbol_info (parse_method ("method"));
2044 } else if (reader.name == "glib:signal") {
2045 add_symbol_info (parse_signal ());
2046 } else {
2047 // error
2048 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2049 skip_element ();
2052 pop_metadata ();
2055 merge_add_process (iface);
2056 current_symbol = old_symbol;
2057 current_symbols_info = old_symbols_info;
2059 handle_async_methods (iface);
2061 end_element ("interface");
2062 return iface;
2065 void handle_async_methods (ObjectTypeSymbol type_symbol) {
2066 var methods = type_symbol.get_methods ();
2067 for (int method_n = 0 ; method_n < methods.size ; method_n++) {
2068 var m = methods.get (method_n);
2070 if (m.coroutine) {
2071 string finish_method_base;
2072 if (m.name.has_suffix ("_async")) {
2073 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
2074 } else {
2075 finish_method_base = m.name;
2077 var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method;
2079 // check if the method is using non-standard finish method name
2080 if (finish_method == null) {
2081 var method_cname = m.get_finish_cname ();
2082 foreach (Method method in type_symbol.get_methods ()) {
2083 if (method.get_cname () == method_cname) {
2084 finish_method = method;
2085 break;
2090 if (finish_method != null) {
2091 m.return_type = finish_method.return_type.copy ();
2092 m.no_array_length = finish_method.no_array_length;
2093 m.array_null_terminated = finish_method.array_null_terminated;
2094 foreach (var param in finish_method.get_parameters ()) {
2095 if (param.direction == ParameterDirection.OUT) {
2096 var async_param = param.copy ();
2097 if (m.scope.lookup (param.name) != null) {
2098 // parameter name conflict
2099 async_param.name += "_out";
2101 m.add_parameter (async_param);
2104 foreach (DataType error_type in finish_method.get_error_types ()) {
2105 m.add_error_type (error_type.copy ());
2107 if (methods.index_of (finish_method) < method_n) {
2108 method_n--;
2110 type_symbol.scope.remove (finish_method.name);
2111 methods.remove (finish_method);
2117 Field parse_field () {
2118 start_element ("field");
2119 string name = reader.get_attribute ("name");
2120 string allow_none = reader.get_attribute ("allow-none");
2121 next ();
2122 var type = parse_type ();
2123 type = element_get_type (type, true);
2124 if (type is DelegateType && current_gtype_struct_for != null) {
2125 // virtual
2126 var gtype_struct_for = parse_symbol_from_string (current_gtype_struct_for);
2127 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2128 if (callbacks == null) {
2129 callbacks = new ArrayList<Delegate> ();
2130 gtype_callbacks.set (gtype_struct_for, callbacks);
2132 callbacks.add (((DelegateType) type).delegate_symbol);
2134 var field = new Field (name, type, null, get_current_src ());
2135 field.access = SymbolAccessibility.PUBLIC;
2136 field.no_array_length = true;
2137 if (allow_none == "1") {
2138 type.nullable = true;
2140 end_element ("field");
2141 return field;
2144 Property parse_property () {
2145 start_element ("property");
2146 string name = reader.get_attribute ("name").replace ("-", "_");
2147 string readable = reader.get_attribute ("readable");
2148 string writable = reader.get_attribute ("writable");
2149 string construct_ = reader.get_attribute ("construct");
2150 string construct_only = reader.get_attribute ("construct-only");
2151 next ();
2152 bool no_array_length;
2153 bool array_null_terminated;
2154 var type = parse_type (null, null, false, out no_array_length, out array_null_terminated);
2155 var prop = new Property (name, type, null, null, get_current_src ());
2156 prop.access = SymbolAccessibility.PUBLIC;
2157 prop.external = true;
2158 prop.no_accessor_method = true;
2159 prop.no_array_length = no_array_length;
2160 prop.array_null_terminated = array_null_terminated;
2161 if (readable != "0") {
2162 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
2164 if (writable == "1" || construct_only == "1") {
2165 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
2167 end_element ("property");
2168 return prop;
2171 Delegate parse_callback () {
2172 return this.parse_function ("callback") as Delegate;
2175 Method parse_constructor (string? parent_ctype = null) {
2176 start_element ("constructor");
2178 string throws_string = reader.get_attribute ("throws");
2179 string cname = reader.get_attribute ("c:identifier");
2180 var m = new CreationMethod (null, element_get_name (), get_current_src ());
2181 m.access = SymbolAccessibility.PUBLIC;
2182 m.has_construct_function = false;
2184 if (m.name == "new") {
2185 m.name = null;
2186 } else if (m.name.has_prefix ("new_")) {
2187 m.name = m.name.substring ("new_".length);
2189 if (cname != null) {
2190 m.set_cname (cname);
2193 next ();
2194 string? ctype;
2195 parse_return_value (out ctype);
2196 if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
2197 m.custom_return_type_cname = ctype;
2200 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2201 start_element ("parameters");
2202 next ();
2203 while (current_token == MarkupTokenType.START_ELEMENT) {
2204 if (!push_metadata ()) {
2205 skip_element ();
2206 continue;
2209 m.add_parameter (parse_parameter ());
2211 pop_metadata ();
2213 end_element ("parameters");
2216 if (throws_string == "1") {
2217 m.add_error_type (new ErrorType (null, null));
2219 end_element ("constructor");
2220 return m;
2223 class MethodInfo {
2224 public MethodInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx) {
2225 this.param = param;
2226 this.array_length_idx = array_length_idx;
2227 this.closure_idx = closure_idx;
2228 this.destroy_idx = destroy_idx;
2229 this.vala_idx = 0.0F;
2230 this.keep = true;
2233 public Parameter param;
2234 public float vala_idx;
2235 public int array_length_idx;
2236 public int closure_idx;
2237 public int destroy_idx;
2238 public bool keep;
2241 Symbol parse_function (string element_name) {
2242 start_element (element_name);
2243 string name = element_get_name ();
2244 string cname = reader.get_attribute ("c:identifier");
2245 string throws_string = reader.get_attribute ("throws");
2246 string invoker = reader.get_attribute ("invoker");
2247 next ();
2248 DataType return_type;
2249 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2250 return_type = parse_return_value ();
2251 } else {
2252 return_type = new VoidType ();
2254 return_type = element_get_type (return_type, true);
2256 Symbol s;
2258 if (element_name == "callback") {
2259 s = new Delegate (name, return_type, get_current_src ());
2260 } else {
2261 s = new Method (name, return_type, get_current_src ());
2264 s.access = SymbolAccessibility.PUBLIC;
2265 if (cname != null) {
2266 if (s is Method) {
2267 ((Method) s).set_cname (cname);
2268 } else {
2269 ((Delegate) s).set_cname (cname);
2273 s.external = true;
2275 if (element_name == "virtual-method" || element_name == "callback") {
2276 if (s is Method) {
2277 ((Method) s).is_virtual = true;
2278 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
2279 s.attributes.append (new Attribute ("NoWrapper", s.source_reference));
2283 if (invoker != null) {
2284 s.name = invoker;
2286 } else if (element_name == "function") {
2287 ((Method) s).binding = MemberBinding.STATIC;
2290 if (s is Method) {
2291 var method = (Method) s;
2292 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
2293 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
2294 method.is_abstract = false;
2295 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
2296 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
2297 method.is_virtual = false;
2299 method.vfunc_name = metadata.get_string (ArgumentType.VFUNC_NAME);
2302 var parameters = new ArrayList<MethodInfo> ();
2303 var array_length_parameters = new ArrayList<int> ();
2304 var closure_parameters = new ArrayList<int> ();
2305 var destroy_parameters = new ArrayList<int> ();
2306 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2307 start_element ("parameters");
2308 next ();
2310 while (current_token == MarkupTokenType.START_ELEMENT) {
2311 if (!push_metadata ()) {
2312 skip_element ();
2313 continue;
2316 int array_length_idx, closure_idx, destroy_idx;
2317 string scope;
2318 string default_param_name = null;
2319 default_param_name = "arg%d".printf (parameters.size);
2320 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, default_param_name);
2321 if (array_length_idx != -1) {
2322 array_length_parameters.add (array_length_idx);
2324 if (closure_idx != -1) {
2325 closure_parameters.add (closure_idx);
2327 if (destroy_idx != -1) {
2328 destroy_parameters.add (destroy_idx);
2331 var info = new MethodInfo(param, array_length_idx, closure_idx, destroy_idx);
2333 if (s is Method && scope == "async") {
2334 var unresolved_type = param.variable_type as UnresolvedType;
2335 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
2336 // GAsync-style method
2337 ((Method) s).coroutine = true;
2338 info.keep = false;
2342 parameters.add (info);
2343 pop_metadata ();
2345 end_element ("parameters");
2347 int i = 0, j=1;
2349 int last = -1;
2350 foreach (MethodInfo info in parameters) {
2351 if (s is Delegate && info.closure_idx == i) {
2352 var d = (Delegate) s;
2353 d.has_target = true;
2354 d.cinstance_parameter_position = (float) j - 0.1;
2355 info.keep = false;
2356 } else if (info.keep
2357 && !array_length_parameters.contains (i)
2358 && !closure_parameters.contains (i)
2359 && !destroy_parameters.contains (i)) {
2360 info.vala_idx = (float) j;
2361 info.keep = true;
2363 /* interpolate for vala_idx between this and last*/
2364 float last_idx = 0.0F;
2365 if (last != -1) {
2366 last_idx = parameters[last].vala_idx;
2368 for (int k=last+1; k < i; k++) {
2369 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
2371 last = i;
2372 j++;
2373 } else {
2374 info.keep = false;
2375 // make sure that vala_idx is always set
2376 // the above if branch does not set vala_idx for
2377 // hidden parameters at the end of the parameter list
2378 info.vala_idx = (j - 1) + (i - last) * 0.1F;
2380 i++;
2383 foreach (MethodInfo info in parameters) {
2384 if (info.keep) {
2386 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
2387 so do it first*/
2388 if (s is Method) {
2389 ((Method) s).add_parameter (info.param);
2390 } else {
2391 ((Delegate) s).add_parameter (info.param);
2394 if (info.array_length_idx != -1) {
2395 if ((info.array_length_idx) >= parameters.size) {
2396 Report.error (get_current_src (), "invalid array_length index");
2397 continue;
2399 info.param.carray_length_parameter_position = parameters[info.array_length_idx].vala_idx;
2400 info.param.set_array_length_cname (parameters[info.array_length_idx].param.name);
2402 if (info.param.variable_type is ArrayType && info.array_length_idx == -1) {
2403 info.param.no_array_length = true;
2406 if (info.closure_idx != -1) {
2407 if ((info.closure_idx) >= parameters.size) {
2408 Report.error (get_current_src (), "invalid closure index");
2409 continue;
2411 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
2413 if (info.destroy_idx != -1) {
2414 if (info.destroy_idx >= parameters.size) {
2415 Report.error (get_current_src (), "invalid destroy index");
2416 continue;
2418 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
2423 if (throws_string == "1") {
2424 s.add_error_type (new ErrorType (null, null));
2426 end_element (element_name);
2427 return s;
2430 Method parse_method (string element_name) {
2431 return this.parse_function (element_name) as Method;
2434 Signal parse_signal () {
2435 start_element ("glib:signal");
2436 string name = reader.get_attribute ("name").replace ("-", "_");
2437 next ();
2438 DataType return_type;
2439 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2440 return_type = parse_return_value ();
2441 } else {
2442 return_type = new VoidType ();
2444 var sig = new Signal (name, return_type, get_current_src ());
2445 sig.access = SymbolAccessibility.PUBLIC;
2446 sig.external = true;
2447 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2448 start_element ("parameters");
2449 next ();
2450 while (current_token == MarkupTokenType.START_ELEMENT) {
2451 if (!push_metadata ()) {
2452 skip_element ();
2453 continue;
2456 sig.add_parameter (parse_parameter ());
2458 pop_metadata ();
2460 end_element ("parameters");
2462 end_element ("glib:signal");
2463 return sig;
2466 Class parse_boxed (string element_name) {
2467 start_element (element_name);
2468 string name = reader.get_attribute ("name");
2469 if (name == null) {
2470 name = reader.get_attribute ("glib:name");
2472 var cl = new Class (name, get_current_src ());
2473 cl.access = SymbolAccessibility.PUBLIC;
2474 cl.external = true;
2475 cl.is_compact = true;
2477 string cname = reader.get_attribute ("c:type");
2478 if (cname != null) {
2479 cl.set_cname (cname);
2482 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
2483 cl.set_free_function ("g_boxed_free");
2484 cl.set_dup_function ("g_boxed_copy");
2486 next ();
2487 var old_symbols_info = current_symbols_info;
2488 var old_symbol = current_symbol;
2489 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
2490 current_symbol = cl;
2491 while (current_token == MarkupTokenType.START_ELEMENT) {
2492 if (!push_metadata ()) {
2493 skip_element ();
2494 continue;
2497 if (reader.name == "field") {
2498 add_symbol_info (parse_field ());
2499 } else if (reader.name == "constructor") {
2500 parse_constructor ();
2501 } else if (reader.name == "method") {
2502 add_symbol_info (parse_method ("method"));
2503 } else {
2504 // error
2505 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2506 skip_element ();
2509 pop_metadata ();
2511 end_element (element_name);
2513 merge_add_process (cl);
2514 current_symbols_info = old_symbols_info;
2515 current_symbol = old_symbol;
2517 return cl;
2520 Struct parse_union () {
2521 start_element ("union");
2522 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2523 st.access = SymbolAccessibility.PUBLIC;
2524 st.external = true;
2525 next ();
2527 var old_symbol = current_symbol;
2528 current_symbol = st;
2529 while (current_token == MarkupTokenType.START_ELEMENT) {
2530 if (!push_metadata ()) {
2531 skip_element ();
2532 continue;
2535 if (reader.name == "field") {
2536 st.add_field (parse_field ());
2537 } else if (reader.name == "constructor") {
2538 parse_constructor ();
2539 } else if (reader.name == "method") {
2540 st.add_method (parse_method ("method"));
2541 } else if (reader.name == "record") {
2542 Struct s = parse_record ();
2543 var fs = s.get_fields ();
2544 foreach (var f in fs) {
2545 f.set_cname (s.get_cname () + "." + f.get_cname ());
2546 f.name = s.name + "_" + f.name;
2547 st.add_field (f);
2549 } else {
2550 // error
2551 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
2552 skip_element ();
2555 pop_metadata ();
2558 end_element ("union");
2559 current_symbol = old_symbol;
2561 return st;
2564 Constant parse_constant () {
2565 start_element ("constant");
2566 string name = element_get_name ();
2567 next ();
2568 var type = parse_type ();
2569 var c = new Constant (name, type, null, get_current_src ());
2570 c.access = SymbolAccessibility.PUBLIC;
2571 c.external = true;
2572 end_element ("constant");
2573 return c;
2576 /* Reporting */
2577 void report_unused_metadata (Metadata metadata) {
2578 if (metadata == Metadata.empty) {
2579 return;
2582 if (metadata.args.size == 0 && metadata.children.size == 0) {
2583 Report.warning (metadata.source_reference, "empty metadata");
2584 return;
2587 foreach (var arg_type in metadata.args.get_keys ()) {
2588 var arg = metadata.args[arg_type];
2589 if (!arg.used) {
2590 // if metadata is used and argument is not, then it's a unexpected argument
2591 Report.warning (arg.source_reference, "argument never used");
2595 foreach (var child in metadata.children) {
2596 if (!child.used) {
2597 Report.warning (child.source_reference, "metadata never used");
2598 } else {
2599 report_unused_metadata (child);
2604 /* Post-parsing */
2606 void resolve_gir_symbols () {
2607 // we are remapping unresolved symbols, so create them from concrete symbols
2608 foreach (var map_from in concrete_symbols_map.get_keys ()) {
2609 unresolved_symbols_map[get_unresolved_symbol(map_from)] = concrete_symbols_map[map_from];
2612 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
2613 foreach (var map_from in unresolved_gir_symbols) {
2614 while (map_from != null) {
2615 var map_to = unresolved_symbols_map[map_from];
2616 if (map_to != null) {
2617 // remap the original symbol to match the target
2618 map_from.inner = null;
2619 map_from.name = map_to.name;
2620 if (map_to is UnresolvedSymbol) {
2621 var umap_to = (UnresolvedSymbol) map_to;
2622 while (umap_to.inner != null) {
2623 umap_to = umap_to.inner;
2624 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
2625 map_from = map_from.inner;
2627 } else {
2628 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
2629 map_to = map_to.parent_symbol;
2630 map_from.inner = new UnresolvedSymbol (null, map_to.name);
2631 map_from = map_from.inner;
2634 break;
2636 map_from = map_from.inner;
2641 Symbol? resolve_symbol (Scope parent_scope, UnresolvedSymbol unresolved_symbol) {
2642 // simple symbol resolver, enough for gir
2643 if (unresolved_symbol.inner == null) {
2644 var scope = parent_scope;
2645 while (scope != null) {
2646 var sym = scope.lookup (unresolved_symbol.name);
2647 if (sym != null) {
2648 return sym;
2650 scope = scope.parent_scope;
2652 } else {
2653 var inner = resolve_symbol (parent_scope, unresolved_symbol.inner);
2654 if (inner != null) {
2655 return inner.scope.lookup (unresolved_symbol.name);
2658 return null;
2661 void postprocess_interfaces () {
2662 foreach (var iface in interfaces) {
2663 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
2664 ensure we have at least one instantiable prerequisite */
2665 bool has_instantiable_prereq = false;
2666 foreach (DataType prereq in iface.get_prerequisites ()) {
2667 Symbol sym = null;
2668 if (prereq is UnresolvedType) {
2669 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
2670 sym = resolve_symbol (iface.parent_symbol.scope, unresolved_symbol);
2671 } else {
2672 sym = prereq.data_type;
2674 if (sym is Class) {
2675 has_instantiable_prereq = true;
2676 break;
2680 if (!has_instantiable_prereq) {
2681 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
2686 void postprocess_reparenting () {
2687 foreach (UnresolvedSymbol target_unresolved_symbol in symbol_reparent_map.get_keys ()) {
2688 var target_symbol = resolve_symbol (context.root.scope, target_unresolved_symbol);
2689 if (target_symbol == null) {
2690 // create namespaces backward
2691 var sym = target_unresolved_symbol;
2692 var ns = new Namespace (sym.name, sym.source_reference);
2693 var result = ns;
2694 sym = sym.inner;
2695 while (sym != null) {
2696 var res = resolve_symbol (context.root.scope, sym);
2697 if (res != null && !(res is Namespace)) {
2698 result = null;
2699 break;
2701 var parent = res as Namespace;
2702 if (res == null) {
2703 parent = new Namespace (sym.name, sym.source_reference);
2705 if (parent.scope.lookup (ns.name) == null) {
2706 parent.add_namespace (ns);
2708 ns = parent;
2709 sym = sym.inner;
2711 if (result != null && sym == null && context.root.scope.lookup (ns.name) == null) {
2712 // a new root namespace, helpful for a possible non-gobject gir?
2713 context.root.add_namespace (ns);
2715 target_symbol = result;
2717 if (target_symbol == null) {
2718 Report.error (null, "unable to reparent into `%s'".printf (target_unresolved_symbol.to_string ()));
2719 continue;
2721 var symbols = symbol_reparent_map[target_unresolved_symbol];
2722 foreach (var symbol in symbols) {
2723 add_symbol_to_container (target_symbol, symbol);
2728 void postprocess_gtype_callbacks (Namespace ns) {
2729 foreach (UnresolvedSymbol gtype_struct_for in gtype_callbacks.get_keys ()) {
2730 // parent symbol is the record, therefore use parent of parent symbol
2731 var gtype = resolve_symbol (ns.scope, gtype_struct_for) as ObjectTypeSymbol;
2732 if (gtype == null) {
2733 Report.error (null, "unknown symbol `%s' while postprocessing callbacks".printf (gtype_struct_for.name));
2734 continue;
2736 ArrayList<Delegate> callbacks = gtype_callbacks.get (gtype_struct_for);
2737 foreach (Delegate d in callbacks) {
2738 var symbol = gtype.scope.lookup (d.name);
2739 if (symbol == null) {
2740 continue;
2741 } else if (symbol is Method) {
2742 var meth = (Method) symbol;
2743 if (gtype is Class) {
2744 meth.is_virtual = true;
2745 } else if (gtype is Interface) {
2746 meth.is_abstract = true;
2748 } else if (symbol is Signal) {
2749 var sig = (Signal) symbol;
2750 sig.is_virtual = true;
2751 assume_parameter_names (sig, d);
2752 } else if (symbol is Property) {
2753 var prop = (Property) symbol;
2754 prop.is_virtual = true;
2755 } else {
2756 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (d.name, gtype.name));
2762 void postprocess_aliases () {
2763 /* this is unfortunate because <alias> tag has no type information, thus we have
2764 to guess it from the target */
2765 foreach (var alias in aliases) {
2766 DataType base_type = null;
2767 Symbol type_sym = null;
2768 if (alias.base_type is UnresolvedType) {
2769 base_type = alias.base_type;
2770 type_sym = resolve_symbol (alias.parent_symbol.scope, ((UnresolvedType) base_type).unresolved_symbol);
2771 } else if (!(alias.base_type is VoidType)) {
2772 base_type = alias.base_type;
2773 type_sym = base_type.data_type;
2776 if (base_type == null || type_sym == null || type_sym is Struct) {
2777 var st = new Struct (alias.name, alias.source_reference);
2778 st.access = SymbolAccessibility.PUBLIC;
2779 if (base_type != null) {
2780 // threat target="none" as a new struct
2781 st.base_type = base_type;
2783 st.external = true;
2784 add_symbol_to_container (alias.parent_symbol, st);
2785 } else if (type_sym is Class) {
2786 var cl = new Class (alias.name, alias.source_reference);
2787 cl.access = SymbolAccessibility.PUBLIC;
2788 if (base_type != null) {
2789 cl.add_base_type (base_type);
2791 cl.external = true;
2792 add_symbol_to_container (alias.parent_symbol, cl);
2797 void find_static_method_parent (string cname, Symbol current, ref Symbol best, ref double match, double match_char) {
2798 var old_best = best;
2799 if (current.scope.get_symbol_table () != null) {
2800 foreach (var child in current.scope.get_symbol_table().get_values ()) {
2801 if (child is Struct || child is ObjectTypeSymbol || child is Namespace) {
2802 find_static_method_parent (cname, child, ref best, ref match, match_char);
2806 if (best != old_best) {
2807 // child is better
2808 return;
2811 var current_cprefix = current.get_lower_case_cprefix ();
2812 if (cname.has_prefix (current_cprefix)) {
2813 var current_match = match_char * current_cprefix.length;
2814 if (current_match > match) {
2815 match = current_match;
2816 best = current;
2821 void postprocess_namespace_methods () {
2822 /* transform static methods into instance methods if possible.
2823 In most of cases this is a .gir fault we are going to fix */
2824 foreach (var ns in namespace_methods.get_keys ()) {
2825 var ns_cprefix = ns.get_lower_case_cprefix ();
2826 var methods = namespace_methods[ns];
2827 foreach (var method in methods) {
2828 if (method.parent_symbol != null) {
2829 // fixed earlier by metadata
2830 continue;
2833 var cname = method.get_cname ();
2835 Parameter first_param = null;
2836 if (method.get_parameters ().size > 0) {
2837 first_param = method.get_parameters()[0];
2839 if (first_param != null && first_param.variable_type is UnresolvedType) {
2840 // check if it's a missed instance method (often happens for structs)
2841 var parent = resolve_symbol (ns.scope, ((UnresolvedType) first_param.variable_type).unresolved_symbol);
2842 if (parent != null && (parent is Struct || parent is ObjectTypeSymbol || parent is Namespace)
2843 && cname.has_prefix (parent.get_lower_case_cprefix ())) {
2844 // instance method
2845 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2846 if (parent.scope.lookup (new_name) == null) {
2847 method.name = new_name;
2848 method.get_parameters().remove_at (0);
2849 method.binding = MemberBinding.INSTANCE;
2850 add_symbol_to_container (parent, method);
2851 } else {
2852 ns.add_method (method);
2854 continue;
2858 double match = 0;
2859 Symbol parent = ns;
2860 find_static_method_parent (cname, ns, ref parent, ref match, 1.0/cname.length);
2861 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2862 if (parent.scope.lookup (new_name) == null) {
2863 method.name = new_name;
2864 add_symbol_to_container (parent, method);
2865 } else {
2866 ns.add_method (method);
2872 /* Hash and equal functions */
2874 static uint unresolved_symbol_hash (void *ptr) {
2875 var sym = (UnresolvedSymbol) ptr;
2876 var builder = new StringBuilder ();
2877 while (sym != null) {
2878 builder.append (sym.name);
2879 sym = sym.inner;
2881 return builder.str.hash ();
2884 static bool unresolved_symbol_equal (void *ptr1, void *ptr2) {
2885 var sym1 = (UnresolvedSymbol) ptr1;
2886 var sym2 = (UnresolvedSymbol) ptr2;
2887 while (sym1 != sym2) {
2888 if (sym1 == null || sym2 == null) {
2889 return false;
2891 if (sym1.name != sym2.name) {
2892 return false;
2894 sym1 = sym1.inner;
2895 sym2 = sym2.inner;
2897 return true;