json-glib-1.0: Make Object.set_member node parameter owned
[vala-lang.git] / vala / valagirparser.vala
blobe2ef05dc2ecc9c167e22cef9b5c3ab03c756464d
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 if (next () == TokenType.COLON) {
293 next ();
294 return MetadataType.SIGNAL;
296 return MetadataType.PROPERTY;
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.TRUE:
408 expr = new BooleanLiteral (true, src);
409 break;
410 case TokenType.FALSE:
411 expr = new BooleanLiteral (false, src);
412 break;
413 case TokenType.INTEGER_LITERAL:
414 expr = new IntegerLiteral (get_string (), src);
415 break;
416 case TokenType.REAL_LITERAL:
417 expr = new RealLiteral (get_string (), src);
418 break;
419 case TokenType.STRING_LITERAL:
420 expr = new StringLiteral (get_string (), src);
421 break;
422 default:
423 Report.error (src, "expected literal");
424 break;
426 next ();
427 return expr;
430 bool parse_args (Metadata metadata) {
431 while (current != TokenType.EOF && has_space () && !has_newline ()) {
432 SourceReference src;
433 var id = parse_identifier (out src, false);
434 if (id == null) {
435 return false;
437 var arg_type = ArgumentType.from_string (id);
438 if (arg_type == null) {
439 Report.error (src, "unknown argument");
440 return false;
443 if (current != TokenType.ASSIGN) {
444 // threat as `true'
445 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, src), src));
446 continue;
448 next ();
450 Expression expr = parse_literal ();
451 if (expr == null) {
452 return false;
454 metadata.add_argument (arg_type, new Argument (expr, src));
457 return true;
460 bool parse_rule () {
461 var old_end = end;
462 var metadata = parse_pattern ();
463 if (metadata == null) {
464 return false;
467 if (current == TokenType.EOF || old_end.line != end.line) {
468 // eof or new rule
469 return true;
471 return parse_args (metadata);
475 class SymbolInfo {
476 public Symbol symbol;
477 public Metadata metadata;
478 // additional information from GIR
479 public HashMap<string,string> girdata;
482 class CallbackScope {
483 public Namespace parent_namespace;
484 public UnresolvedSymbol gtype_struct_for;
487 class Alias {
488 public string name;
489 public DataType base_type;
490 public Namespace parent_namespace;
491 public SourceReference source_reference;
494 static GLib.Regex type_from_string_regex;
496 MarkupReader reader;
498 CodeContext context;
499 Namespace glib_ns;
501 SourceFile current_source_file;
502 Namespace current_namespace;
503 string current_gtype_struct_for;
504 SourceLocation begin;
505 SourceLocation end;
506 MarkupTokenType current_token;
508 string[] cheader_filenames;
510 ArrayList<Metadata> metadata_stack;
511 Metadata metadata;
512 ArrayList<HashMap<string,string>> girdata_stack;
513 HashMap<string,string> girdata;
515 HashMap<string,ArrayList<SymbolInfo>> current_symbols_info;
517 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
518 HashMap<Symbol,Symbol> concrete_symbols_map = new HashMap<Symbol,Symbol> ();
520 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
521 HashMap<UnresolvedSymbol,ArrayList<Symbol>> symbol_reparent_map = new HashMap<UnresolvedSymbol,ArrayList<Symbol>> (unresolved_symbol_hash, unresolved_symbol_equal);
522 HashMap<Namespace,ArrayList<Method>> namespace_methods = new HashMap<Namespace,ArrayList<Method>> ();
523 HashMap<CallbackScope,ArrayList<Delegate>> gtype_callbacks = new HashMap<CallbackScope,ArrayList<Delegate>> (callback_scope_hash, callback_scope_equal);
524 ArrayList<Alias> aliases = new ArrayList<Alias> ();
525 ArrayList<Interface> interfaces = new ArrayList<Interface> ();
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_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 metadata_stack = new ArrayList<Metadata> ();
574 metadata = Metadata.empty;
575 girdata_stack = new ArrayList<HashMap<string,string>> ();
577 // load metadata
578 string metadata_filename = "%s.metadata".printf (source_file.filename.substring (0, source_file.filename.length - ".gir".length));
579 if (FileUtils.test (metadata_filename, FileTest.EXISTS)) {
580 var metadata_parser = new MetadataParser ();
581 var metadata_file = new SourceFile (context, source_file.file_type, metadata_filename);
582 context.add_source_file (metadata_file);
583 metadata = metadata_parser.parse_metadata (metadata_file);
586 this.current_source_file = source_file;
587 reader = new MarkupReader (source_file.filename);
589 // xml prolog
590 next ();
591 next ();
593 next ();
594 parse_repository ();
596 report_unused_metadata (metadata);
598 reader = null;
599 this.current_source_file = null;
602 void next () {
603 current_token = reader.read_token (out begin, out end);
605 // Skip *all* <doc> tags
606 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "doc")
607 skip_element();
610 void start_element (string name) {
611 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
612 // error
613 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
617 void end_element (string name) {
618 if (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
619 // error
620 Report.error (get_current_src (), "expected end element of `%s'".printf (name));
622 next ();
625 SourceReference get_current_src () {
626 return new SourceReference (this.current_source_file, begin.line, begin.column, end.line, end.column);
629 const string GIR_VERSION = "1.2";
631 void add_symbol_to_container (Symbol container, Symbol sym) {
632 if (container is Class) {
633 unowned Class cl = (Class) container;
635 if (sym is Class) {
636 cl.add_class ((Class) sym);
637 } else if (sym is Constant) {
638 cl.add_constant ((Constant) sym);
639 } else if (sym is Enum) {
640 cl.add_enum ((Enum) sym);
641 } else if (sym is Field) {
642 cl.add_field ((Field) sym);
643 } else if (sym is Method) {
644 cl.add_method ((Method) sym);
645 } else if (sym is Property) {
646 cl.add_property ((Property) sym);
647 } else if (sym is Signal) {
648 cl.add_signal ((Signal) sym);
649 } else if (sym is Struct) {
650 cl.add_struct ((Struct) sym);
652 } else if (container is Enum) {
653 unowned Enum en = (Enum) container;
655 if (sym is EnumValue) {
656 en.add_value ((EnumValue) sym);
657 } else if (sym is Constant) {
658 en.add_constant ((Constant) sym);
659 } else if (sym is Method) {
660 en.add_method ((Method) sym);
662 } else if (container is Interface) {
663 unowned Interface iface = (Interface) container;
665 if (sym is Class) {
666 iface.add_class ((Class) sym);
667 } else if (sym is Constant) {
668 iface.add_constant ((Constant) sym);
669 } else if (sym is Enum) {
670 iface.add_enum ((Enum) sym);
671 } else if (sym is Field) {
672 iface.add_field ((Field) sym);
673 } else if (sym is Method) {
674 iface.add_method ((Method) sym);
675 } else if (sym is Property) {
676 iface.add_property ((Property) sym);
677 } else if (sym is Signal) {
678 iface.add_signal ((Signal) sym);
679 } else if (sym is Struct) {
680 iface.add_struct ((Struct) sym);
682 } else if (container is Namespace) {
683 unowned Namespace ns = (Namespace) container;
685 if (sym is Namespace) {
686 ns.add_namespace ((Namespace) sym);
687 } else if (sym is Class) {
688 ns.add_class ((Class) sym);
689 } else if (sym is Constant) {
690 ns.add_constant ((Constant) sym);
691 } else if (sym is Delegate) {
692 ns.add_delegate ((Delegate) sym);
693 } else if (sym is Enum) {
694 ns.add_enum ((Enum) sym);
695 } else if (sym is ErrorDomain) {
696 ns.add_error_domain ((ErrorDomain) sym);
697 } else if (sym is Field) {
698 ns.add_field ((Field) sym);
699 } else if (sym is Interface) {
700 ns.add_interface ((Interface) sym);
701 } else if (sym is Method) {
702 ns.add_method ((Method) sym);
703 } else if (sym is Namespace) {
704 ns.add_namespace ((Namespace) sym);
705 } else if (sym is Struct) {
706 ns.add_struct ((Struct) sym);
708 } else if (container is Struct) {
709 unowned Struct st = (Struct) container;
711 if (sym is Constant) {
712 st.add_constant ((Constant) sym);
713 } else if (sym is Field) {
714 st.add_field ((Field) sym);
715 } else if (sym is Method) {
716 st.add_method ((Method) sym);
717 } else if (sym is Property) {
718 st.add_property ((Property) sym);
720 } else {
721 Report.error (sym.source_reference, "impossible to add to container `%s'".printf (container.name));
725 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
726 UnresolvedSymbol? sym = null;
727 foreach (unowned string s in symbol_string.split (".")) {
728 sym = new UnresolvedSymbol (sym, s, source_reference);
730 if (sym == null) {
731 Report.error (source_reference, "a symbol must be specified");
733 return sym;
736 UnresolvedSymbol get_unresolved_symbol (Symbol symbol) {
737 if (symbol is UnresolvedSymbol) {
738 return (UnresolvedSymbol) symbol;
740 var sym = new UnresolvedSymbol (null, symbol.name);
741 var result = sym;
742 var cur = symbol.parent_node as Symbol;
743 while (cur != null && cur.name != null) {
744 sym = new UnresolvedSymbol (sym, cur.name);
745 cur = cur.parent_node as Symbol;
747 return result;
750 void set_symbol_mapping (Symbol map_from, Symbol map_to) {
751 // last mapping is the most up-to-date
752 if (map_from is UnresolvedSymbol) {
753 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
754 } else {
755 concrete_symbols_map[map_from] = map_to;
759 void assume_parameter_names (Signal sig, Symbol sym) {
760 Iterator<Parameter> iter;
761 if (sym is Method) {
762 iter = ((Method) sym).get_parameters ().iterator ();
763 } else {
764 iter = ((Delegate) sym).get_parameters ().iterator ();
766 foreach (var param in sig.get_parameters ()) {
767 if (!iter.next ()) {
768 // unreachable for valid GIR
769 break;
771 param.name = iter.get ().name;
775 SymbolInfo? add_symbol_info (Symbol symbol) {
776 var name = symbol.name;
777 if (symbol is CreationMethod && name == null) {
778 name = ".new";
781 var info = new SymbolInfo ();
782 info.symbol = symbol;
783 info.metadata = metadata;
784 info.girdata = girdata;
785 var colliding = current_symbols_info[name];
786 if (colliding == null) {
787 colliding = new ArrayList<SymbolInfo> ();
788 current_symbols_info[name] = colliding;
790 colliding.add (info);
791 return info;
794 SymbolInfo? find_invoker (Method method) {
795 /* most common use case is invoker has at least the given method prefix
796 and the same parameter names */
797 var prefix = "%s_".printf (method.name);
798 foreach (var name in current_symbols_info.get_keys ()) {
799 if (!name.has_prefix (prefix)) {
800 continue;
802 var infos = current_symbols_info[name];
803 foreach (var cinfo in infos) {
804 Method? invoker = cinfo.symbol as Method;
805 if (invoker == null || (method.get_parameters ().size != invoker.get_parameters ().size)) {
806 continue;
808 var iter = invoker.get_parameters ().iterator ();
809 foreach (var param in method.get_parameters ()) {
810 assert (iter.next ());
811 if (param.name != iter.get ().name) {
812 invoker = null;
813 break;
816 if (invoker != null) {
817 return cinfo;
822 return null;
825 void merge (SymbolInfo info, ArrayList<SymbolInfo> colliding, ArrayList<SymbolInfo> merged) {
826 if (info.symbol is Struct) {
827 var gtype_struct_for = info.girdata["glib:is-gtype-struct-for"];
828 if (gtype_struct_for != null && current_symbols_info.contains (gtype_struct_for)) {
829 var iface = current_symbols_info.get (gtype_struct_for).get (0).symbol as Interface;
830 if (iface != null) {
831 // set the interface struct name
832 iface.set_type_cname (((Struct) info.symbol).get_cname ());
834 merged.add (info);
836 } else if (info.symbol is Property) {
837 foreach (var cinfo in colliding) {
838 var sym = cinfo.symbol;
839 if (sym is Signal || sym is Field) {
840 // properties take precedence
841 merged.add (cinfo);
842 } else if (sym is Method) {
843 // assume method is getter
844 merged.add (cinfo);
847 var getter_name = "get_%s".printf (info.symbol.name);
848 if (current_symbols_info.contains (getter_name)) {
849 ((Property) info.symbol).no_accessor_method = false;
851 } else if (info.symbol is Signal) {
852 var sig = (Signal) info.symbol;
853 foreach (var cinfo in colliding) {
854 var sym = cinfo.symbol;
855 if (sym is Method) {
856 var method = (Method) sym;
857 if (method.is_virtual) {
858 sig.is_virtual = true;
859 } else {
860 sig.has_emitter = true;
862 assume_parameter_names (sig, method);
863 merged.add (cinfo);
864 } else if (sym is Field) {
865 merged.add (cinfo);
868 } else if (info.symbol is Method && !(info.symbol is CreationMethod)) {
869 var method = (Method) info.symbol;
870 foreach (var cinfo in colliding) {
871 var sym = cinfo.symbol;
872 if (sym != method && method.is_virtual && sym is Method) {
873 bool different_invoker = false;
874 foreach (var attr in method.attributes) {
875 if (attr.name == "NoWrapper") {
876 /* no invoker but this method has the same name,
877 most probably the invoker has a different name
878 and g-ir-scanner missed it */
879 var invoker = find_invoker (method);
880 if (invoker != null) {
881 method.vfunc_name = method.name;
882 method.name = invoker.symbol.name;
883 method.attributes.remove (attr);
884 merged.add (invoker);
885 different_invoker = true;
886 break;
890 if (!different_invoker) {
891 merged.add (cinfo);
895 } else if (info.symbol is Field) {
896 foreach (var cinfo in colliding) {
897 var sym = cinfo.symbol;
898 if (sym is Method) {
899 // assume method is getter
900 merged.add (cinfo);
904 var field = (Field) info.symbol;
905 if (field.variable_type is ArrayType) {
906 SymbolInfo array_length = null;
907 if (current_symbols_info.contains ("n_%s".printf (field.name))) {
908 array_length = current_symbols_info.get ("n_%s".printf (field.name)).get (0);
909 } else if (current_symbols_info.contains ("%s_length".printf (field.name))) {
910 array_length = current_symbols_info.get ("%s_length".printf (field.name)).get (0);
912 if (array_length != null) {
913 // array has length
914 field.set_array_length_cname (array_length.symbol.name);
915 field.no_array_length = false;
916 merged.add (array_length);
922 void postprocess_symbol (Symbol symbol, Metadata metadata) {
923 // deprecation
924 symbol.replacement = metadata.get_string (ArgumentType.REPLACEMENT);
925 symbol.deprecated_since = element_get_string ("deprecated-version", ArgumentType.DEPRECATED_SINCE);
926 symbol.deprecated = metadata.get_bool (ArgumentType.DEPRECATED) || symbol.replacement != null || symbol.deprecated_since != null;
928 // mark to be reparented
929 if (metadata.has_argument (ArgumentType.PARENT)) {
930 var target_symbol = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
931 var reparent_list = symbol_reparent_map[target_symbol];
932 if (reparent_list == null) {
933 reparent_list = new ArrayList<Symbol>();
934 symbol_reparent_map[target_symbol] = reparent_list;
936 reparent_list.add (symbol);
938 // if referenceable, map unresolved references to point to the new place
939 if (symbol is Namespace || symbol is TypeSymbol) {
940 set_symbol_mapping (symbol, new UnresolvedSymbol (target_symbol, symbol.name));
945 void merge_add_process (Symbol container) {
946 var merged = new ArrayList<SymbolInfo> ();
947 foreach (var name in current_symbols_info.get_keys ()) {
948 var colliding = current_symbols_info[name];
949 foreach (var info in colliding) {
950 merge (info, colliding, merged);
954 foreach (var infos in current_symbols_info.get_values ()) {
955 foreach (var info in infos) {
956 if (merged.contains (info) || info.metadata.get_bool (ArgumentType.HIDDEN)) {
957 continue;
959 if (!info.metadata.has_argument (ArgumentType.PARENT)) {
960 add_symbol_to_container (container, info.symbol);
962 postprocess_symbol (info.symbol, info.metadata);
967 Metadata get_current_metadata () {
968 var name = reader.name;
969 var child_name = reader.get_attribute ("name");
970 if (child_name == null) {
971 return Metadata.empty;
974 var type = MetadataType.GENERIC;
975 if (name == "glib:signal") {
976 type = MetadataType.SIGNAL;
977 } else if (name == "property") {
978 type = MetadataType.PROPERTY;
981 return metadata.match_child (child_name, type);
984 bool push_metadata () {
985 // skip?
986 if (reader.get_attribute ("introspectable") == "0") {
987 return false;
989 var new_metadata = get_current_metadata ();
990 if (new_metadata.get_bool (ArgumentType.SKIP)) {
991 return false;
994 metadata_stack.add (metadata);
995 metadata = new_metadata;
996 girdata_stack.add (girdata);
997 girdata = new HashMap<string,string> (str_hash, str_equal);
999 return true;
1002 void pop_metadata () {
1003 metadata = metadata_stack[metadata_stack.size - 1];
1004 metadata_stack.remove_at (metadata_stack.size - 1);
1005 girdata = girdata_stack[girdata_stack.size - 1];
1006 girdata_stack.remove_at (girdata_stack.size - 1);
1009 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1010 int type_arguments_length = (int) type_arguments.length;
1011 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1013 int depth = 0;
1014 for (var c = 0 ; c < type_arguments_length ; c++) {
1015 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1016 depth++;
1017 current.append_unichar (type_arguments[c]);
1018 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1019 depth--;
1020 current.append_unichar (type_arguments[c]);
1021 } else if (type_arguments[c] == ',') {
1022 if (depth == 0) {
1023 var dt = parse_type_from_string (current.str, true, source_reference);
1024 if (dt == null) {
1025 return false;
1027 parent_type.add_type_argument (dt);
1028 current.truncate ();
1029 } else {
1030 current.append_unichar (type_arguments[c]);
1032 } else {
1033 current.append_unichar (type_arguments[c]);
1037 var dt = parse_type_from_string (current.str, true, source_reference);
1038 if (dt == null) {
1039 return false;
1041 parent_type.add_type_argument (dt);
1043 return true;
1046 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1047 if (type_from_string_regex == null) {
1048 try {
1049 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[(,*)?\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1050 } catch (GLib.RegexError e) {
1051 GLib.error ("Unable to compile regex: %s", e.message);
1055 GLib.MatchInfo match;
1056 if (!type_from_string_regex.match (type_string, 0, out match)) {
1057 Report.error (source_reference, "unable to parse type");
1058 return null;
1061 DataType? type = null;
1063 var ownership_data = match.fetch (1);
1064 var type_name = match.fetch (2);
1065 var type_arguments_data = match.fetch (3);
1066 var pointers_data = match.fetch (4);
1067 var array_data = match.fetch (5);
1068 var nullable_data = match.fetch (6);
1070 var nullable = nullable_data != null && nullable_data.length > 0;
1072 if (ownership_data == null && type_name == "void") {
1073 if (array_data == null && !nullable) {
1074 type = new VoidType (source_reference);
1075 if (pointers_data != null) {
1076 for (int i=0; i < pointers_data.length; i++) {
1077 type = new PointerType (type);
1080 return type;
1081 } else {
1082 Report.error (source_reference, "invalid void type");
1083 return null;
1087 bool value_owned = owned_by_default;
1089 if (ownership_data == "owned") {
1090 if (owned_by_default) {
1091 Report.error (source_reference, "unexpected `owned' keyword");
1092 } else {
1093 value_owned = true;
1095 } else if (ownership_data == "unowned") {
1096 if (owned_by_default) {
1097 value_owned = true;
1098 } else {
1099 Report.error (source_reference, "unexpected `unowned' keyword");
1100 return null;
1104 var sym = parse_symbol_from_string (type_name, source_reference);
1105 if (sym == null) {
1106 return null;
1108 type = new UnresolvedType.from_symbol (sym, source_reference);
1110 if (type_arguments_data != null && type_arguments_data.length > 0) {
1111 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1112 return null;
1116 if (pointers_data != null) {
1117 for (int i=0; i < pointers_data.length; i++) {
1118 type = new PointerType (type);
1122 if (array_data != null) {
1123 type = new ArrayType (type, (int) array_data.length + 1, source_reference);
1126 type.nullable = nullable;
1127 type.value_owned = value_owned;
1128 return type;
1131 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1132 var str = metadata.get_string (arg_type);
1133 if (str == null) {
1134 str = reader.get_attribute (attribute_name);
1136 return str;
1139 DataType? element_get_type (DataType orig_type, bool owned_by_default, out bool changed = null) {
1140 if (&changed != null) {
1141 changed = false;
1144 var type = orig_type;
1146 if (metadata.has_argument (ArgumentType.TYPE)) {
1147 var new_type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1148 if (&changed != null) {
1149 changed = true;
1151 return new_type;
1154 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1155 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1156 if (&changed != null) {
1157 changed = true;
1161 if (type is VoidType) {
1162 return type;
1165 if (metadata.get_bool (ArgumentType.ARRAY)) {
1166 type = new ArrayType (type, 1, type.source_reference);
1167 if (&changed != null) {
1168 changed = true;
1172 if (owned_by_default) {
1173 if (metadata.has_argument (ArgumentType.UNOWNED)) {
1174 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED);
1176 } else {
1177 if (metadata.has_argument (ArgumentType.OWNED)) {
1178 type.value_owned = metadata.get_bool (ArgumentType.OWNED);
1181 if (metadata.has_argument (ArgumentType.NULLABLE)) {
1182 type.nullable = metadata.get_bool (ArgumentType.NULLABLE);
1185 return type;
1188 string? element_get_name (string attribute_name = "name", ArgumentType arg_type = ArgumentType.NAME) {
1189 var name = reader.get_attribute (attribute_name);
1190 var pattern = metadata.get_string (arg_type);
1191 if (pattern != null) {
1192 try {
1193 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1194 GLib.MatchInfo match;
1195 if (!regex.match (name, 0, out match)) {
1196 name = pattern;
1197 } else {
1198 var matched = match.fetch (1);
1199 if (matched != null && matched.length > 0) {
1200 name = matched;
1201 } else {
1202 name = pattern;
1205 } catch (Error e) {
1206 name = pattern;
1209 return name;
1212 void parse_repository () {
1213 start_element ("repository");
1214 if (reader.get_attribute ("version") != GIR_VERSION) {
1215 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1216 return;
1218 next ();
1219 while (current_token == MarkupTokenType.START_ELEMENT) {
1220 if (reader.name == "namespace") {
1221 var ns = parse_namespace ();
1222 if (ns != null) {
1223 context.root.add_namespace (ns);
1225 } else if (reader.name == "include") {
1226 parse_include ();
1227 } else if (reader.name == "package") {
1228 var pkg = parse_package ();
1229 if (context.has_package (pkg)) {
1230 // package already provided elsewhere, stop parsing this GIR
1231 return;
1232 } else {
1233 context.add_package (pkg);
1235 } else if (reader.name == "c:include") {
1236 parse_c_include ();
1237 } else {
1238 // error
1239 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1240 skip_element ();
1243 end_element ("repository");
1246 void parse_include () {
1247 start_element ("include");
1248 var pkg = reader.get_attribute ("name");
1249 var version = reader.get_attribute ("version");
1250 if (version != null) {
1251 pkg = "%s-%s".printf (pkg, version);
1253 // add the package to the queue
1254 context.add_external_package (pkg);
1255 next ();
1256 end_element ("include");
1259 string parse_package () {
1260 start_element ("package");
1261 var pkg = reader.get_attribute ("name");
1262 next ();
1263 end_element ("package");
1264 return pkg;
1267 void parse_c_include () {
1268 start_element ("c:include");
1269 cheader_filenames += reader.get_attribute ("name");
1270 next ();
1271 end_element ("c:include");
1274 void skip_element () {
1275 next ();
1277 int level = 1;
1278 while (level > 0) {
1279 if (current_token == MarkupTokenType.START_ELEMENT) {
1280 level++;
1281 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1282 level--;
1283 } else if (current_token == MarkupTokenType.EOF) {
1284 Report.error (get_current_src (), "unexpected end of file");
1285 break;
1287 next ();
1291 Namespace? parse_namespace () {
1292 start_element ("namespace");
1294 bool new_namespace = false;
1295 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
1296 string namespace_name = cprefix;
1297 string gir_namespace = reader.get_attribute ("name");
1298 string gir_version = reader.get_attribute ("version");
1299 if (namespace_name == null) {
1300 namespace_name = gir_namespace;
1302 current_source_file.gir_namespace = gir_namespace;
1303 current_source_file.gir_version = gir_version;
1305 var ns_metadata = metadata.match_child (gir_namespace);
1306 if (ns_metadata.has_argument (ArgumentType.NAME)) {
1307 namespace_name = ns_metadata.get_string (ArgumentType.NAME);
1310 var ns = context.root.scope.lookup (namespace_name) as Namespace;
1311 if (ns == null) {
1312 ns = new Namespace (namespace_name, get_current_src ());
1313 new_namespace = true;
1314 } else {
1315 if (ns.external_package) {
1316 ns.attributes = null;
1317 ns.source_reference = get_current_src ();
1321 if (gir_namespace != ns.name) {
1322 set_symbol_mapping (new UnresolvedSymbol (null, gir_namespace), ns);
1325 var old_namespace = current_namespace;
1326 current_namespace = ns;
1328 if (cprefix != null) {
1329 ns.add_cprefix (cprefix);
1330 ns.set_lower_case_cprefix (Symbol.camel_case_to_lower_case (cprefix) + "_");
1333 if (ns_metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1334 var val = ns_metadata.get_string (ArgumentType.CHEADER_FILENAME);
1335 foreach (string filename in val.split (",")) {
1336 ns.add_cheader_filename (filename);
1338 } else {
1339 foreach (string c_header in cheader_filenames) {
1340 ns.add_cheader_filename (c_header);
1344 next ();
1345 var current_namespace_methods = namespace_methods[ns];
1346 if (current_namespace_methods == null) {
1347 current_namespace_methods = new ArrayList<Method> ();
1348 namespace_methods[ns] = current_namespace_methods;
1350 var old_symbols_info = current_symbols_info;
1351 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1352 while (current_token == MarkupTokenType.START_ELEMENT) {
1353 if (!push_metadata ()) {
1354 skip_element ();
1355 continue;
1358 if (reader.name == "alias") {
1359 var alias = parse_alias ();
1360 aliases.add (alias);
1361 } else if (reader.name == "enumeration") {
1362 if (reader.get_attribute ("glib:error-quark") != null) {
1363 add_symbol_info (parse_error_domain ());
1364 } else {
1365 add_symbol_info (parse_enumeration ());
1367 } else if (reader.name == "bitfield") {
1368 add_symbol_info (parse_bitfield ());
1369 } else if (reader.name == "function") {
1370 current_namespace_methods.add (parse_method ("function"));
1371 } else if (reader.name == "callback") {
1372 add_symbol_info (parse_callback ());
1373 } else if (reader.name == "record") {
1374 if (reader.get_attribute ("glib:get-type") != null) {
1375 add_symbol_info (parse_boxed ("record"));
1376 } else {
1377 add_symbol_info (parse_record ());
1379 } else if (reader.name == "class") {
1380 add_symbol_info (parse_class ());
1381 } else if (reader.name == "interface") {
1382 var iface = parse_interface ();
1383 add_symbol_info (iface);
1384 interfaces.add (iface);
1385 } else if (reader.name == "glib:boxed") {
1386 add_symbol_info (parse_boxed ("glib:boxed"));
1387 } else if (reader.name == "union") {
1388 add_symbol_info (parse_union ());
1389 } else if (reader.name == "constant") {
1390 add_symbol_info (parse_constant ());
1391 } else {
1392 // error
1393 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
1394 skip_element ();
1397 pop_metadata ();
1399 end_element ("namespace");
1401 merge_add_process (ns);
1402 current_symbols_info = old_symbols_info;
1403 current_namespace = old_namespace;
1405 if (!new_namespace) {
1406 ns = null;
1409 return ns;
1412 Alias parse_alias () {
1413 // alias has no type information
1414 start_element ("alias");
1415 var alias = new Alias ();
1416 alias.source_reference = get_current_src ();
1417 alias.name = reader.get_attribute ("name");
1418 alias.parent_namespace = current_namespace;
1419 next ();
1421 alias.base_type = element_get_type (parse_type (null, null, true), true);
1423 end_element ("alias");
1424 return alias;
1427 private void calculate_common_prefix (ref string common_prefix, string cname) {
1428 if (common_prefix == null) {
1429 common_prefix = cname;
1430 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
1431 // FIXME: could easily be made faster
1432 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1434 } else {
1435 while (!cname.has_prefix (common_prefix)) {
1436 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1439 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
1440 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
1441 // enum values may not consist solely of digits
1442 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
1446 Enum parse_enumeration () {
1447 start_element ("enumeration");
1448 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1449 en.access = SymbolAccessibility.PUBLIC;
1451 string enum_cname = reader.get_attribute ("c:type");
1452 if (enum_cname != null) {
1453 en.set_cname (enum_cname);
1456 next ();
1458 string common_prefix = null;
1460 while (current_token == MarkupTokenType.START_ELEMENT) {
1461 if (!push_metadata ()) {
1462 skip_element ();
1463 continue;
1466 if (reader.name == "member") {
1467 var ev = parse_enumeration_member ();
1468 en.add_value (ev);
1469 calculate_common_prefix (ref common_prefix, ev.get_cname ());
1470 } else {
1471 // error
1472 Report.error (get_current_src (), "unknown child element `%s' in `enumaration'".printf (reader.name));
1473 skip_element ();
1476 pop_metadata ();
1479 en.set_cprefix (common_prefix);
1481 end_element ("enumeration");
1482 return en;
1485 ErrorDomain parse_error_domain () {
1486 start_element ("enumeration");
1488 var ed = new ErrorDomain (reader.get_attribute ("name"), get_current_src ());
1489 ed.access = SymbolAccessibility.PUBLIC;
1491 string enum_cname = reader.get_attribute ("c:type");
1492 if (enum_cname != null) {
1493 ed.set_cname (enum_cname);
1496 next ();
1498 string common_prefix = null;
1500 while (current_token == MarkupTokenType.START_ELEMENT) {
1501 if (!push_metadata ()) {
1502 skip_element ();
1503 continue;
1506 if (reader.name == "member") {
1507 ErrorCode ec = parse_error_member ();
1508 ed.add_code (ec);
1509 calculate_common_prefix (ref common_prefix, ec.get_cname ());
1510 } else {
1511 // error
1512 Report.error (get_current_src (), "unknown child element `%s' in `enumeration'".printf (reader.name));
1513 skip_element ();
1516 pop_metadata ();
1519 ed.set_cprefix (common_prefix);
1521 end_element ("enumeration");
1522 return ed;
1525 Enum parse_bitfield () {
1526 start_element ("bitfield");
1527 var en = new Enum (reader.get_attribute ("name"), get_current_src ());
1528 en.access = SymbolAccessibility.PUBLIC;
1529 next ();
1530 while (current_token == MarkupTokenType.START_ELEMENT) {
1531 if (!push_metadata ()) {
1532 skip_element ();
1533 continue;
1536 if (reader.name == "member") {
1537 en.add_value (parse_enumeration_member ());
1538 } else {
1539 // error
1540 Report.error (get_current_src (), "unknown child element `%s' in `bitfield'".printf (reader.name));
1541 skip_element ();
1544 pop_metadata ();
1546 end_element ("bitfield");
1547 return en;
1550 EnumValue parse_enumeration_member () {
1551 start_element ("member");
1552 var ev = new EnumValue (reader.get_attribute ("name").up ().replace ("-", "_"), null, get_current_src ());
1553 ev.set_cname (reader.get_attribute ("c:identifier"));
1554 next ();
1555 end_element ("member");
1556 return ev;
1559 ErrorCode parse_error_member () {
1560 start_element ("member");
1562 ErrorCode ec;
1563 string name = reader.get_attribute ("name").up ().replace ("-", "_");
1564 string value = reader.get_attribute ("value");
1565 if (value != null) {
1566 ec = new ErrorCode.with_value (name, new IntegerLiteral (value));
1567 } else {
1568 ec = new ErrorCode (name);
1571 next ();
1572 end_element ("member");
1573 return ec;
1576 DataType parse_return_value (out string? ctype = null) {
1577 start_element ("return-value");
1578 string transfer = reader.get_attribute ("transfer-ownership");
1579 string allow_none = reader.get_attribute ("allow-none");
1580 next ();
1581 var transfer_elements = transfer == "full";
1582 var type = &ctype != null ? parse_type(out ctype, null, transfer_elements) : parse_type (null, null, transfer_elements);
1583 if (transfer == "full" || transfer == "container") {
1584 type.value_owned = true;
1586 if (allow_none == "1") {
1587 type.nullable = true;
1589 end_element ("return-value");
1590 return type;
1593 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) {
1594 Parameter param;
1596 if (&array_length_idx != null) {
1597 array_length_idx = -1;
1599 if (&closure_idx != null) {
1600 closure_idx = -1;
1602 if (&destroy_idx != null) {
1603 destroy_idx = -1;
1606 start_element ("parameter");
1607 string name = reader.get_attribute ("name");
1608 if (name == null) {
1609 name = default_name;
1611 string direction = null;
1612 if (metadata.has_argument (ArgumentType.OUT)) {
1613 if (metadata.get_bool (ArgumentType.OUT)) {
1614 direction = "out";
1615 } // null otherwise
1616 } else if (metadata.has_argument (ArgumentType.REF)) {
1617 if (metadata.get_bool (ArgumentType.REF)) {
1618 direction = "inout";
1619 } // null otherwise
1620 } else {
1621 direction = reader.get_attribute ("direction");
1623 string transfer = reader.get_attribute ("transfer-ownership");
1624 string allow_none = reader.get_attribute ("allow-none");
1626 if (&scope != null) {
1627 scope = reader.get_attribute ("scope");
1630 string closure = reader.get_attribute ("closure");
1631 string destroy = reader.get_attribute ("destroy");
1632 if (closure != null && &closure_idx != null) {
1633 closure_idx = int.parse (closure);
1635 if (destroy != null && &destroy_idx != null) {
1636 destroy_idx = int.parse (destroy);
1639 next ();
1640 if (reader.name == "varargs") {
1641 start_element ("varargs");
1642 next ();
1643 param = new Parameter.with_ellipsis (get_current_src ());
1644 end_element ("varargs");
1645 } else {
1646 string ctype;
1647 var type = parse_type (out ctype, out array_length_idx, transfer == "full");
1648 bool changed;
1649 type = element_get_type (type, false, out changed);
1650 if (!changed) {
1651 // discard ctype, duplicated information
1652 ctype = null;
1655 if (type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_POS)) {
1656 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_POS);
1659 if (transfer == "full" || transfer == "container" || destroy != null) {
1660 type.value_owned = true;
1662 if (allow_none == "1") {
1663 type.nullable = true;
1665 param = new Parameter (name, type, get_current_src ());
1666 param.ctype = ctype;
1667 if (direction == "out") {
1668 param.direction = ParameterDirection.OUT;
1669 } else if (direction == "inout") {
1670 param.direction = ParameterDirection.REF;
1672 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
1674 end_element ("parameter");
1675 return param;
1678 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) {
1679 bool is_array = false;
1680 string type_name = reader.get_attribute ("name");
1682 if (&array_length_index != null) {
1683 array_length_index = -1;
1686 if (reader.name == "array") {
1687 is_array = true;
1688 start_element ("array");
1690 if (!(type_name == "GLib.Array" || type_name == "GLib.PtrArray")) {
1691 if (reader.get_attribute ("length") != null
1692 && &array_length_index != null) {
1693 array_length_index = int.parse (reader.get_attribute ("length"));
1695 next ();
1696 var element_type = parse_type ();
1697 end_element ("array");
1698 return new ArrayType (element_type, 1, null);
1700 } else if (reader.name == "callback"){
1701 var callback = parse_callback ();
1702 return new DelegateType (callback);
1703 } else {
1704 start_element ("type");
1707 if (&ctype != null) {
1708 ctype = reader.get_attribute("c:type");
1711 next ();
1713 if (type_name == "GLib.PtrArray"
1714 && current_token == MarkupTokenType.START_ELEMENT) {
1715 type_name = "GLib.GenericArray";
1718 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated);
1720 // type arguments / element types
1721 while (current_token == MarkupTokenType.START_ELEMENT) {
1722 var element_type = parse_type ();
1723 element_type.value_owned = transfer_elements;
1724 type.add_type_argument (element_type);
1727 end_element (is_array ? "array" : "type");
1728 return type;
1731 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null) {
1732 if (&no_array_length != null) {
1733 no_array_length = false;
1735 if (&array_null_terminated != null) {
1736 array_null_terminated = false;
1739 DataType type;
1740 if (type_name == "none") {
1741 type = new VoidType (get_current_src ());
1742 } else if (type_name == "gpointer") {
1743 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
1744 } else if (type_name == "GObject.Strv") {
1745 type = new ArrayType (new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string")), 1, get_current_src ());
1746 if (&no_array_length != null) {
1747 no_array_length = true;
1749 if (&array_null_terminated != null) {
1750 array_null_terminated = true;
1752 } else {
1753 bool known_type = true;
1754 if (type_name == "utf8") {
1755 type_name = "string";
1756 } else if (type_name == "gboolean") {
1757 type_name = "bool";
1758 } else if (type_name == "gchar") {
1759 type_name = "char";
1760 } else if (type_name == "gshort") {
1761 type_name = "short";
1762 } else if (type_name == "gushort") {
1763 type_name = "ushort";
1764 } else if (type_name == "gint") {
1765 type_name = "int";
1766 } else if (type_name == "guint") {
1767 type_name = "uint";
1768 } else if (type_name == "glong") {
1769 type_name = "long";
1770 } else if (type_name == "gulong") {
1771 type_name = "ulong";
1772 } else if (type_name == "gint8") {
1773 type_name = "int8";
1774 } else if (type_name == "guint8") {
1775 type_name = "uint8";
1776 } else if (type_name == "gint16") {
1777 type_name = "int16";
1778 } else if (type_name == "guint16") {
1779 type_name = "uint16";
1780 } else if (type_name == "gint32") {
1781 type_name = "int32";
1782 } else if (type_name == "guint32") {
1783 type_name = "uint32";
1784 } else if (type_name == "gint64") {
1785 type_name = "int64";
1786 } else if (type_name == "guint64") {
1787 type_name = "uint64";
1788 } else if (type_name == "gfloat") {
1789 type_name = "float";
1790 } else if (type_name == "gdouble") {
1791 type_name = "double";
1792 } else if (type_name == "filename") {
1793 type_name = "string";
1794 } else if (type_name == "GLib.offset") {
1795 type_name = "int64";
1796 } else if (type_name == "gsize") {
1797 type_name = "size_t";
1798 } else if (type_name == "gssize") {
1799 type_name = "ssize_t";
1800 } else if (type_name == "GType") {
1801 type_name = "GLib.Type";
1802 } else if (type_name == "GLib.String") {
1803 type_name = "GLib.StringBuilder";
1804 } else if (type_name == "GObject.Class") {
1805 type_name = "GLib.ObjectClass";
1806 } else if (type_name == "GLib.unichar") {
1807 type_name = "unichar";
1808 } else if (type_name == "GLib.Data") {
1809 type_name = "GLib.Datalist";
1810 } else if (type_name == "Atk.ImplementorIface") {
1811 type_name = "Atk.Implementor";
1812 } else {
1813 known_type = false;
1815 var sym = parse_symbol_from_string (type_name, get_current_src ());
1816 type = new UnresolvedType.from_symbol (sym, get_current_src ());
1817 if (!known_type) {
1818 unresolved_gir_symbols.add (sym);
1822 return type;
1825 Struct parse_record () {
1826 start_element ("record");
1827 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
1828 st.external = true;
1829 st.access = SymbolAccessibility.PUBLIC;
1831 string cname = reader.get_attribute ("c:type");
1832 if (cname != null) {
1833 st.set_cname (cname);
1836 current_gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
1837 if (current_gtype_struct_for != null) {
1838 girdata["glib:is-gtype-struct-for"] = current_gtype_struct_for;
1841 next ();
1842 while (current_token == MarkupTokenType.START_ELEMENT) {
1843 if (!push_metadata ()) {
1844 skip_element ();
1845 continue;
1848 if (reader.name == "field") {
1849 if (reader.get_attribute ("name") != "priv") {
1850 st.add_field (parse_field ());
1851 } else {
1852 skip_element ();
1854 } else if (reader.name == "constructor") {
1855 parse_constructor ();
1856 } else if (reader.name == "method") {
1857 st.add_method (parse_method ("method"));
1858 } else if (reader.name == "union") {
1859 Struct s = parse_union ();
1860 var s_fields = s.get_fields ();
1861 foreach (var f in s_fields) {
1862 f.set_cname (s.get_cname () + "." + f.get_cname ());
1863 f.name = s.name + "_" + f.name;
1864 st.add_field (f);
1866 } else {
1867 // error
1868 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
1869 skip_element ();
1872 pop_metadata ();
1874 end_element ("record");
1876 current_gtype_struct_for = null;
1877 return st;
1880 Class parse_class () {
1881 start_element ("class");
1882 var cl = new Class (reader.get_attribute ("name"), get_current_src ());
1883 cl.access = SymbolAccessibility.PUBLIC;
1884 cl.external = true;
1886 string cname = reader.get_attribute ("c:type");
1887 if (cname != null) {
1888 cl.set_cname (cname);
1891 string parent = reader.get_attribute ("parent");
1892 if (parent != null) {
1893 cl.add_base_type (parse_type_from_gir_name (parent));
1896 next ();
1897 var first_field = true;
1898 var old_symbols_info = current_symbols_info;
1899 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1900 while (current_token == MarkupTokenType.START_ELEMENT) {
1901 if (!push_metadata ()) {
1902 skip_element ();
1903 continue;
1906 if (reader.name == "implements") {
1907 start_element ("implements");
1908 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
1909 next ();
1910 end_element ("implements");
1911 } else if (reader.name == "constant") {
1912 add_symbol_info (parse_constant ());
1913 } else if (reader.name == "field") {
1914 if (first_field && parent != null) {
1915 // first field is guaranteed to be the parent instance
1916 skip_element ();
1917 } else {
1918 add_symbol_info (parse_field ());
1920 first_field = false;
1921 } else if (reader.name == "property") {
1922 add_symbol_info (parse_property ());
1923 } else if (reader.name == "constructor") {
1924 add_symbol_info (parse_constructor (cname));
1925 } else if (reader.name == "function") {
1926 add_symbol_info (parse_method ("function"));
1927 } else if (reader.name == "method") {
1928 add_symbol_info (parse_method ("method"));
1929 } else if (reader.name == "virtual-method") {
1930 add_symbol_info (parse_method ("virtual-method"));
1931 } else if (reader.name == "union") {
1932 Struct s = parse_union ();
1933 var s_fields = s.get_fields ();
1934 foreach (var f in s_fields) {
1935 f.set_cname (s.get_cname () + "." + f.get_cname ());
1936 f.name = s.name + "_" + f.name;
1937 add_symbol_info (f);
1939 } else if (reader.name == "glib:signal") {
1940 add_symbol_info (parse_signal ());
1941 } else {
1942 // error
1943 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
1944 skip_element ();
1947 pop_metadata ();
1950 merge_add_process (cl);
1951 current_symbols_info = old_symbols_info;
1953 handle_async_methods (cl);
1955 end_element ("class");
1956 return cl;
1959 Interface parse_interface () {
1960 start_element ("interface");
1961 var iface = new Interface (element_get_name (), get_current_src ());
1962 iface.access = SymbolAccessibility.PUBLIC;
1963 iface.external = true;
1965 string cname = reader.get_attribute ("c:type");
1966 if (cname != null) {
1967 iface.set_cname (cname);
1970 next ();
1971 var old_symbols_info = current_symbols_info;
1972 current_symbols_info = new HashMap<string,ArrayList<SymbolInfo>> (str_hash, str_equal);
1973 while (current_token == MarkupTokenType.START_ELEMENT) {
1974 if (!push_metadata ()) {
1975 skip_element ();
1976 continue;
1979 if (reader.name == "prerequisite") {
1980 start_element ("prerequisite");
1981 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
1982 next ();
1983 end_element ("prerequisite");
1984 } else if (reader.name == "field") {
1985 add_symbol_info (parse_field ());
1986 } else if (reader.name == "property") {
1987 add_symbol_info (parse_property ());
1988 } else if (reader.name == "virtual-method") {
1989 add_symbol_info (parse_method ("virtual-method"));
1990 } else if (reader.name == "function") {
1991 add_symbol_info (parse_method ("function"));
1992 } else if (reader.name == "method") {
1993 add_symbol_info (parse_method ("method"));
1994 } else if (reader.name == "glib:signal") {
1995 add_symbol_info (parse_signal ());
1996 } else {
1997 // error
1998 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
1999 skip_element ();
2002 pop_metadata ();
2005 merge_add_process (iface);
2006 current_symbols_info = old_symbols_info;
2008 handle_async_methods (iface);
2010 end_element ("interface");
2011 return iface;
2014 void handle_async_methods (ObjectTypeSymbol type_symbol) {
2015 var methods = type_symbol.get_methods ();
2016 for (int method_n = 0 ; method_n < methods.size ; method_n++) {
2017 var m = methods.get (method_n);
2019 if (m.coroutine) {
2020 string finish_method_base;
2021 if (m.name.has_suffix ("_async")) {
2022 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
2023 } else {
2024 finish_method_base = m.name;
2026 var finish_method = type_symbol.scope.lookup (finish_method_base + "_finish") as Method;
2028 // check if the method is using non-standard finish method name
2029 if (finish_method == null) {
2030 var method_cname = m.get_finish_cname ();
2031 foreach (Method method in type_symbol.get_methods ()) {
2032 if (method.get_cname () == method_cname) {
2033 finish_method = method;
2034 break;
2039 if (finish_method != null) {
2040 m.return_type = finish_method.return_type.copy ();
2041 m.no_array_length = finish_method.no_array_length;
2042 m.array_null_terminated = finish_method.array_null_terminated;
2043 foreach (var param in finish_method.get_parameters ()) {
2044 if (param.direction == ParameterDirection.OUT) {
2045 var async_param = param.copy ();
2046 if (m.scope.lookup (param.name) != null) {
2047 // parameter name conflict
2048 async_param.name += "_out";
2050 m.add_parameter (async_param);
2053 foreach (DataType error_type in finish_method.get_error_types ()) {
2054 m.add_error_type (error_type.copy ());
2056 if (methods.index_of (finish_method) < method_n) {
2057 method_n--;
2059 type_symbol.scope.remove (finish_method.name);
2060 methods.remove (finish_method);
2066 Field parse_field () {
2067 start_element ("field");
2068 string name = reader.get_attribute ("name");
2069 string allow_none = reader.get_attribute ("allow-none");
2070 next ();
2071 var type = parse_type ();
2072 type = element_get_type (type, true);
2073 if (type is DelegateType && current_gtype_struct_for != null) {
2074 // virtual
2075 var callback_scope = new CallbackScope ();
2076 callback_scope.parent_namespace = current_namespace;
2077 callback_scope.gtype_struct_for = parse_symbol_from_string (current_gtype_struct_for);
2078 ArrayList<Delegate> callbacks = gtype_callbacks.get (callback_scope);
2079 if (callbacks == null) {
2080 callbacks = new ArrayList<Delegate> ();
2081 gtype_callbacks.set (callback_scope, callbacks);
2083 callbacks.add (((DelegateType) type).delegate_symbol);
2085 var field = new Field (name, type, null, get_current_src ());
2086 field.access = SymbolAccessibility.PUBLIC;
2087 field.no_array_length = true;
2088 if (allow_none == "1") {
2089 type.nullable = true;
2091 end_element ("field");
2092 return field;
2095 Property parse_property () {
2096 start_element ("property");
2097 string name = reader.get_attribute ("name").replace ("-", "_");
2098 string readable = reader.get_attribute ("readable");
2099 string writable = reader.get_attribute ("writable");
2100 string construct_ = reader.get_attribute ("construct");
2101 string construct_only = reader.get_attribute ("construct-only");
2102 next ();
2103 bool no_array_length;
2104 bool array_null_terminated;
2105 var type = parse_type (null, null, false, out no_array_length, out array_null_terminated);
2106 var prop = new Property (name, type, null, null, get_current_src ());
2107 prop.access = SymbolAccessibility.PUBLIC;
2108 prop.no_accessor_method = true;
2109 prop.no_array_length = no_array_length;
2110 prop.array_null_terminated = array_null_terminated;
2111 if (readable != "0") {
2112 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
2114 if (writable == "1" || construct_only == "1") {
2115 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
2117 end_element ("property");
2118 return prop;
2121 Delegate parse_callback () {
2122 return this.parse_function ("callback") as Delegate;
2125 Method parse_constructor (string? parent_ctype = null) {
2126 start_element ("constructor");
2127 string name = element_get_name ();
2128 string throws_string = reader.get_attribute ("throws");
2129 string cname = reader.get_attribute ("c:identifier");
2130 next ();
2132 string? ctype;
2133 parse_return_value (out ctype);
2135 var m = new CreationMethod (null, name, get_current_src ());
2136 m.access = SymbolAccessibility.PUBLIC;
2137 m.has_construct_function = false;
2138 if (ctype != null && (parent_ctype == null || ctype != parent_ctype + "*")) {
2139 m.custom_return_type_cname = ctype;
2141 if (m.name == "new") {
2142 m.name = null;
2143 } else if (m.name.has_prefix ("new_")) {
2144 m.name = m.name.substring ("new_".length);
2146 if (cname != null) {
2147 m.set_cname (cname);
2149 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2150 start_element ("parameters");
2151 next ();
2152 while (current_token == MarkupTokenType.START_ELEMENT) {
2153 if (!push_metadata ()) {
2154 skip_element ();
2155 continue;
2158 m.add_parameter (parse_parameter ());
2160 pop_metadata ();
2162 end_element ("parameters");
2165 if (throws_string == "1") {
2166 m.add_error_type (new ErrorType (null, null));
2168 end_element ("constructor");
2169 return m;
2172 class MethodInfo {
2173 public MethodInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx) {
2174 this.param = param;
2175 this.array_length_idx = array_length_idx;
2176 this.closure_idx = closure_idx;
2177 this.destroy_idx = destroy_idx;
2178 this.vala_idx = 0.0F;
2179 this.keep = true;
2182 public Parameter param;
2183 public float vala_idx;
2184 public int array_length_idx;
2185 public int closure_idx;
2186 public int destroy_idx;
2187 public bool keep;
2190 Symbol parse_function (string element_name) {
2191 start_element (element_name);
2192 string name = element_get_name ();
2193 string cname = reader.get_attribute ("c:identifier");
2194 string throws_string = reader.get_attribute ("throws");
2195 string invoker = reader.get_attribute ("invoker");
2196 next ();
2197 DataType return_type;
2198 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2199 return_type = parse_return_value ();
2200 } else {
2201 return_type = new VoidType ();
2203 return_type = element_get_type (return_type, true);
2205 Symbol s;
2207 if (element_name == "callback") {
2208 s = new Delegate (name, return_type, get_current_src ());
2209 } else {
2210 s = new Method (name, return_type, get_current_src ());
2213 s.access = SymbolAccessibility.PUBLIC;
2214 if (cname != null) {
2215 if (s is Method) {
2216 ((Method) s).set_cname (cname);
2217 } else {
2218 ((Delegate) s).set_cname (cname);
2222 if (element_name == "virtual-method" || element_name == "callback") {
2223 if (s is Method) {
2224 ((Method) s).is_virtual = true;
2225 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
2226 s.attributes.append (new Attribute ("NoWrapper", s.source_reference));
2230 if (invoker != null) {
2231 s.name = invoker;
2233 } else if (element_name == "function") {
2234 ((Method) s).binding = MemberBinding.STATIC;
2237 if (s is Method) {
2238 var method = (Method) s;
2239 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
2240 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
2241 method.is_abstract = false;
2242 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
2243 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
2244 method.is_virtual = false;
2246 method.vfunc_name = metadata.get_string (ArgumentType.VFUNC_NAME);
2249 var parameters = new ArrayList<MethodInfo> ();
2250 var array_length_parameters = new ArrayList<int> ();
2251 var closure_parameters = new ArrayList<int> ();
2252 var destroy_parameters = new ArrayList<int> ();
2253 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2254 start_element ("parameters");
2255 next ();
2257 while (current_token == MarkupTokenType.START_ELEMENT) {
2258 if (!push_metadata ()) {
2259 skip_element ();
2260 continue;
2263 int array_length_idx, closure_idx, destroy_idx;
2264 string scope;
2265 string default_param_name = null;
2266 default_param_name = "arg%d".printf (parameters.size);
2267 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, default_param_name);
2268 if (array_length_idx != -1) {
2269 array_length_parameters.add (array_length_idx);
2271 if (closure_idx != -1) {
2272 closure_parameters.add (closure_idx);
2274 if (destroy_idx != -1) {
2275 destroy_parameters.add (destroy_idx);
2278 var info = new MethodInfo(param, array_length_idx, closure_idx, destroy_idx);
2280 if (s is Method && scope == "async") {
2281 var unresolved_type = param.variable_type as UnresolvedType;
2282 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
2283 // GAsync-style method
2284 ((Method) s).coroutine = true;
2285 info.keep = false;
2289 parameters.add (info);
2290 pop_metadata ();
2292 end_element ("parameters");
2294 int i = 0, j=1;
2296 int last = -1;
2297 foreach (MethodInfo info in parameters) {
2298 if (s is Delegate && info.closure_idx == i) {
2299 var d = (Delegate) s;
2300 d.has_target = true;
2301 d.cinstance_parameter_position = (float) j - 0.1;
2302 info.keep = false;
2303 } else if (info.keep
2304 && !array_length_parameters.contains (i)
2305 && !closure_parameters.contains (i)
2306 && !destroy_parameters.contains (i)) {
2307 info.vala_idx = (float) j;
2308 info.keep = true;
2310 /* interpolate for vala_idx between this and last*/
2311 float last_idx = 0.0F;
2312 if (last != -1) {
2313 last_idx = parameters[last].vala_idx;
2315 for (int k=last+1; k < i; k++) {
2316 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
2318 last = i;
2319 j++;
2320 } else {
2321 info.keep = false;
2322 // make sure that vala_idx is always set
2323 // the above if branch does not set vala_idx for
2324 // hidden parameters at the end of the parameter list
2325 info.vala_idx = (j - 1) + (i - last) * 0.1F;
2327 i++;
2330 foreach (MethodInfo info in parameters) {
2331 if (info.keep) {
2333 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
2334 so do it first*/
2335 if (s is Method) {
2336 ((Method) s).add_parameter (info.param);
2337 } else {
2338 ((Delegate) s).add_parameter (info.param);
2341 if (info.array_length_idx != -1) {
2342 if ((info.array_length_idx) >= parameters.size) {
2343 Report.error (get_current_src (), "invalid array_length index");
2344 continue;
2346 info.param.carray_length_parameter_position = parameters[info.array_length_idx].vala_idx;
2347 info.param.set_array_length_cname (parameters[info.array_length_idx].param.name);
2349 if (info.param.variable_type is ArrayType && info.array_length_idx == -1) {
2350 info.param.no_array_length = true;
2353 if (info.closure_idx != -1) {
2354 if ((info.closure_idx) >= parameters.size) {
2355 Report.error (get_current_src (), "invalid closure index");
2356 continue;
2358 info.param.cdelegate_target_parameter_position = parameters[info.closure_idx].vala_idx;
2360 if (info.destroy_idx != -1) {
2361 if (info.destroy_idx >= parameters.size) {
2362 Report.error (get_current_src (), "invalid destroy index");
2363 continue;
2365 info.param.cdestroy_notify_parameter_position = parameters[info.destroy_idx].vala_idx;
2370 if (throws_string == "1") {
2371 s.add_error_type (new ErrorType (null, null));
2373 end_element (element_name);
2374 return s;
2377 Method parse_method (string element_name) {
2378 return this.parse_function (element_name) as Method;
2381 Signal parse_signal () {
2382 start_element ("glib:signal");
2383 string name = reader.get_attribute ("name").replace ("-", "_");
2384 next ();
2385 DataType return_type;
2386 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
2387 return_type = parse_return_value ();
2388 } else {
2389 return_type = new VoidType ();
2391 var sig = new Signal (name, return_type, get_current_src ());
2392 sig.access = SymbolAccessibility.PUBLIC;
2393 sig.external = true;
2394 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
2395 start_element ("parameters");
2396 next ();
2397 while (current_token == MarkupTokenType.START_ELEMENT) {
2398 if (!push_metadata ()) {
2399 skip_element ();
2400 continue;
2403 sig.add_parameter (parse_parameter ());
2405 pop_metadata ();
2407 end_element ("parameters");
2409 end_element ("glib:signal");
2410 return sig;
2413 Class parse_boxed (string element_name) {
2414 start_element (element_name);
2415 string name = reader.get_attribute ("name");
2416 if (name == null) {
2417 name = reader.get_attribute ("glib:name");
2419 var cl = new Class (name, get_current_src ());
2420 cl.access = SymbolAccessibility.PUBLIC;
2421 cl.external = true;
2422 cl.is_compact = true;
2424 string cname = reader.get_attribute ("c:type");
2425 if (cname != null) {
2426 cl.set_cname (cname);
2429 cl.set_type_id ("%s ()".printf (reader.get_attribute ("glib:get-type")));
2430 cl.set_free_function ("g_boxed_free");
2431 cl.set_dup_function ("g_boxed_copy");
2433 next ();
2435 while (current_token == MarkupTokenType.START_ELEMENT) {
2436 if (!push_metadata ()) {
2437 skip_element ();
2438 continue;
2441 if (reader.name == "field") {
2442 cl.add_field (parse_field ());
2443 } else if (reader.name == "constructor") {
2444 parse_constructor ();
2445 } else if (reader.name == "method") {
2446 cl.add_method (parse_method ("method"));
2447 } else {
2448 // error
2449 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2450 skip_element ();
2453 pop_metadata ();
2455 end_element (element_name);
2456 return cl;
2459 Struct parse_union () {
2460 start_element ("union");
2461 var st = new Struct (reader.get_attribute ("name"), get_current_src ());
2462 st.access = SymbolAccessibility.PUBLIC;
2463 st.external = true;
2464 next ();
2466 while (current_token == MarkupTokenType.START_ELEMENT) {
2467 if (!push_metadata ()) {
2468 skip_element ();
2469 continue;
2472 if (reader.name == "field") {
2473 st.add_field (parse_field ());
2474 } else if (reader.name == "constructor") {
2475 parse_constructor ();
2476 } else if (reader.name == "method") {
2477 st.add_method (parse_method ("method"));
2478 } else if (reader.name == "record") {
2479 Struct s = parse_record ();
2480 var fs = s.get_fields ();
2481 foreach (var f in fs) {
2482 f.set_cname (s.get_cname () + "." + f.get_cname ());
2483 f.name = s.name + "_" + f.name;
2484 st.add_field (f);
2486 } else {
2487 // error
2488 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
2489 skip_element ();
2492 pop_metadata ();
2495 end_element ("union");
2496 return st;
2499 Constant parse_constant () {
2500 start_element ("constant");
2501 string name = element_get_name ();
2502 next ();
2503 var type = parse_type ();
2504 var c = new Constant (name, type, null, get_current_src ());
2505 c.access = SymbolAccessibility.PUBLIC;
2506 c.external = true;
2507 end_element ("constant");
2508 return c;
2511 /* Reporting */
2512 void report_unused_metadata (Metadata metadata) {
2513 if (metadata == Metadata.empty) {
2514 return;
2517 if (metadata.args.size == 0 && metadata.children.size == 0) {
2518 Report.warning (metadata.source_reference, "empty metadata");
2519 return;
2522 foreach (var arg_type in metadata.args.get_keys ()) {
2523 var arg = metadata.args[arg_type];
2524 if (!arg.used) {
2525 // if metadata is used and argument is not, then it's a unexpected argument
2526 Report.warning (arg.source_reference, "argument never used");
2530 foreach (var child in metadata.children) {
2531 if (!child.used) {
2532 Report.warning (child.source_reference, "metadata never used");
2533 } else {
2534 report_unused_metadata (child);
2539 /* Post-parsing */
2541 void resolve_gir_symbols () {
2542 // we are remapping unresolved symbols, so create them from concrete symbols
2543 foreach (var map_from in concrete_symbols_map.get_keys ()) {
2544 unresolved_symbols_map[get_unresolved_symbol(map_from)] = concrete_symbols_map[map_from];
2547 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
2548 foreach (var map_from in unresolved_gir_symbols) {
2549 while (map_from != null) {
2550 var map_to = unresolved_symbols_map[map_from];
2551 if (map_to != null) {
2552 // remap the original symbol to match the target
2553 map_from.inner = null;
2554 map_from.name = map_to.name;
2555 if (map_to is UnresolvedSymbol) {
2556 var umap_to = (UnresolvedSymbol) map_to;
2557 while (umap_to.inner != null) {
2558 umap_to = umap_to.inner;
2559 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
2560 map_from = map_from.inner;
2562 } else {
2563 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
2564 map_to = map_to.parent_symbol;
2565 map_from.inner = new UnresolvedSymbol (null, map_to.name);
2566 map_from = map_from.inner;
2569 break;
2571 map_from = map_from.inner;
2576 Symbol? resolve_symbol (Scope parent_scope, UnresolvedSymbol unresolved_symbol) {
2577 // simple symbol resolver, enough for gir
2578 if (unresolved_symbol.inner == null) {
2579 var scope = parent_scope;
2580 while (scope != null) {
2581 var sym = scope.lookup (unresolved_symbol.name);
2582 if (sym != null) {
2583 return sym;
2585 scope = scope.parent_scope;
2587 } else {
2588 var inner = resolve_symbol (parent_scope, unresolved_symbol.inner);
2589 if (inner != null) {
2590 return inner.scope.lookup (unresolved_symbol.name);
2593 return null;
2596 void postprocess_interfaces () {
2597 foreach (var iface in interfaces) {
2598 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
2599 ensure we have at least one instantiable prerequisite */
2600 bool has_instantiable_prereq = false;
2601 foreach (DataType prereq in iface.get_prerequisites ()) {
2602 Symbol sym = null;
2603 if (prereq is UnresolvedType) {
2604 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
2605 sym = resolve_symbol (iface.parent_symbol.scope, unresolved_symbol);
2606 } else {
2607 sym = prereq.data_type;
2609 if (sym is Class) {
2610 has_instantiable_prereq = true;
2611 break;
2615 if (!has_instantiable_prereq) {
2616 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
2621 void postprocess_reparenting () {
2622 foreach (UnresolvedSymbol target_unresolved_symbol in symbol_reparent_map.get_keys ()) {
2623 var target_symbol = resolve_symbol (context.root.scope, target_unresolved_symbol);
2624 if (target_symbol == null) {
2625 // create namespaces backward
2626 var sym = target_unresolved_symbol;
2627 var ns = new Namespace (sym.name, sym.source_reference);
2628 var result = ns;
2629 sym = sym.inner;
2630 while (sym != null) {
2631 var res = resolve_symbol (context.root.scope, sym);
2632 if (res != null && !(res is Namespace)) {
2633 result = null;
2634 break;
2636 var parent = res as Namespace;
2637 if (res == null) {
2638 parent = new Namespace (sym.name, sym.source_reference);
2640 if (parent.scope.lookup (ns.name) == null) {
2641 parent.add_namespace (ns);
2643 ns = parent;
2644 sym = sym.inner;
2646 if (result != null && sym == null && context.root.scope.lookup (ns.name) == null) {
2647 // a new root namespace, helpful for a possible non-gobject gir?
2648 context.root.add_namespace (ns);
2650 target_symbol = result;
2652 if (target_symbol == null) {
2653 Report.error (null, "unable to reparent into `%s'".printf (target_unresolved_symbol.to_string ()));
2654 continue;
2656 var symbols = symbol_reparent_map[target_unresolved_symbol];
2657 foreach (var symbol in symbols) {
2658 add_symbol_to_container (target_symbol, symbol);
2663 void postprocess_gtype_callbacks () {
2664 foreach (CallbackScope callback_scope in gtype_callbacks.get_keys ()) {
2665 var gtype = resolve_symbol (callback_scope.parent_namespace.scope, callback_scope.gtype_struct_for) as ObjectTypeSymbol;
2666 if (gtype == null) {
2667 Report.error (null, "unknown symbol `%s'".printf (callback_scope.gtype_struct_for.to_string ()));
2668 continue;
2670 ArrayList<Delegate> callbacks = gtype_callbacks.get (callback_scope);
2671 foreach (Delegate d in callbacks) {
2672 var symbol = gtype.scope.lookup (d.name);
2673 if (symbol == null) {
2674 continue;
2675 } else if (symbol is Method) {
2676 var meth = (Method) symbol;
2677 if (gtype is Class) {
2678 meth.is_virtual = true;
2679 } else if (gtype is Interface) {
2680 meth.is_abstract = true;
2682 } else if (symbol is Signal) {
2683 var sig = (Signal) symbol;
2684 sig.is_virtual = true;
2685 assume_parameter_names (sig, d);
2686 } else if (symbol is Property) {
2687 var prop = (Property) symbol;
2688 prop.is_virtual = true;
2689 } else {
2690 Report.error (get_current_src (), "unknown member type `%s' in `%s'".printf (d.name, gtype.name));
2696 void postprocess_aliases () {
2697 /* this is unfortunate because <alias> tag has no type information, thus we have
2698 to guess it from the target */
2699 foreach (var alias in aliases) {
2700 DataType base_type = null;
2701 Symbol type_sym = null;
2702 if (alias.base_type is UnresolvedType) {
2703 base_type = alias.base_type;
2704 type_sym = resolve_symbol (alias.parent_namespace.scope, ((UnresolvedType) base_type).unresolved_symbol);
2705 } else if (!(alias.base_type is VoidType)) {
2706 base_type = alias.base_type;
2707 type_sym = base_type.data_type;
2710 if (base_type == null || type_sym == null || type_sym is Struct) {
2711 var st = new Struct (alias.name, alias.source_reference);
2712 st.access = SymbolAccessibility.PUBLIC;
2713 if (base_type != null) {
2714 // threat target="none" as a new struct
2715 st.base_type = base_type;
2717 st.external = true;
2718 alias.parent_namespace.add_struct (st);
2719 } else if (type_sym is Class) {
2720 var cl = new Class (alias.name, alias.source_reference);
2721 cl.access = SymbolAccessibility.PUBLIC;
2722 if (base_type != null) {
2723 cl.add_base_type (base_type);
2725 cl.external = true;
2726 alias.parent_namespace.add_class (cl);
2731 void find_static_method_parent (string cname, Symbol current, ref Symbol best, ref double match, double match_char) {
2732 var old_best = best;
2733 if (current.scope.get_symbol_table () != null) {
2734 foreach (var child in current.scope.get_symbol_table().get_values ()) {
2735 if (child is Struct || child is ObjectTypeSymbol || child is Namespace) {
2736 find_static_method_parent (cname, child, ref best, ref match, match_char);
2740 if (best != old_best) {
2741 // child is better
2742 return;
2745 var current_cprefix = current.get_lower_case_cprefix ();
2746 if (cname.has_prefix (current_cprefix)) {
2747 var current_match = match_char * current_cprefix.length;
2748 if (current_match > match) {
2749 match = current_match;
2750 best = current;
2755 void postprocess_namespace_methods () {
2756 /* transform static methods into instance methods if possible.
2757 In most of cases this is a .gir fault we are going to fix */
2758 foreach (var ns in namespace_methods.get_keys ()) {
2759 var ns_cprefix = ns.get_lower_case_cprefix ();
2760 var methods = namespace_methods[ns];
2761 foreach (var method in methods) {
2762 if (method.parent_node != null) {
2763 // fixed earlier by metadata
2764 continue;
2767 var cname = method.get_cname ();
2769 Parameter first_param = null;
2770 if (method.get_parameters ().size > 0) {
2771 first_param = method.get_parameters()[0];
2773 if (first_param != null && first_param.variable_type is UnresolvedType) {
2774 // check if it's a missed instance method (often happens for structs)
2775 var parent = resolve_symbol (ns.scope, ((UnresolvedType) first_param.variable_type).unresolved_symbol);
2776 if (parent != null && (parent is Struct || parent is ObjectTypeSymbol || parent is Namespace)
2777 && cname.has_prefix (parent.get_lower_case_cprefix ())) {
2778 // instance method
2779 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2780 if (parent.scope.lookup (new_name) == null) {
2781 method.name = new_name;
2782 method.get_parameters().remove_at (0);
2783 method.binding = MemberBinding.INSTANCE;
2784 add_symbol_to_container (parent, method);
2785 } else {
2786 ns.add_method (method);
2788 continue;
2792 double match = 0;
2793 Symbol parent = ns;
2794 find_static_method_parent (cname, ns, ref parent, ref match, 1.0/cname.length);
2795 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
2796 if (parent.scope.lookup (new_name) == null) {
2797 method.name = new_name;
2798 add_symbol_to_container (parent, method);
2799 } else {
2800 ns.add_method (method);
2806 /* Hash and equal functions */
2808 static uint unresolved_symbol_hash (void *ptr) {
2809 var sym = (UnresolvedSymbol) ptr;
2810 var builder = new StringBuilder ();
2811 while (sym != null) {
2812 builder.append (sym.name);
2813 sym = sym.inner;
2815 return builder.str.hash ();
2818 static bool unresolved_symbol_equal (void *ptr1, void *ptr2) {
2819 var sym1 = (UnresolvedSymbol) ptr1;
2820 var sym2 = (UnresolvedSymbol) ptr2;
2821 while (sym1 != sym2) {
2822 if (sym1 == null || sym2 == null) {
2823 return false;
2825 if (sym1.name != sym2.name) {
2826 return false;
2828 sym1 = sym1.inner;
2829 sym2 = sym2.inner;
2831 return true;
2834 static uint callback_scope_hash (void *ptr) {
2835 var cs = (CallbackScope) ptr;
2836 return unresolved_symbol_hash (cs.gtype_struct_for);
2839 static bool callback_scope_equal (void *ptr1, void *ptr2) {
2840 var cs1 = (CallbackScope) ptr1;
2841 var cs2 = (CallbackScope) ptr2;
2842 return cs1.parent_namespace == cs2.parent_namespace && unresolved_symbol_equal (cs1.gtype_struct_for, cs2.gtype_struct_for);