girparser: Do not add Object prerequisite before symbol resolution
[vala-lang.git] / vala / valagirparser.vala
blob5b95ff45f4fd3b22ae82aa74dbe35bec833fa1d0
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
36 * Best hacking practices:
37 * - Keep GIR parsing bloat-free, it must contain the logic
38 * - Prefer being clean / short over performance
39 * - Try to make things common as much as possible
40 * - Prefer replace/merge after parse rather than a bunch of if-then-else and hardcoding
41 * - Prefer postprocessing over hardcoding the parser
43 public class Vala.GirParser : CodeVisitor {
44 enum MetadataType {
45 GENERIC,
46 PROPERTY,
47 SIGNAL
50 enum ArgumentType {
51 SKIP,
52 HIDDEN,
53 TYPE,
54 TYPE_ARGUMENTS,
55 CHEADER_FILENAME,
56 NAME,
57 OWNED,
58 UNOWNED,
59 PARENT,
60 NULLABLE,
61 DEPRECATED,
62 REPLACEMENT,
63 DEPRECATED_SINCE,
64 ARRAY,
65 ARRAY_LENGTH_POS,
66 DEFAULT,
67 OUT,
68 REF;
70 public static ArgumentType? from_string (string name) {
71 var enum_class = (EnumClass) typeof(ArgumentType).class_ref ();
72 var nick = name.replace ("_", "-");
73 unowned GLib.EnumValue? enum_value = enum_class.get_value_by_nick (nick);
74 if (enum_value != null) {
75 ArgumentType value = (ArgumentType) enum_value.value;
76 return value;
78 return null;
82 class Argument {
83 public Expression expression;
84 public SourceReference source_reference;
86 public bool used = false;
88 public Argument (Expression expression, SourceReference? source_reference = null) {
89 this.expression = expression;
90 this.source_reference = source_reference;
94 class MetadataSet : Metadata {
95 public MetadataSet (MetadataType type) {
96 base ("", type);
99 public void add_sibling (Metadata metadata) {
100 foreach (var child in metadata.children) {
101 add_child (child);
103 // merge arguments and take precedence
104 foreach (var key in metadata.args.get_keys ()) {
105 args[key] = metadata.args[key];
110 class Metadata {
111 private static Metadata _empty = null;
112 public static Metadata empty {
113 get {
114 if (_empty == null) {
115 _empty = new Metadata ("");
117 return _empty;
121 public string pattern;
122 public PatternSpec pattern_spec;
123 public MetadataType type;
124 public SourceReference source_reference;
126 public bool used = false;
127 public Vala.Map<ArgumentType,Argument> args = new HashMap<ArgumentType,Argument> ();
128 public ArrayList<Metadata> children = new ArrayList<Metadata> ();
130 public Metadata (string pattern, MetadataType type = MetadataType.GENERIC, SourceReference? source_reference = null) {
131 this.pattern = pattern;
132 this.pattern_spec = new PatternSpec (pattern);
133 this.type = type;
134 this.source_reference = source_reference;
137 public void add_child (Metadata metadata) {
138 children.add (metadata);
141 public Metadata? get_child (string pattern, MetadataType type = MetadataType.GENERIC) {
142 foreach (var metadata in children) {
143 if (metadata.type == type && metadata.pattern == pattern) {
144 return metadata;
147 return null;
150 public Metadata match_child (string name, MetadataType type = MetadataType.GENERIC) {
151 var result = Metadata.empty;
152 foreach (var metadata in children) {
153 if (metadata.type == type && metadata.pattern_spec.match_string (name)) {
154 metadata.used = true;
155 if (result == Metadata.empty) {
156 // first match
157 result = metadata;
158 } else {
159 var ms = result as MetadataSet;
160 if (ms == null) {
161 // second match
162 ms = new MetadataSet (type);
163 ms.add_sibling (result);
165 ms.add_sibling (metadata);
166 result = ms;
170 return result;
173 public void add_argument (ArgumentType key, Argument value) {
174 args.set (key, value);
177 public bool has_argument (ArgumentType key) {
178 return args.contains (key);
181 public Expression? get_expression (ArgumentType arg) {
182 var val = args.get (arg);
183 if (val != null) {
184 val.used = true;
185 return val.expression;
187 return null;
190 public string? get_string (ArgumentType arg) {
191 var lit = get_expression (arg) as StringLiteral;
192 if (lit != null) {
193 return lit.eval ();
195 return null;
198 public int get_integer (ArgumentType arg) {
199 var lit = get_expression (arg) as IntegerLiteral;
200 if (lit != null) {
201 return lit.value.to_int ();
204 return 0;
207 public double get_double (ArgumentType arg) {
208 var expr = get_expression (arg);
209 if (expr is RealLiteral) {
210 var lit = (RealLiteral) expr;
211 return lit.value.to_double ();
212 } else if (expr is IntegerLiteral) {
213 var lit = (IntegerLiteral) expr;
214 return lit.value.to_int ();
216 return 0;
219 public bool get_bool (ArgumentType arg) {
220 var lit = get_expression (arg) as BooleanLiteral;
221 if (lit != null) {
222 return lit.value;
224 return false;
227 public SourceReference? get_source_reference (ArgumentType arg) {
228 var val = args.get (arg);
229 if (val != null) {
230 return val.source_reference;
232 return null;
236 class MetadataParser {
238 * Grammar:
239 * metadata ::= [ rule [ '\n' relativerule ]* ]
240 * rule ::= pattern ' ' [ args ]
241 * relativerule ::= [ access ] rule
242 * pattern ::= identifier [ access identifier ]*
243 * access ::= '.' | ':' | '::'
245 private Metadata tree = new Metadata ("");
246 private Scanner scanner;
247 private SourceLocation begin;
248 private SourceLocation end;
249 private SourceLocation old_end;
250 private TokenType current;
251 private Metadata parent_metadata;
253 public MetadataParser () {
254 tree.used = true;
257 SourceReference get_current_src () {
258 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
261 SourceReference get_src (SourceLocation begin) {
262 return new SourceReference (scanner.source_file, begin.line, begin.column, end.line, end.column);
265 public Metadata parse_metadata (SourceFile metadata_file) {
266 scanner = new Scanner (metadata_file);
267 next ();
268 while (current != TokenType.EOF) {
269 if (!parse_rule ()) {
270 return Metadata.empty;
273 return tree;
276 TokenType next () {
277 old_end = end;
278 current = scanner.read_token (out begin, out end);
279 return current;
282 bool has_space () {
283 return old_end.pos != begin.pos;
286 bool has_newline () {
287 return old_end.line != begin.line;
290 string get_string () {
291 return ((string) begin.pos).ndup ((end.pos - begin.pos));
294 MetadataType? parse_metadata_access () {
295 switch (current) {
296 case TokenType.DOT:
297 next ();
298 return MetadataType.GENERIC;
299 case TokenType.COLON:
300 if (next () == TokenType.COLON) {
301 next ();
302 return MetadataType.SIGNAL;
304 return MetadataType.PROPERTY;
305 default:
306 return null;
310 string? parse_identifier (out SourceReference source_reference, bool is_glob) {
311 var begin = this.begin;
312 var builder = new StringBuilder ();
313 do {
314 if (is_glob && current == TokenType.STAR) {
315 builder.append_c ('*');
316 } else {
317 string str = null;
318 switch (current) {
319 case TokenType.IDENTIFIER:
320 case TokenType.UNOWNED:
321 case TokenType.OWNED:
322 case TokenType.GET:
323 case TokenType.NEW:
324 case TokenType.DEFAULT:
325 case TokenType.OUT:
326 case TokenType.REF:
327 str = get_string ();
328 break;
330 if (str == null) {
331 break;
333 builder.append (str);
335 source_reference = get_src (begin);
336 next ();
337 } while (!has_space ());
339 if (builder.str == "") {
340 if (is_glob) {
341 Report.error (get_src (begin), "expected pattern");
342 } else {
343 Report.error (get_src (begin), "expected identifier");
345 return null;
347 return builder.str;
350 Metadata? parse_pattern () {
351 Metadata metadata;
352 bool is_relative = false;
353 MetadataType? type = MetadataType.GENERIC;
354 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
355 // absolute pattern
356 parent_metadata = tree;
357 } else {
358 // relative pattern
359 type = parse_metadata_access ();
360 is_relative = true;
363 if (type == null) {
364 Report.error (get_current_src (), "expected pattern, `.', `:' or `::'");
365 return null;
368 if (parent_metadata == null) {
369 Report.error (get_current_src (), "cannot determinate parent metadata");
370 return null;
373 SourceReference src;
374 var pattern = parse_identifier (out src, true);
375 if (pattern == null) {
376 return null;
378 metadata = parent_metadata.get_child (pattern, type);
379 if (metadata == null) {
380 metadata = new Metadata (pattern, type, src);
381 parent_metadata.add_child (metadata);
384 while (current != TokenType.EOF && !has_space ()) {
385 type = parse_metadata_access ();
386 if (type == null) {
387 Report.error (get_current_src (), "expected `.', `:' or `::'");
388 return null;
391 pattern = parse_identifier (out src, true);
392 if (pattern == null) {
393 return null;
395 var child = metadata.get_child (pattern, type);
396 if (child == null) {
397 child = new Metadata (pattern, type, src);
398 metadata.add_child (child);
400 metadata = child;
402 if (!is_relative) {
403 parent_metadata = metadata;
406 return metadata;
409 Expression? parse_literal () {
410 var src = get_current_src ();
411 Expression expr = null;
412 switch (current) {
413 case TokenType.TRUE:
414 expr = new BooleanLiteral (true, src);
415 break;
416 case TokenType.FALSE:
417 expr = new BooleanLiteral (false, src);
418 break;
419 case TokenType.INTEGER_LITERAL:
420 expr = new IntegerLiteral (get_string (), src);
421 break;
422 case TokenType.REAL_LITERAL:
423 expr = new RealLiteral (get_string (), src);
424 break;
425 case TokenType.STRING_LITERAL:
426 expr = new StringLiteral (get_string (), src);
427 break;
428 default:
429 Report.error (src, "expected literal");
430 break;
432 next ();
433 return expr;
436 bool parse_args (Metadata metadata) {
437 while (current != TokenType.EOF && has_space () && !has_newline ()) {
438 SourceReference src;
439 var id = parse_identifier (out src, false);
440 if (id == null) {
441 return false;
443 var arg_type = ArgumentType.from_string (id);
444 if (arg_type == null) {
445 Report.error (src, "unknown argument");
446 return false;
449 if (current != TokenType.ASSIGN) {
450 // threat as `true'
451 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, src), src));
452 continue;
454 next ();
456 Expression expr = parse_literal ();
457 if (expr == null) {
458 return false;
460 metadata.add_argument (arg_type, new Argument (expr, src));
463 return true;
466 bool parse_rule () {
467 var old_end = end;
468 var metadata = parse_pattern ();
469 if (metadata == null) {
470 return false;
473 if (current == TokenType.EOF || old_end.line != end.line) {
474 // eof or new rule
475 return true;
477 return parse_args (metadata);
481 class SymbolInfo {
482 public Symbol symbol;
483 public Metadata metadata;
486 class CallbackScope {
487 public Namespace parent_namespace;
488 public UnresolvedSymbol gtype_struct_for;
491 class Alias {
492 public string name;
493 public DataType base_type;
494 public Namespace parent_namespace;
495 public SourceReference source_reference;
498 static GLib.Regex type_from_string_regex;
500 MarkupReader reader;
502 CodeContext context;
503 Namespace glib_ns;
505 SourceFile current_source_file;
506 Namespace current_namespace;
507 string current_gtype_struct_for;
508 SourceLocation begin;
509 SourceLocation end;
510 MarkupTokenType current_token;
512 string[] cheader_filenames;
514 ArrayList<Metadata> metadata_stack;
515 Metadata metadata;
517 HashMap<string,ArrayList<SymbolInfo>> current_symbols_info;
519 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
520 HashMap<Symbol,Symbol> concrete_symbols_map = new HashMap<Symbol,Symbol> ();
522 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
523 HashMap<UnresolvedSymbol,ArrayList<Symbol>> symbol_reparent_map = new HashMap<UnresolvedSymbol,ArrayList<Symbol>> (unresolved_symbol_hash, unresolved_symbol_equal);
524 HashMap<Namespace,ArrayList<Method>> namespace_methods = new HashMap<Namespace,ArrayList<Method>> ();
525 HashMap<CallbackScope,ArrayList<Delegate>> gtype_callbacks = new HashMap<CallbackScope,ArrayList<Delegate>> (callback_scope_hash, callback_scope_equal);
526 ArrayList<Alias> aliases = new ArrayList<Alias> ();
529 * Parses all .gir source files in the specified code
530 * context and builds a code tree.
532 * @param context a code context
534 public void parse (CodeContext context) {
535 this.context = context;
536 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
537 context.accept (this);
539 resolve_gir_symbols ();
541 postprocess_reparenting ();
542 postprocess_gtype_callbacks ();
543 postprocess_aliases ();
544 postprocess_namespace_methods ();
547 public override void visit_source_file (SourceFile source_file) {
548 // collect gir namespaces
549 foreach (var node in source_file.get_nodes ()) {
550 if (node is Namespace) {
551 var ns = (Namespace) node;
552 var gir_namespace = source_file.gir_namespace;
553 if (gir_namespace == null) {
554 var a = ns.get_attribute ("CCode");
555 if (a != null && a.has_argument ("gir_namespace")) {
556 gir_namespace = a.get_string ("gir_namespace");
559 if (gir_namespace != null && gir_namespace != ns.name) {
560 var map_from = new UnresolvedSymbol (null, gir_namespace);
561 set_symbol_mapping (map_from, ns);
562 break;
567 if (source_file.filename.has_suffix (".gir")) {
568 parse_file (source_file);
572 public void parse_file (SourceFile source_file) {
573 // load metadata
574 metadata_stack = new ArrayList<Metadata> ();
575 metadata = Metadata.empty;
576 string metadata_filename = "%s.metadata".printf (source_file.filename.ndup (source_file.filename.length - ".gir".length));
577 if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
578 var metadata_parser = new MetadataParser ();
579 var metadata_file = new SourceFile (context, source_file.file_type, metadata_filename);
580 context.add_source_file (metadata_file);
581 metadata = metadata_parser.parse_metadata (metadata_file);
584 this.current_source_file = source_file;
585 reader = new MarkupReader (source_file.filename);
587 // xml prolog
588 next ();
589 next ();
591 next ();
592 parse_repository ();
594 report_unused_metadata (metadata);
596 reader = null;
597 this.current_source_file = null;
600 void next () {
601 current_token = reader.read_token (out begin, out end);
603 // Skip *all* <doc> tags
604 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
605 skip_element();
608 void start_element (string name) {
609 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
610 // error
611 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
615 void end_element (string name) {
616 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
617 // error
618 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
620 next ();
623 SourceReference get_current_src () {
624 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
627 const string GIR_VERSION = "1.2";
629 void add_symbol_to_container (Symbol container, Symbol sym) {
630 if (container is Class) {
631 unowned Class cl = (Class) container;
633 if (sym is Class) {
634 cl.add_class ((Class) sym);
635 } else if (sym is Constant) {
636 cl.add_constant ((Constant) sym);
637 } else if (sym is Enum) {
638 cl.add_enum ((Enum) sym);
639 } else if (sym is Field) {
640 cl.add_field ((Field) sym);
641 } else if (sym is Method) {
642 cl.add_method ((Method) sym);
643 } else if (sym is Property) {
644 cl.add_property ((Property) sym);
645 } else if (sym is Signal) {
646 cl.add_signal ((Signal) sym);
647 } else if (sym is Struct) {
648 cl.add_struct ((Struct) sym);
650 } else if (container is Enum) {
651 unowned Enum en = (Enum) container;
653 if (sym is EnumValue) {
654 en.add_value ((EnumValue) sym);
655 } else if (sym is Constant) {
656 en.add_constant ((Constant) sym);
657 } else if (sym is Method) {
658 en.add_method ((Method) sym);
660 } else if (container is Interface) {
661 unowned Interface iface = (Interface) container;
663 if (sym is Class) {
664 iface.add_class ((Class) sym);
665 } else if (sym is Constant) {
666 iface.add_constant ((Constant) sym);
667 } else if (sym is Enum) {
668 iface.add_enum ((Enum) sym);
669 } else if (sym is Field) {
670 iface.add_field ((Field) sym);
671 } else if (sym is Method) {
672 iface.add_method ((Method) sym);
673 } else if (sym is Property) {
674 iface.add_property ((Property) sym);
675 } else if (sym is Signal) {
676 iface.add_signal ((Signal) sym);
677 } else if (sym is Struct) {
678 iface.add_struct ((Struct) sym);
680 } else if (container is Namespace) {
681 unowned Namespace ns = (Namespace) container;
683 if (sym is Namespace) {
684 ns.add_namespace ((Namespace) sym);
685 } else if (sym is Class) {
686 ns.add_class ((Class) sym);
687 } else if (sym is Constant) {
688 ns.add_constant ((Constant) sym);
689 } else if (sym is Delegate) {
690 ns.add_delegate ((Delegate) sym);
691 } else if (sym is Enum) {
692 ns.add_enum ((Enum) sym);
693 } else if (sym is ErrorDomain) {
694 ns.add_error_domain ((ErrorDomain) sym);
695 } else if (sym is Field) {
696 ns.add_field ((Field) sym);
697 } else if (sym is Interface) {
698 ns.add_interface ((Interface) sym);
699 } else if (sym is Method) {
700 ns.add_method ((Method) sym);
701 } else if (sym is Namespace) {
702 ns.add_namespace ((Namespace) sym);
703 } else if (sym is Struct) {
704 ns.add_struct ((Struct) sym);
706 } else if (container is Struct) {
707 unowned Struct st = (Struct) container;
709 if (sym is Constant) {
710 st.add_constant ((Constant) sym);
711 } else if (sym is Field) {
712 st.add_field ((Field) sym);
713 } else if (sym is Method) {
714 st.add_method ((Method) sym);
715 } else if (sym is Property) {
716 st.add_property ((Property) sym);
718 } else {
719 Report.error (sym.source_reference, "impossible to add to container `%s'".printf (container.name));
723 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
724 UnresolvedSymbol? sym = null;
725 foreach (unowned string s in symbol_string.split (".")) {
726 sym = new UnresolvedSymbol (sym, s, source_reference);
728 if (sym == null) {
729 Report.error (source_reference, "a symbol must be specified");
731 return sym;
734 UnresolvedSymbol get_unresolved_symbol (Symbol symbol) {
735 if (symbol is UnresolvedSymbol) {
736 return (UnresolvedSymbol) symbol;
738 var sym = new UnresolvedSymbol (null, symbol.name);
739 var result = sym;
740 var cur = symbol.parent_node as Symbol;
741 while (cur != null && cur.name != null) {
742 sym = new UnresolvedSymbol (sym, cur.name);
743 cur = cur.parent_node as Symbol;
745 return result;
748 void set_symbol_mapping (Symbol map_from, Symbol map_to) {
749 // last mapping is the most up-to-date
750 if (map_from is UnresolvedSymbol) {
751 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
752 } else {
753 concrete_symbols_map[map_from] = map_to;
757 void assume_parameter_names (Signal sig, Symbol sym) {
758 Iterator<Parameter> iter;
759 if (sym is Method) {
760 iter = ((Method) sym).get_parameters ().iterator ();
761 } else {
762 iter = ((Delegate) sym).get_parameters ().iterator ();
764 foreach (var param in sig.get_parameters ()) {
765 if (!iter.next ()) {
766 // unreachable for valid GIR
767 break;
769 param.name = iter.get ().name;
773 SymbolInfo? add_symbol_info (Symbol symbol) {
774 var name = symbol.name;
775 if (symbol is CreationMethod && name == null) {
776 name = ".new";
779 var info = new SymbolInfo ();
780 info.symbol = symbol;
781 info.metadata = metadata;
782 var colliding = current_symbols_info[name];
783 if (colliding == null) {
784 colliding = new ArrayList<SymbolInfo> ();
785 current_symbols_info[name] = colliding;
787 colliding.add (info);
788 return info;
791 void merge (SymbolInfo info, ArrayList<SymbolInfo> colliding, ArrayList<SymbolInfo> merged) {
792 if (info.symbol is Property) {
793 foreach (var cinfo in colliding) {
794 var sym = cinfo.symbol;
795 if (sym is Signal || sym is Field) {
796 // properties take precedence
797 merged.add (cinfo);
798 } else if (sym is Method) {
799 // assume method is getter
800 merged.add (cinfo);
803 var getter_name = "get_%s".printf (info.symbol.name);
804 if (current_symbols_info.contains (getter_name)) {
805 ((Property) info.symbol).no_accessor_method = false;
807 } else if (info.symbol is Signal) {
808 var sig = (Signal) info.symbol;
809 foreach (var cinfo in colliding) {
810 var sym = cinfo.symbol;
811 if (sym is Method) {
812 var method = (Method) sym;
813 if (method.is_virtual) {
814 sig.is_virtual = true;
815 } else {
816 sig.has_emitter = true;
818 assume_parameter_names (sig, method);
819 merged.add (cinfo);
820 } else if (sym is Field) {
821 merged.add (cinfo);
824 } else if (info.symbol is Method && !(info.symbol is CreationMethod)) {
825 var method = (Method) info.symbol;
826 foreach (var cinfo in colliding) {
827 var sym = cinfo.symbol;
828 if (sym != method && method.is_virtual && sym is Method) {
829 // assume method is wrapper
830 merged.add (cinfo);
833 } else if (info.symbol is Field) {
834 foreach (var cinfo in colliding) {
835 var sym = cinfo.symbol;
836 if (sym is Method) {
837 // assume method is getter
838 merged.add (cinfo);
842 var field = (Field) info.symbol;
843 if (field.variable_type is ArrayType) {
844 SymbolInfo array_length = null;
845 if (current_symbols_info.contains ("n_%s".printf (field.name))) {
846 array_length = current_symbols_info.get ("n_%s".printf (field.name)).get (0);
847 } else if (current_symbols_info.contains ("%s_length".printf (field.name))) {
848 array_length = current_symbols_info.get ("%s_length".printf (field.name)).get (0);
850 if (array_length != null) {
851 // array has length
852 field.set_array_length_cname (array_length.symbol.name);
853 field.no_array_length = false;
854 merged.add (array_length);
860 void postprocess_symbol (Symbol symbol, Metadata metadata) {
861 // deprecation
862 symbol.replacement = metadata.get_string (ArgumentType.REPLACEMENT);
863 symbol.deprecated_since = element_get_string ("deprecated-version", ArgumentType.DEPRECATED_SINCE);
864 symbol.deprecated = metadata.get_bool (ArgumentType.DEPRECATED) || symbol.replacement != null || symbol.deprecated_since != null;
866 // mark to be reparented
867 if (metadata.has_argument (ArgumentType.PARENT)) {
868 var target_symbol = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
869 var reparent_list = symbol_reparent_map[target_symbol];
870 if (reparent_list == null) {
871 reparent_list = new ArrayList<Symbol>();
872 symbol_reparent_map[target_symbol] = reparent_list;
874 reparent_list.add (symbol);
876 // if referenceable, map unresolved references to point to the new place
877 if (symbol is Namespace || symbol is TypeSymbol) {
878 set_symbol_mapping (symbol, new UnresolvedSymbol (target_symbol, symbol.name));
883 void merge_add_process (Symbol container) {
884 var merged = new ArrayList<SymbolInfo> ();
885 foreach (var name in current_symbols_info.get_keys ()) {
886 var colliding = current_symbols_info[name];
887 foreach (var info in colliding) {
888 merge (info, colliding, merged);
892 foreach (var infos in current_symbols_info.get_values ()) {
893 foreach (var info in infos) {
894 if (merged.contains (info) || info.metadata.get_bool (ArgumentType.HIDDEN)) {
895 continue;
897 if (!info.metadata.has_argument (ArgumentType.PARENT)) {
898 add_symbol_to_container (container, info.symbol);
900 postprocess_symbol (info.symbol, info.metadata);
905 Metadata get_current_metadata () {
906 var name = reader.name;
907 var child_name = reader.get_attribute ("name");
908 if (child_name == null) {
909 return Metadata.empty;
912 var type = MetadataType.GENERIC;
913 if (name == "glib:signal") {
914 type = MetadataType.SIGNAL;
915 } else if (name == "property") {
916 type = MetadataType.PROPERTY;
919 return metadata.match_child (child_name, type);
922 bool push_metadata () {
923 // skip?
924 if (reader.get_attribute ("introspectable") == "0") {
925 return false;
927 var new_metadata = get_current_metadata ();
928 if (new_metadata.get_bool (ArgumentType.SKIP)) {
929 return false;
932 metadata_stack.add (metadata);
933 metadata = new_metadata;
934 return true;
937 void pop_metadata () {
938 metadata = metadata_stack[metadata_stack.size - 1];
939 metadata_stack.remove_at (metadata_stack.size - 1);
942 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
943 int type_arguments_length = (int) type_arguments.length;
944 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
946 int depth = 0;
947 for (var c = 0 ; c < type_arguments_length ; c++) {
948 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
949 depth++;
950 current.append_unichar (type_arguments[c]);
951 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
952 depth--;
953 current.append_unichar (type_arguments[c]);
954 } else if (type_arguments[c] == ',') {
955 if (depth == 0) {
956 var dt = parse_type_from_string (current.str, true, source_reference);
957 if (dt == null) {
958 return false;
960 parent_type.add_type_argument (dt);
961 current.truncate ();
962 } else {
963 current.append_unichar (type_arguments[c]);
965 } else {
966 current.append_unichar (type_arguments[c]);
970 var dt = parse_type_from_string (current.str, true, source_reference);
971 if (dt == null) {
972 return false;
974 parent_type.add_type_argument (dt);
976 return true;
979 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
980 if (type_from_string_regex == null) {
981 try {
982 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[(,*)?\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
983 } catch (GLib.RegexError e) {
984 GLib.error ("Unable to compile regex: %s", e.message);
988 GLib.MatchInfo match;
989 if (!type_from_string_regex.match (type_string, 0, out match)) {
990 Report.error (source_reference, "unable to parse type");
991 return null;
994 DataType? type = null;
996 var ownership_data = match.fetch (1);
997 var type_name = match.fetch (2);
998 var type_arguments_data = match.fetch (3);
999 var pointers_data = match.fetch (4);
1000 var array_data = match.fetch (5);
1001 var nullable_data = match.fetch (6);
1003 var nullable = nullable_data != null && nullable_data.length > 0;
1005 if (ownership_data == null && type_name == "void") {
1006 if (array_data == null && !nullable) {
1007 type = new VoidType (source_reference);
1008 if (pointers_data != null) {
1009 for (int i=0; i < pointers_data.length; i++) {
1010 type = new PointerType (type);
1013 return type;
1014 } else {
1015 Report.error (source_reference, "invalid void type");
1016 return null;
1020 bool value_owned = owned_by_default;
1022 if (ownership_data == "owned") {
1023 if (owned_by_default) {
1024 Report.error (source_reference, "unexpected `owned' keyword");
1025 } else {
1026 value_owned = true;
1028 } else if (ownership_data == "unowned") {
1029 if (owned_by_default) {
1030 value_owned = true;
1031 } else {
1032 Report.error (source_reference, "unexpected `unowned' keyword");
1033 return null;
1037 var sym = parse_symbol_from_string (type_name, source_reference);
1038 if (sym == null) {
1039 return null;
1041 type = new UnresolvedType.from_symbol (sym, source_reference);
1043 if (type_arguments_data != null && type_arguments_data.length > 0) {
1044 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1045 return null;
1049 if (pointers_data != null) {
1050 for (int i=0; i < pointers_data.length; i++) {
1051 type = new PointerType (type);
1055 if (array_data != null) {
1056 type = new ArrayType (type, (int) array_data.length + 1, source_reference);
1059 type.nullable = nullable;
1060 type.value_owned = value_owned;
1061 return type;
1064 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1065 var str = metadata.get_string (arg_type);
1066 if (str == null) {
1067 str = reader.get_attribute (attribute_name);
1069 return str;
1072 DataType? element_get_type (DataType orig_type, bool owned_by_default, out bool changed = null) {
1073 if (&changed != null) {
1074 changed = false;
1077 var type = orig_type;
1079 if (metadata.has_argument (ArgumentType.TYPE)) {
1080 var new_type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1081 if (&changed != null) {
1082 changed = true;
1084 return new_type;
1087 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1088 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1089 if (&changed != null) {
1090 changed = true;
1094 if (type is VoidType) {
1095 return type;
1098 if (metadata.get_bool (ArgumentType.ARRAY)) {
1099 type = new ArrayType (type, 1, type.source_reference);
1100 if (&changed != null) {
1101 changed = true;
1105 if (owned_by_default) {
1106 if (metadata.has_argument (ArgumentType.UNOWNED)) {
1107 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED);
1109 } else {
1110 if (metadata.has_argument (ArgumentType.OWNED)) {
1111 type.value_owned = metadata.get_bool (ArgumentType.OWNED);
1114 if (metadata.has_argument (ArgumentType.NULLABLE)) {
1115 type.nullable = metadata.get_bool (ArgumentType.NULLABLE);
1118 return type;
1121 string? element_get_name (string attribute_name = "name", ArgumentType arg_type = ArgumentType.NAME) {
1122 var name = reader.get_attribute (attribute_name);
1123 var pattern = metadata.get_string (arg_type);
1124 if (pattern != null) {
1125 try {
1126 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1127 GLib.MatchInfo match;
1128 if (!regex.match (name, 0, out match)) {
1129 name = pattern;
1130 } else {
1131 var matched = match.fetch (1);
1132 if (matched != null && matched.length > 0) {
1133 name = matched;
1134 } else {
1135 name = pattern;
1138 } catch (Error e) {
1139 name = pattern;
1142 return name;
1145 void parse_repository () {
1146 start_element ("repository");
1147 if (reader.get_attribute ("version") != GIR_VERSION) {
1148 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1149 return;
1151 next ();
1152 while (current_token == MarkupTokenType.START_ELEMENT) {
1153 if (reader.name == "namespace") {
1154 var ns = parse_namespace ();
1155 if (ns != null) {
1156 context.root.add_namespace (ns);
1158 } else if (reader.name == "include") {
1159 parse_include ();
1160 } else if (reader.name == "package") {
1161 var pkg = parse_package ();
1162 if (context.has_package (pkg)) {
1163 // package already provided elsewhere, stop parsing this GIR
1164 return;
1165 } else {
1166 context.add_package (pkg);
1168 } else if (reader.name == "c:include") {
1169 parse_c_include ();
1170 } else {
1171 // error
1172 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1173 skip_element ();
1176 end_element ("repository");
1179 void parse_include () {
1180 start_element ("include");
1181 var pkg = reader.get_attribute ("name");
1182 var version = reader.get_attribute ("version");
1183 if (version != null) {
1184 pkg = "%s-%s".printf (pkg, version);
1186 // add the package to the queue
1187 context.add_external_package (pkg);
1188 next ();
1189 end_element ("include");
1192 string parse_package () {
1193 start_element ("package");
1194 var pkg = reader.get_attribute ("name");
1195 next ();
1196 end_element ("package");
1197 return pkg;
1200 void parse_c_include () {
1201 start_element ("c:include");
1202 cheader_filenames += reader.get_attribute ("name");
1203 next ();
1204 end_element ("c:include");
1207 void skip_element () {
1208 next ();
1210 int level = 1;
1211 while (level > 0) {
1212 if (current_token == MarkupTokenType.START_ELEMENT) {
1213 level++;
1214 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1215 level--;
1216 } else if (current_token == MarkupTokenType.EOF) {
1217 Report.error (get_current_src (), "unexpected end of file");
1218 break;
1220 next ();
1224 Namespace? parse_namespace () {
1225 start_element ("namespace");
1227 bool new_namespace = false;
1228 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
1229 string namespace_name = cprefix;
1230 string gir_namespace = reader.get_attribute ("name");
1231 string gir_version = reader.get_attribute ("version");
1232 if (namespace_name == null) {
1233 namespace_name = gir_namespace;
1235 current_source_file.gir_namespace = gir_namespace;
1236 current_source_file.gir_version = gir_version;
1238 var ns_metadata = metadata.match_child (gir_namespace);
1239 if (ns_metadata.has_argument (ArgumentType.NAME)) {
1240 namespace_name = ns_metadata.get_string (ArgumentType.NAME);
1243 var ns = context.root.scope.lookup (namespace_name) as Namespace;
1244 if (ns == null) {
1245 ns = new Namespace (namespace_name, get_current_src ());
1246 new_namespace = true;
1247 } else {
1248 if (ns.external_package) {
1249 ns.attributes = null;
1250 ns.source_reference = get_current_src ();
1254 if (gir_namespace != ns.name) {
1255 set_symbol_mapping (new UnresolvedSymbol (null, gir_namespace), ns);
1258 var old_namespace = current_namespace;
1259 current_namespace = ns;
1261 if (cprefix != null) {
1262 ns.add_cprefix (cprefix);
1263 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
1266 if (ns_metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1267 var val = ns_metadata.get_string (ArgumentType.CHEADER_FILENAME);
1268 foreach (string filename in val.split (",")) {
1269 ns.add_cheader_filename (filename);
1271 } else {
1272 foreach (string c_header in cheader_filenames) {
1273 ns.add_cheader_filename (c_header);
1277 next ();
1278 var current_namespace_methods = namespace_methods[ns];
1279 if (current_namespace_methods == null) {
1280 current_namespace_methods = new ArrayList<Method> ();
1281 namespace_methods[ns] = current_namespace_methods;
1283 var old_symbols_info = current_symbols_info;
1284 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1285 while (current_token == MarkupTokenType.START_ELEMENT) {
1286 if (!push_metadata ()) {
1287 skip_element ();
1288 continue;
1291 if (reader.name == "alias") {
1292 var alias = parse_alias ();
1293 aliases.add (alias);
1294 } else if (reader.name == "enumeration") {
1295 if (reader.get_attribute ("glib:error-quark") != null) {
1296 add_symbol_info (parse_error_domain ());
1297 } else {
1298 add_symbol_info (parse_enumeration ());
1300 } else if (reader.name == "bitfield") {
1301 add_symbol_info (parse_bitfield ());
1302 } else if (reader.name == "function") {
1303 current_namespace_methods.add (parse_method ("function"));
1304 } else if (reader.name == "callback") {
1305 add_symbol_info (parse_callback ());
1306 } else if (reader.name == "record") {
1307 if (reader.get_attribute ("glib:get-type") != null) {
1308 add_symbol_info (parse_boxed ());
1309 } else {
1310 var record = parse_record ();
1311 if (record != null) {
1312 add_symbol_info (record);
1315 } else if (reader.name == "class") {
1316 add_symbol_info (parse_class ());
1317 } else if (reader.name == "interface") {
1318 add_symbol_info (parse_interface ());
1319 } else if (reader.name == "glib:boxed") {
1320 add_symbol_info (parse_boxed ());
1321 } else if (reader.name == "union") {
1322 add_symbol_info (parse_union ());
1323 } else if (reader.name == "constant") {
1324 add_symbol_info (parse_constant ());
1325 } else {
1326 // error
1327 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
1328 skip_element ();
1331 pop_metadata ();
1333 end_element ("namespace");
1335 merge_add_process (ns);
1336 current_symbols_info = old_symbols_info;
1337 current_namespace = old_namespace;
1339 if (!new_namespace) {
1340 ns = null;
1343 return ns;
1346 Alias parse_alias () {
1347 // alias has no type information
1348 start_element ("alias");
1349 var alias = new Alias ();
1350 alias.source_reference = get_current_src ();
1351 alias.name = reader.get_attribute ("name");
1352 alias.base_type = element_get_type (parse_type_from_gir_name (reader.get_attribute ("target")), true);
1353 alias.parent_namespace = current_namespace;
1354 next ();
1355 end_element ("alias");
1356 return alias;
1359 private void calculate_common_prefix (ref string common_prefix, string cname) {
1360 if (common_prefix == null) {
1361 common_prefix = cname;
1362 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
1363 // FIXME: could easily be made faster
1364 common_prefix = common_prefix.ndup (common_prefix.length - 1);
1366 } else {
1367 while (!cname.has_prefix (common_prefix)) {
1368 common_prefix = common_prefix.ndup (common_prefix.length - 1);
1371 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
1372 (cname.offset (common_prefix.length).get_char ().isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
1373 // enum values may not consist solely of digits
1374 common_prefix = common_prefix.ndup (common_prefix.length - 1);
1378 Enum parse_enumeration () {
1379 start_element ("enumeration");
1380 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1381 en.access = SymbolAccessibility.PUBLIC;
1383 string enum_cname = reader.get_attribute ("c:type");
1384 if (enum_cname != null) {
1385 en.set_cname (enum_cname);
1388 next ();
1390 string common_prefix = null;
1392 while (current_token == MarkupTokenType.START_ELEMENT) {
1393 if (!push_metadata ()) {
1394 skip_element ();
1395 continue;
1398 if (reader.name == "member") {
1399 var ev = parse_enumeration_member ();
1400 en.add_value (ev);
1401 calculate_common_prefix (ref common_prefix, ev.get_cname ());
1402 } else {
1403 // error
1404 Report.error (get_current_src (), "unknown child element `%s' in `enumaration'".printf (reader.name));
1405 skip_element ();
1408 pop_metadata ();
1411 en.set_cprefix (common_prefix);
1413 end_element ("enumeration");
1414 return en;
1417 ErrorDomain parse_error_domain () {
1418 start_element ("enumeration");
1420 var ed = new ErrorDomain (reader.get_attribute ("name"), get_current_src ());
1421 ed.access = SymbolAccessibility.PUBLIC;
1423 string enum_cname = reader.get_attribute ("c:type");
1424 if (enum_cname != null) {
1425 ed.set_cname (enum_cname);
1428 next ();
1430 string common_prefix = null;
1432 while (current_token == MarkupTokenType.START_ELEMENT) {
1433 if (!push_metadata ()) {
1434 skip_element ();
1435 continue;
1438 if (reader.name == "member") {
1439 ErrorCode ec = parse_error_member ();
1440 ed.add_code (ec);
1441 calculate_common_prefix (ref common_prefix, ec.get_cname ());
1442 } else {
1443 // error
1444 Report.error (get_current_src (), "unknown child element `%s' in `enumeration'".printf (reader.name));
1445 skip_element ();
1448 pop_metadata ();
1451 ed.set_cprefix (common_prefix);
1453 end_element ("enumeration");
1454 return ed;
1457 Enum parse_bitfield () {
1458 start_element ("bitfield");
1459 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1460 en.access = SymbolAccessibility.PUBLIC;
1461 next ();
1462 while (current_token == MarkupTokenType.START_ELEMENT) {
1463 if (!push_metadata ()) {
1464 skip_element ();
1465 continue;
1468 if (reader.name == "member") {
1469 en.add_value (parse_enumeration_member ());
1470 } else {
1471 // error
1472 Report.error (get_current_src (), "unknown child element `%s' in `bitfield'".printf (reader.name));
1473 skip_element ();
1476 pop_metadata ();
1478 end_element ("bitfield");
1479 return en;
1482 EnumValue parse_enumeration_member () {
1483 start_element ("member");
1484 var ev = new EnumValue (reader.get_attribute ("name").up ().replace ("-", "_"), null, get_current_src ());
1485 ev.set_cname (reader.get_attribute ("c:identifier"));
1486 next ();
1487 end_element ("member");
1488 return ev;
1491 ErrorCode parse_error_member () {
1492 start_element ("member");
1494 ErrorCode ec;
1495 string name = reader.get_attribute ("name").up ().replace ("-", "_");
1496 string value = reader.get_attribute ("value");
1497 if (value != null) {
1498 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
1499 } else {
1500 ec = new ErrorCode (name);
1503 next ();
1504 end_element ("member");
1505 return ec;
1508 DataType parse_return_value (out string? ctype = null) {
1509 start_element ("return-value");
1510 string transfer = reader.get_attribute ("transfer-ownership");
1511 string allow_none = reader.get_attribute ("allow-none");
1512 next ();
1513 var transfer_elements = transfer == "full";
1514 var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
1515 if (transfer == "full" || transfer == "container") {
1516 type.value_owned = true;
1518 if (allow_none == "1") {
1519 type.nullable = true;
1521 end_element ("return-value");
1522 return type;
1525 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) {
1526 Parameter param;
1528 if (&array_length_idx != null) {
1529 array_length_idx = -1;
1531 if (&closure_idx != null) {
1532 closure_idx = -1;
1534 if (&destroy_idx != null) {
1535 destroy_idx = -1;
1538 start_element ("parameter");
1539 string name = reader.get_attribute ("name");
1540 if (name == null) {
1541 name = default_name;
1543 string direction = null;
1544 if (metadata.has_argument (ArgumentType.OUT)) {
1545 if (metadata.get_bool (ArgumentType.OUT)) {
1546 direction = "out";
1547 } // null otherwise
1548 } else if (metadata.has_argument (ArgumentType.REF)) {
1549 if (metadata.get_bool (ArgumentType.REF)) {
1550 direction = "inout";
1551 } // null otherwise
1552 } else {
1553 direction = reader.get_attribute ("direction");
1555 string transfer = reader.get_attribute ("transfer-ownership");
1556 string allow_none = reader.get_attribute ("allow-none");
1558 if (&scope != null) {
1559 scope = reader.get_attribute ("scope");
1562 string closure = reader.get_attribute ("closure");
1563 string destroy = reader.get_attribute ("destroy");
1564 if (closure != null && &closure_idx != null) {
1565 closure_idx = closure.to_int ();
1567 if (destroy != null && &destroy_idx != null) {
1568 destroy_idx = destroy.to_int ();
1571 next ();
1572 if (reader.name == "varargs") {
1573 start_element ("varargs");
1574 next ();
1575 param = new Parameter.with_ellipsis (get_current_src ());
1576 end_element ("varargs");
1577 } else {
1578 string ctype;
1579 var type = parse_type (out ctype, out array_length_idx, transfer == "full");
1580 bool changed;
1581 type = element_get_type (type, false, out changed);
1582 if (!changed) {
1583 // discard ctype, duplicated information
1584 ctype = null;
1587 if (type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_POS)) {
1588 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_POS);
1591 if (transfer == "full" || transfer == "container" || destroy != null) {
1592 type.value_owned = true;
1594 if (allow_none == "1") {
1595 type.nullable = true;
1597 param = new Parameter (name, type, get_current_src ());
1598 param.ctype = ctype;
1599 if (direction == "out") {
1600 param.direction = ParameterDirection.OUT;
1601 } else if (direction == "inout") {
1602 param.direction = ParameterDirection.REF;
1604 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
1606 end_element ("parameter");
1607 return param;
1610 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) {
1611 bool is_array = false;
1612 string type_name = reader.get_attribute ("name");
1614 if (reader.name == "array") {
1615 is_array = true;
1616 start_element ("array");
1618 if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
1619 if (reader.get_attribute ("length") != null
1620 && &array_length_index != null) {
1621 array_length_index = reader.get_attribute ("length").to_int ();
1623 next ();
1624 var element_type = parse_type ();
1625 end_element ("array");
1626 return new ArrayType (element_type, 1, null);
1628 } else if (reader.name == "callback"){
1629 var callback = parse_callback ();
1630 return new DelegateType (callback);
1631 } else {
1632 start_element ("type");
1635 if (&ctype != null) {
1636 ctype = reader.get_attribute("c:type");
1639 next ();
1641 if (type_name == "GLib.PtrArray"
1642 && current_token == MarkupTokenType.START_ELEMENT) {
1643 type_name = "GLib.GenericArray";
1646 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated);
1648 // type arguments / element types
1649 while (current_token == MarkupTokenType.START_ELEMENT) {
1650 var element_type = parse_type ();
1651 element_type.value_owned = transfer_elements;
1652 type.add_type_argument (element_type);
1655 end_element (is_array ? "array" : "type");
1656 return type;
1659 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null) {
1660 if (&no_array_length != null) {
1661 no_array_length = false;
1663 if (&array_null_terminated != null) {
1664 array_null_terminated = false;
1667 DataType type;
1668 if (type_name == "none") {
1669 type = new VoidType (get_current_src ());
1670 } else if (type_name == "gpointer") {
1671 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
1672 } else if (type_name == "GObject.Strv") {
1673 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, get_current_src ());
1674 if (&no_array_length != null) {
1675 no_array_length = true;
1677 if (&array_null_terminated != null) {
1678 array_null_terminated = true;
1680 } else {
1681 bool known_type = true;
1682 if (type_name == "utf8") {
1683 type_name = "string";
1684 } else if (type_name == "gboolean") {
1685 type_name = "bool";
1686 } else if (type_name == "gchar") {
1687 type_name = "char";
1688 } else if (type_name == "gshort") {
1689 type_name = "short";
1690 } else if (type_name == "gushort") {
1691 type_name = "ushort";
1692 } else if (type_name == "gint") {
1693 type_name = "int";
1694 } else if (type_name == "guint") {
1695 type_name = "uint";
1696 } else if (type_name == "glong") {
1697 type_name = "long";
1698 } else if (type_name == "gulong") {
1699 type_name = "ulong";
1700 } else if (type_name == "gint8") {
1701 type_name = "int8";
1702 } else if (type_name == "guint8") {
1703 type_name = "uint8";
1704 } else if (type_name == "gint16") {
1705 type_name = "int16";
1706 } else if (type_name == "guint16") {
1707 type_name = "uint16";
1708 } else if (type_name == "gint32") {
1709 type_name = "int32";
1710 } else if (type_name == "guint32") {
1711 type_name = "uint32";
1712 } else if (type_name == "gint64") {
1713 type_name = "int64";
1714 } else if (type_name == "guint64") {
1715 type_name = "uint64";
1716 } else if (type_name == "gfloat") {
1717 type_name = "float";
1718 } else if (type_name == "gdouble") {
1719 type_name = "double";
1720 } else if (type_name == "filename") {
1721 type_name = "string";
1722 } else if (type_name == "GLib.offset") {
1723 type_name = "int64";
1724 } else if (type_name == "gsize") {
1725 type_name = "size_t";
1726 } else if (type_name == "gssize") {
1727 type_name = "ssize_t";
1728 } else if (type_name == "GType") {
1729 type_name = "GLib.Type";
1730 } else if (type_name == "GLib.String") {
1731 type_name = "GLib.StringBuilder";
1732 } else if (type_name == "GObject.Class") {
1733 type_name = "GLib.ObjectClass";
1734 } else if (type_name == "GLib.unichar") {
1735 type_name = "unichar";
1736 } else if (type_name == "GLib.Data") {
1737 type_name = "GLib.Datalist";
1738 } else if (type_name == "Atk.ImplementorIface") {
1739 type_name = "Atk.Implementor";
1740 } else {
1741 known_type = false;
1743 var sym = parse_symbol_from_string (type_name, get_current_src ());
1744 type = new UnresolvedType.from_symbol (sym, get_current_src ());
1745 if (!known_type) {
1746 unresolved_gir_symbols.add (sym);
1750 return type;
1753 Struct? parse_record () {
1754 start_element ("record");
1755 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
1756 st.external = true;
1758 current_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
1760 st.access = SymbolAccessibility.PUBLIC;
1761 next ();
1762 while (current_token == MarkupTokenType.START_ELEMENT) {
1763 if (!push_metadata ()) {
1764 skip_element ();
1765 continue;
1768 if (reader.name == "field") {
1769 st.add_field (parse_field ());
1770 } else if (reader.name == "constructor") {
1771 parse_constructor ();
1772 } else if (reader.name == "method") {
1773 st.add_method (parse_method ("method"));
1774 } else if (reader.name == "union") {
1775 Struct s = parse_union ();
1776 var s_fields = s.get_fields ();
1777 foreach (var f in s_fields) {
1778 f.set_cname (s.get_cname () + "." + f.get_cname ());
1779 f.name = s.name + "_" + f.name;
1780 st.add_field (f);
1782 } else {
1783 // error
1784 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
1785 skip_element ();
1788 pop_metadata ();
1790 end_element ("record");
1792 if (current_gtype_struct_for != null) {
1793 // skip *Class or *Iface
1794 current_gtype_struct_for = null;
1795 return null;
1798 return st;
1801 Class parse_class () {
1802 start_element ("class");
1803 var cl = new Class (reader.get_attribute ("name"), get_current_src ());
1804 cl.access = SymbolAccessibility.PUBLIC;
1805 cl.external = true;
1807 string cname = reader.get_attribute ("c:type");
1808 if (cname != null) {
1809 cl.set_cname (cname);
1812 string parent = reader.get_attribute ("parent");
1813 if (parent != null) {
1814 cl.add_base_type (parse_type_from_gir_name (parent));
1817 next ();
1818 var old_symbols_info = current_symbols_info;
1819 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1820 while (current_token == MarkupTokenType.START_ELEMENT) {
1821 if (!push_metadata ()) {
1822 skip_element ();
1823 continue;
1826 if (reader.name == "implements") {
1827 start_element ("implements");
1828 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
1829 next ();
1830 end_element ("implements");
1831 } else if (reader.name == "constant") {
1832 add_symbol_info (parse_constant ());
1833 } else if (reader.name == "field") {
1834 add_symbol_info (parse_field ());
1835 } else if (reader.name == "property") {
1836 add_symbol_info (parse_property ());
1837 } else if (reader.name == "constructor") {
1838 add_symbol_info (parse_constructor (cname));
1839 } else if (reader.name == "function") {
1840 add_symbol_info (parse_method ("function"));
1841 } else if (reader.name == "method") {
1842 add_symbol_info (parse_method ("method"));
1843 } else if (reader.name == "virtual-method") {
1844 add_symbol_info (parse_method ("virtual-method"));
1845 } else if (reader.name == "union") {
1846 Struct s = parse_union ();
1847 var s_fields = s.get_fields ();
1848 foreach (var f in s_fields) {
1849 f.set_cname (s.get_cname () + "." + f.get_cname ());
1850 f.name = s.name + "_" + f.name;
1851 add_symbol_info (f);
1853 } else if (reader.name == "glib:signal") {
1854 add_symbol_info (parse_signal ());
1855 } else {
1856 // error
1857 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
1858 skip_element ();
1861 pop_metadata ();
1864 merge_add_process (cl);
1865 current_symbols_info = old_symbols_info;
1867 handle_async_methods (cl);
1869 end_element ("class");
1870 return cl;
1873 Interface parse_interface () {
1874 start_element ("interface");
1875 var iface = new Interface (element_get_name (), get_current_src ());
1876 iface.access = SymbolAccessibility.PUBLIC;
1877 iface.external = true;
1879 string cname = reader.get_attribute ("c:type");
1880 if (cname != null) {
1881 iface.set_cname (cname);
1884 next ();
1885 var old_symbols_info = current_symbols_info;
1886 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1887 while (current_token == MarkupTokenType.START_ELEMENT) {
1888 if (!push_metadata ()) {
1889 skip_element ();
1890 continue;
1893 if (reader.name == "prerequisite") {
1894 start_element ("prerequisite");
1895 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
1896 next ();
1897 end_element ("prerequisite");
1898 } else if (reader.name == "field") {
1899 parse_field ();
1900 } else if (reader.name == "property") {
1901 add_symbol_info (parse_property ());
1902 } else if (reader.name == "virtual-method") {
1903 add_symbol_info (parse_method ("virtual-method"));
1904 } else if (reader.name == "function") {
1905 add_symbol_info (parse_method ("function"));
1906 } else if (reader.name == "method") {
1907 add_symbol_info (parse_method ("method"));
1908 } else if (reader.name == "glib:signal") {
1909 add_symbol_info (parse_signal ());
1910 } else {
1911 // error
1912 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
1913 skip_element ();
1916 pop_metadata ();
1919 merge_add_process (iface);
1920 current_symbols_info = old_symbols_info;
1922 handle_async_methods (iface);
1924 end_element ("interface");
1925 return iface;
1928 void handle_async_methods (ObjectTypeSymbol type_symbol) {
1929 var methods = type_symbol.get_methods ();
1930 for (int method_n = 0 ; method_n < methods.size ; method_n++) {
1931 var m = methods.get (method_n);
1933 if (m.coroutine) {
1934 string finish_method_base;
1935 if (m.name.has_suffix ("_async")) {
1936 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
1937 } else {
1938 finish_method_base = m.name;
1940 var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method;
1942 // check if the method is using non-standard finish method name
1943 if (finish_method == null) {
1944 var method_cname = m.get_finish_cname ();
1945 foreach (Method method in type_symbol.get_methods ()) {
1946 if (method.get_cname () == method_cname) {
1947 finish_method = method;
1948 break;
1953 if (finish_method != null) {
1954 m.return_type = finish_method.return_type.copy ();
1955 m.no_array_length = finish_method.no_array_length;
1956 m.array_null_terminated = finish_method.array_null_terminated;
1957 foreach (var param in finish_method.get_parameters ()) {
1958 if (param.direction == ParameterDirection.OUT) {
1959 var async_param = param.copy ();
1960 if (m.scope.lookup (param.name) != null) {
1961 // parameter name conflict
1962 async_param.name += "_out";
1964 m.add_parameter (async_param);
1967 foreach (DataType error_type in finish_method.get_error_types ()) {
1968 m.add_error_type (error_type.copy ());
1970 if (methods.index_of (finish_method) < method_n) {
1971 method_n--;
1973 type_symbol.scope.remove (finish_method.name);
1974 methods.remove (finish_method);
1980 Field parse_field () {
1981 start_element ("field");
1982 string name = reader.get_attribute ("name");
1983 string allow_none = reader.get_attribute ("allow-none");
1984 next ();
1985 var type = parse_type ();
1986 type = element_get_type (type, true);
1987 if (type is DelegateType && current_gtype_struct_for != null) {
1988 // virtual
1989 var callback_scope = new CallbackScope ();
1990 callback_scope.parent_namespace = current_namespace;
1991 callback_scope.gtype_struct_for = parse_symbol_from_string (current_gtype_struct_for);
1992 ArrayList<Delegate> callbacks = gtype_callbacks.get (callback_scope);
1993 if (callbacks == null) {
1994 callbacks = new ArrayList<Delegate> ();
1995 gtype_callbacks.set (callback_scope, callbacks);
1997 callbacks.add (((DelegateType) type).delegate_symbol);
1999 var field = new Field (name, type, null, get_current_src ());
2000 field.access = SymbolAccessibility.PUBLIC;
2001 field.no_array_length = true;
2002 if (allow_none == "1") {
2003 type.nullable = true;
2005 end_element ("field");
2006 return field;
2009 Property parse_property () {
2010 start_element ("property");
2011 string name = reader.get_attribute ("name").replace ("-", "_");
2012 string readable = reader.get_attribute ("readable");
2013 string writable = reader.get_attribute ("writable");
2014 string construct_ = reader.get_attribute ("construct");
2015 string construct_only = reader.get_attribute ("construct-only");
2016 next ();
2017 bool no_array_length;
2018 bool array_null_terminated;
2019 var type = parse_type (null, null, false, out no_array_length, out array_null_terminated);
2020 var prop = new Property (name, type, null, null, get_current_src ());
2021 prop.access = SymbolAccessibility.PUBLIC;
2022 prop.no_accessor_method = true;
2023 prop.no_array_length = no_array_length;
2024 prop.array_null_terminated = array_null_terminated;
2025 if (readable != "0") {
2026 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
2028 if (writable == "1" || construct_only == "1") {
2029 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
2031 end_element ("property");
2032 return prop;
2035 Delegate parse_callback () {
2036 return this.parse_function ("callback") as Delegate;
2039 Method parse_constructor (string? parent_ctype = null) {
2040 start_element ("constructor");
2041 string name = element_get_name ();
2042 string throws_string = reader.get_attribute ("throws");
2043 string cname = reader.get_attribute ("c:identifier");
2044 next ();
2046 string? ctype;
2047 parse_return_value (out ctype);
2049 var m = new CreationMethod (null, name, get_current_src ());
2050 m.access = SymbolAccessibility.PUBLIC;
2051 m.has_construct_function = false;
2052 if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
2053 m.custom_return_type_cname = ctype;
2055 if (m.name == "new") {
2056 m.name = null;
2057 } else if (m.name.has_prefix ("new_")) {
2058 m.name = m.name.offset ("new_".length);
2060 if (cname != null) {
2061 m.set_cname (cname);
2063 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2064 start_element ("parameters");
2065 next ();
2066 while (current_token == MarkupTokenType.START_ELEMENT) {
2067 if (!push_metadata ()) {
2068 skip_element ();
2069 continue;
2072 m.add_parameter (parse_parameter ());
2074 pop_metadata ();
2076 end_element ("parameters");
2079 if (throws_string == "1") {
2080 m.add_error_type (new ErrorType (null, null));
2082 end_element ("constructor");
2083 return m;
2086 class MethodInfo {
2087 public MethodInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx) {
2088 this.param = param;
2089 this.array_length_idx = array_length_idx;
2090 this.closure_idx = closure_idx;
2091 this.destroy_idx = destroy_idx;
2092 this.vala_idx = 0.0F;
2093 this.keep = true;
2096 public Parameter param;
2097 public float vala_idx;
2098 public int array_length_idx;
2099 public int closure_idx;
2100 public int destroy_idx;
2101 public bool keep;
2104 Symbol parse_function (string element_name) {
2105 start_element (element_name);
2106 string name = element_get_name ();
2107 string cname = reader.get_attribute ("c:identifier");
2108 string throws_string = reader.get_attribute ("throws");
2109 string invoker = reader.get_attribute ("invoker");
2110 next ();
2111 DataType return_type;
2112 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2113 return_type = parse_return_value ();
2114 } else {
2115 return_type = new VoidType ();
2117 return_type = element_get_type (return_type, true);
2119 Symbol s;
2121 if (element_name == "callback") {
2122 s = new Delegate (name, return_type, get_current_src ());
2123 } else {
2124 s = new Method (name, return_type, get_current_src ());
2127 s.access = SymbolAccessibility.PUBLIC;
2128 if (cname != null) {
2129 if (s is Method) {
2130 ((Method) s).set_cname (cname);
2131 } else {
2132 ((Delegate) s).set_cname (cname);
2136 if (element_name == "virtual-method" || element_name == "callback") {
2137 if (s is Method) {
2138 ((Method) s).is_virtual = true;
2141 if (invoker != null){
2142 s.name = invoker;
2144 } else if (element_name == "function") {
2145 ((Method) s).binding = MemberBinding.STATIC;
2148 var parameters = new ArrayList<MethodInfo> ();
2149 var array_length_parameters = new ArrayList<int> ();
2150 var closure_parameters = new ArrayList<int> ();
2151 var destroy_parameters = new ArrayList<int> ();
2152 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2153 start_element ("parameters");
2154 next ();
2156 while (current_token == MarkupTokenType.START_ELEMENT) {
2157 if (!push_metadata ()) {
2158 skip_element ();
2159 continue;
2162 int array_length_idx, closure_idx, destroy_idx;
2163 string scope;
2164 string default_param_name = null;
2165 if (s is Delegate) {
2166 default_param_name = "arg%d".printf (parameters.size);
2168 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, default_param_name);
2169 if (array_length_idx != -1) {
2170 array_length_parameters.add (array_length_idx);
2172 if (closure_idx != -1) {
2173 closure_parameters.add (closure_idx);
2175 if (destroy_idx != -1) {
2176 destroy_parameters.add (destroy_idx);
2179 var info = new MethodInfo(param, array_length_idx, closure_idx, destroy_idx);
2181 if (s is Method && scope == "async") {
2182 var unresolved_type = param.variable_type as UnresolvedType;
2183 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
2184 // GAsync-style method
2185 ((Method) s).coroutine = true;
2186 info.keep = false;
2190 parameters.add (info);
2191 pop_metadata ();
2193 end_element ("parameters");
2195 int i = 0, j=1;
2197 int last = -1;
2198 foreach (MethodInfo info in parameters) {
2199 if (s is Delegate && info.closure_idx == i) {
2200 var d = (Delegate) s;
2201 d.has_target = true;
2202 d.cinstance_parameter_position = (float) j - 0.1;
2203 info.keep = false;
2204 } else if (info.keep
2205 && !array_length_parameters.contains (i)
2206 && !closure_parameters.contains (i)
2207 && !destroy_parameters.contains (i)) {
2208 info.vala_idx = (float) j;
2209 info.keep = true;
2211 /* interpolate for vala_idx between this and last*/
2212 float last_idx = 0.0F;
2213 if (last != -1) {
2214 last_idx = parameters[last].vala_idx;
2216 for (int k=last+1; k < i; k++) {
2217 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
2219 last = i;
2220 j++;
2221 } else {
2222 info.keep = false;
2223 // make sure that vala_idx is always set
2224 // the above if branch does not set vala_idx for
2225 // hidden parameters at the end of the parameter list
2226 info.vala_idx = (j - 1) + (i - last) * 0.1F;
2228 i++;
2231 foreach (MethodInfo info in parameters) {
2232 if (info.keep) {
2234 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
2235 so do it first*/
2236 if (s is Method) {
2237 ((Method) s).add_parameter (info.param);
2238 } else {
2239 ((Delegate) s).add_parameter (info.param);
2242 if (info.array_length_idx != -1) {
2243 if ((info.array_length_idx) >= parameters.size) {
2244 Report.error (get_current_src (), "invalid array_length index");
2245 continue;
2247 info.param.carray_length_parameter_position = parameters[info.array_length_idx].vala_idx;
2248 info.param.set_array_length_cname (parameters[info.array_length_idx].param.name);
2250 if (info.param.variable_type is ArrayType && info.array_length_idx == -1) {
2251 info.param.no_array_length = true;
2254 if (info.closure_idx != -1) {
2255 if ((info.closure_idx) >= parameters.size) {
2256 Report.error (get_current_src (), "invalid closure index");
2257 continue;
2259 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
2261 if (info.destroy_idx != -1) {
2262 if (info.destroy_idx >= parameters.size) {
2263 Report.error (get_current_src (), "invalid destroy index");
2264 continue;
2266 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
2271 if (throws_string == "1") {
2272 s.add_error_type (new ErrorType (null, null));
2274 end_element (element_name);
2275 return s;
2278 Method parse_method (string element_name) {
2279 return this.parse_function (element_name) as Method;
2282 Signal parse_signal () {
2283 start_element ("glib:signal");
2284 string name = reader.get_attribute ("name").replace ("-", "_");
2285 next ();
2286 DataType return_type;
2287 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2288 return_type = parse_return_value ();
2289 } else {
2290 return_type = new VoidType ();
2292 var sig = new Signal (name, return_type, get_current_src ());
2293 sig.access = SymbolAccessibility.PUBLIC;
2294 sig.external = true;
2295 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2296 start_element ("parameters");
2297 next ();
2298 while (current_token == MarkupTokenType.START_ELEMENT) {
2299 if (!push_metadata ()) {
2300 skip_element ();
2301 continue;
2304 sig.add_parameter (parse_parameter ());
2306 pop_metadata ();
2308 end_element ("parameters");
2310 end_element ("glib:signal");
2311 return sig;
2314 Class parse_boxed () {
2315 string name = reader.get_attribute ("name");
2316 if (name == null) {
2317 name = reader.get_attribute ("glib:name");
2319 var cl = new Class (name, get_current_src ());
2320 cl.access = SymbolAccessibility.PUBLIC;
2321 cl.external = true;
2322 cl.is_compact = true;
2324 string cname = reader.get_attribute ("c:type");
2325 if (cname != null) {
2326 cl.set_cname (cname);
2329 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
2330 cl.set_free_function ("g_boxed_free");
2331 cl.set_dup_function ("g_boxed_copy");
2333 next ();
2335 while (current_token == MarkupTokenType.START_ELEMENT) {
2336 if (!push_metadata ()) {
2337 skip_element ();
2338 continue;
2341 if (reader.name == "field") {
2342 cl.add_field (parse_field ());
2343 } else if (reader.name == "constructor") {
2344 parse_constructor ();
2345 } else if (reader.name == "method") {
2346 cl.add_method (parse_method ("method"));
2347 } else {
2348 // error
2349 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2350 skip_element ();
2353 pop_metadata ();
2356 if (current_token != MarkupTokenType.END_ELEMENT) {
2357 // error
2358 Report.error (get_current_src (), "expected end element");
2360 next ();
2361 return cl;
2364 Struct parse_union () {
2365 start_element ("union");
2366 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2367 st.access = SymbolAccessibility.PUBLIC;
2368 st.external = true;
2369 next ();
2371 while (current_token == MarkupTokenType.START_ELEMENT) {
2372 if (!push_metadata ()) {
2373 skip_element ();
2374 continue;
2377 if (reader.name == "field") {
2378 st.add_field (parse_field ());
2379 } else if (reader.name == "constructor") {
2380 parse_constructor ();
2381 } else if (reader.name == "method") {
2382 st.add_method (parse_method ("method"));
2383 } else if (reader.name == "record") {
2384 Struct s = parse_record ();
2385 var fs = s.get_fields ();
2386 foreach (var f in fs) {
2387 f.set_cname (s.get_cname () + "." + f.get_cname ());
2388 f.name = s.name + "_" + f.name;
2389 st.add_field (f);
2391 } else {
2392 // error
2393 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
2394 skip_element ();
2397 pop_metadata ();
2400 end_element ("union");
2401 return st;
2404 Constant parse_constant () {
2405 start_element ("constant");
2406 string name = element_get_name ();
2407 next ();
2408 var type = parse_type ();
2409 var c = new Constant (name, type, null, get_current_src ());
2410 c.access = SymbolAccessibility.PUBLIC;
2411 c.external = true;
2412 end_element ("constant");
2413 return c;
2416 /* Reporting */
2417 void report_unused_metadata (Metadata metadata) {
2418 if (metadata == Metadata.empty) {
2419 return;
2422 if (metadata.args.size == 0 && metadata.children.size == 0) {
2423 Report.warning (metadata.source_reference, "empty metadata");
2424 return;
2427 foreach (var arg_type in metadata.args.get_keys ()) {
2428 var arg = metadata.args[arg_type];
2429 if (!arg.used) {
2430 // if metadata is used and argument is not, then it's a unexpected argument
2431 Report.warning (arg.source_reference, "argument never used");
2435 foreach (var child in metadata.children) {
2436 if (!child.used) {
2437 Report.warning (child.source_reference, "metadata never used");
2438 } else {
2439 report_unused_metadata (child);
2444 /* Post-parsing */
2446 void resolve_gir_symbols () {
2447 // we are remapping unresolved symbols, so create them from concrete symbols
2448 foreach (var map_from in concrete_symbols_map.get_keys ()) {
2449 unresolved_symbols_map[get_unresolved_symbol(map_from)] = concrete_symbols_map[map_from];
2452 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
2453 foreach (var map_from in unresolved_gir_symbols) {
2454 while (map_from != null) {
2455 var map_to = unresolved_symbols_map[map_from];
2456 if (map_to != null) {
2457 // remap the original symbol to match the target
2458 map_from.inner = null;
2459 map_from.name = map_to.name;
2460 if (map_to is UnresolvedSymbol) {
2461 var umap_to = (UnresolvedSymbol) map_to;
2462 while (umap_to.inner != null) {
2463 umap_to = umap_to.inner;
2464 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
2465 map_from = map_from.inner;
2467 } else {
2468 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
2469 map_to = map_to.parent_symbol;
2470 map_from.inner = new UnresolvedSymbol (null, map_to.name);
2471 map_from = map_from.inner;
2474 break;
2476 map_from = map_from.inner;
2481 Symbol? resolve_symbol (Scope parent_scope, UnresolvedSymbol unresolved_symbol) {
2482 // simple symbol resolver, enough for gir
2483 if (unresolved_symbol.inner == null) {
2484 var scope = parent_scope;
2485 while (scope != null) {
2486 var sym = scope.lookup (unresolved_symbol.name);
2487 if (sym != null) {
2488 return sym;
2490 scope = scope.parent_scope;
2492 } else {
2493 var inner = resolve_symbol (parent_scope, unresolved_symbol.inner);
2494 if (inner != null) {
2495 return inner.scope.lookup (unresolved_symbol.name);
2498 return null;
2501 void postprocess_reparenting () {
2502 foreach (UnresolvedSymbol target_unresolved_symbol in symbol_reparent_map.get_keys ()) {
2503 var target_symbol = resolve_symbol (context.root.scope, target_unresolved_symbol);
2504 if (target_symbol == null) {
2505 // create namespaces backward
2506 var sym = target_unresolved_symbol;
2507 var ns = new Namespace (sym.name, sym.source_reference);
2508 var result = ns;
2509 sym = sym.inner;
2510 while (sym != null) {
2511 var res = resolve_symbol (context.root.scope, sym);
2512 if (res != null && !(res is Namespace)) {
2513 result = null;
2514 break;
2516 var parent = res as Namespace;
2517 if (res == null) {
2518 parent = new Namespace (sym.name, sym.source_reference);
2520 if (parent.scope.lookup (ns.name) == null) {
2521 parent.add_namespace (ns);
2523 ns = parent;
2524 sym = sym.inner;
2526 if (result != null && sym == null && context.root.scope.lookup (ns.name) == null) {
2527 // a new root namespace, helpful for a possible non-gobject gir?
2528 context.root.add_namespace (ns);
2530 target_symbol = result;
2532 if (target_symbol == null) {
2533 Report.error (null, "unable to reparent into `%s'".printf (target_unresolved_symbol.to_string ()));
2534 continue;
2536 var symbols = symbol_reparent_map[target_unresolved_symbol];
2537 foreach (var symbol in symbols) {
2538 add_symbol_to_container (target_symbol, symbol);
2543 void postprocess_gtype_callbacks () {
2544 foreach (CallbackScope callback_scope in gtype_callbacks.get_keys ()) {
2545 var gtype = resolve_symbol (callback_scope.parent_namespace.scope, callback_scope.gtype_struct_for) as ObjectTypeSymbol;
2546 if (gtype == null) {
2547 Report.error (null, "unknown symbol `%s'".printf (callback_scope.gtype_struct_for.to_string ()));
2548 continue;
2550 ArrayList<Delegate> callbacks = gtype_callbacks.get (callback_scope);
2551 foreach (Delegate d in callbacks) {
2552 var symbol = gtype.scope.lookup (d.name);
2553 if (symbol == null) {
2554 continue;
2555 } else if (symbol is Method) {
2556 var meth = (Method) symbol;
2557 if (gtype is Class) {
2558 meth.is_virtual = true;
2559 } else if (gtype is Interface) {
2560 meth.is_abstract = true;
2562 } else if (symbol is Signal) {
2563 var sig = (Signal) symbol;
2564 sig.is_virtual = true;
2565 assume_parameter_names (sig, d);
2566 } else if (symbol is Property) {
2567 var prop = (Property) symbol;
2568 prop.is_virtual = true;
2569 } else {
2570 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (d.name, gtype.name));
2576 void postprocess_aliases () {
2577 /* this is unfortunate because <alias> tag has no type information, thus we have
2578 to guess it from the target */
2579 foreach (var alias in aliases) {
2580 DataType base_type = null;
2581 Symbol type_sym = null;
2582 if (alias.base_type is UnresolvedType) {
2583 base_type = alias.base_type;
2584 type_sym = resolve_symbol (alias.parent_namespace.scope, ((UnresolvedType) base_type).unresolved_symbol);
2585 } else if (!(alias.base_type is VoidType)) {
2586 base_type = alias.base_type;
2587 type_sym = base_type.data_type;
2590 if (base_type == null || type_sym == null || type_sym is Struct) {
2591 var st = new Struct (alias.name, alias.source_reference);
2592 st.access = SymbolAccessibility.PUBLIC;
2593 if (base_type != null) {
2594 // threat target="none" as a new struct
2595 st.base_type = base_type;
2597 st.external = true;
2598 alias.parent_namespace.add_struct (st);
2599 } else if (type_sym is Class) {
2600 var cl = new Class (alias.name, alias.source_reference);
2601 cl.access = SymbolAccessibility.PUBLIC;
2602 if (base_type != null) {
2603 cl.add_base_type (base_type);
2605 cl.external = true;
2606 alias.parent_namespace.add_class (cl);
2611 void find_static_method_parent (string cname, Symbol current, ref Symbol best, ref double match, double match_char) {
2612 var old_best = best;
2613 if (current.scope.get_symbol_table () != null) {
2614 foreach (var child in current.scope.get_symbol_table().get_values ()) {
2615 if (child is Struct || child is ObjectTypeSymbol || child is Namespace) {
2616 find_static_method_parent (cname, child, ref best, ref match, match_char);
2620 if (best != old_best) {
2621 // child is better
2622 return;
2625 var current_cprefix = current.get_lower_case_cprefix ();
2626 if (cname.has_prefix (current_cprefix)) {
2627 var current_match = match_char * current_cprefix.length;
2628 if (current_match > match) {
2629 match = current_match;
2630 best = current;
2635 void postprocess_namespace_methods () {
2636 /* transform static methods into instance methods if possible.
2637 In most of cases this is a .gir fault we are going to fix */
2638 foreach (var ns in namespace_methods.get_keys ()) {
2639 var ns_cprefix = ns.get_lower_case_cprefix ();
2640 var methods = namespace_methods[ns];
2641 foreach (var method in methods) {
2642 if (method.parent_node != null) {
2643 // fixed earlier by metadata
2644 continue;
2647 var cname = method.get_cname ();
2649 Parameter first_param = null;
2650 if (method.get_parameters ().size > 0) {
2651 first_param = method.get_parameters()[0];
2653 if (first_param != null && first_param.variable_type is UnresolvedType) {
2654 // check if it's a missed instance method (often happens for structs)
2655 var parent = resolve_symbol (ns.scope, ((UnresolvedType) first_param.variable_type).unresolved_symbol);
2656 if (parent != null && (parent is Struct || parent is ObjectTypeSymbol || parent is Namespace)
2657 && cname.has_prefix (parent.get_lower_case_cprefix ())) {
2658 // instance method
2659 var new_name = method.name.offset (parent.get_lower_case_cprefix().length-ns_cprefix.length);
2660 if (parent.scope.lookup (new_name) == null) {
2661 method.name = new_name;
2662 method.get_parameters().remove_at (0);
2663 method.binding = MemberBinding.INSTANCE;
2664 add_symbol_to_container (parent, method);
2665 } else {
2666 ns.add_method (method);
2668 continue;
2672 double match = 0;
2673 Symbol parent = ns;
2674 find_static_method_parent (cname, ns, ref parent, ref match, 1.0/cname.length);
2675 var new_name = method.name.offset (parent.get_lower_case_cprefix().length-ns_cprefix.length);
2676 if (parent.scope.lookup (new_name) == null) {
2677 method.name = new_name;
2678 add_symbol_to_container (parent, method);
2679 } else {
2680 ns.add_method (method);
2686 /* Hash and equal functions */
2688 static uint unresolved_symbol_hash (void *ptr) {
2689 var sym = (UnresolvedSymbol) ptr;
2690 var builder = new StringBuilder ();
2691 while (sym != null) {
2692 builder.append (sym.name);
2693 sym = sym.inner;
2695 return builder.str.hash ();
2698 static bool unresolved_symbol_equal (void *ptr1, void *ptr2) {
2699 var sym1 = (UnresolvedSymbol) ptr1;
2700 var sym2 = (UnresolvedSymbol) ptr2;
2701 while (sym1 != sym2) {
2702 if (sym1 == null || sym2 == null) {
2703 return false;
2705 if (sym1.name != sym2.name) {
2706 return false;
2708 sym1 = sym1.inner;
2709 sym2 = sym2.inner;
2711 return true;
2714 static uint callback_scope_hash (void *ptr) {
2715 var cs = (CallbackScope) ptr;
2716 return unresolved_symbol_hash (cs.gtype_struct_for);
2719 static bool callback_scope_equal (void *ptr1, void *ptr2) {
2720 var cs1 = (CallbackScope) ptr1;
2721 var cs2 = (CallbackScope) ptr2;
2722 return cs1.parent_namespace == cs2.parent_namespace && unresolved_symbol_equal (cs1.gtype_struct_for, cs2.gtype_struct_for);