xcb: Drop vala-specific deprecations <= vala-0.22
[vala-gnome.git] / vala / valagirparser.vala
blob94a8721a8e22eb87573fe84bb798e2da6d00eaf0
1 /* valagirparser.vala
3 * Copyright (C) 2008-2012 Jürg Billeter
4 * Copyright (C) 2011-2014 Luca Bruno
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Luca Bruno <lucabru@src.gnome.org>
25 using GLib;
27 /**
28 * Code visitor parsing all GIR source files.
30 * Pipeline:
31 * 1) Parse metadata
32 * 2) Parse GIR with metadata, track unresolved GIR symbols, create Vala symbols
33 * 3) Reconciliate the tree by mapping tracked symbols
34 * 4) Process the tree
36 public class Vala.GirParser : CodeVisitor {
38 * Metadata parser
41 enum ArgumentType {
42 SKIP,
43 HIDDEN,
44 TYPE,
45 TYPE_ARGUMENTS,
46 CHEADER_FILENAME,
47 NAME,
48 OWNED,
49 UNOWNED,
50 PARENT,
51 NULLABLE,
52 DEPRECATED,
53 REPLACEMENT,
54 DEPRECATED_SINCE,
55 SINCE,
56 ARRAY,
57 ARRAY_LENGTH_IDX,
58 ARRAY_NULL_TERMINATED,
59 DEFAULT,
60 OUT,
61 REF,
62 VFUNC_NAME,
63 VIRTUAL,
64 ABSTRACT,
65 SCOPE,
66 STRUCT,
67 THROWS,
68 PRINTF_FORMAT,
69 ARRAY_LENGTH_FIELD,
70 SENTINEL,
71 CLOSURE,
72 DESTROY,
73 CPREFIX,
74 LOWER_CASE_CPREFIX,
75 ERRORDOMAIN,
76 DESTROYS_INSTANCE,
77 BASE_TYPE,
78 FINISH_NAME,
79 FINISH_INSTANCE,
80 SYMBOL_TYPE,
81 INSTANCE_IDX,
82 EXPERIMENTAL,
83 FLOATING,
84 TYPE_ID,
85 RETURN_VOID,
86 RETURNS_MODIFIED_POINTER,
87 DELEGATE_TARGET_CNAME,
88 FINISH_VFUNC_NAME,
89 NO_ACCESSOR_METHOD,
90 CNAME;
92 public static ArgumentType? from_string (string name) {
93 var enum_class = (EnumClass) typeof(ArgumentType).class_ref ();
94 var nick = name.replace ("_", "-");
95 unowned GLib.EnumValue? enum_value = enum_class.get_value_by_nick (nick);
96 if (enum_value != null) {
97 ArgumentType value = (ArgumentType) enum_value.value;
98 return value;
100 return null;
104 class Argument {
105 public Expression expression;
106 public SourceReference source_reference;
108 public bool used = false;
110 public Argument (Expression expression, SourceReference? source_reference = null) {
111 this.expression = expression;
112 this.source_reference = source_reference;
116 class MetadataSet : Metadata {
117 public MetadataSet (string? selector = null) {
118 base ("", selector);
121 public void add_sibling (Metadata metadata) {
122 foreach (var child in metadata.children) {
123 add_child (child);
125 // merge arguments and take precedence
126 foreach (var key in metadata.args.get_keys ()) {
127 args[key] = metadata.args[key];
132 class Metadata {
133 private static Metadata _empty = null;
134 public static Metadata empty {
135 get {
136 if (_empty == null) {
137 _empty = new Metadata ("");
139 return _empty;
143 public PatternSpec pattern_spec;
144 public string? selector;
145 public SourceReference source_reference;
147 public bool used = false;
148 public Vala.Map<ArgumentType,Argument> args = new HashMap<ArgumentType,Argument> ();
149 public ArrayList<Metadata> children = new ArrayList<Metadata> ();
151 public Metadata (string pattern, string? selector = null, SourceReference? source_reference = null) {
152 this.pattern_spec = new PatternSpec (pattern);
153 this.selector = selector;
154 this.source_reference = source_reference;
157 public void add_child (Metadata metadata) {
158 children.add (metadata);
161 public Metadata match_child (string name, string? selector = null) {
162 var result = Metadata.empty;
163 foreach (var metadata in children) {
164 if ((selector == null || metadata.selector == null || metadata.selector == selector) && metadata.pattern_spec.match_string (name)) {
165 metadata.used = true;
166 if (result == Metadata.empty) {
167 // first match
168 result = metadata;
169 } else {
170 var ms = result as MetadataSet;
171 if (ms == null) {
172 // second match
173 ms = new MetadataSet (selector);
174 ms.add_sibling (result);
176 ms.add_sibling (metadata);
177 result = ms;
181 return result;
184 public void add_argument (ArgumentType key, Argument value) {
185 args.set (key, value);
188 public bool has_argument (ArgumentType key) {
189 return args.contains (key);
192 public Expression? get_expression (ArgumentType arg) {
193 var val = args.get (arg);
194 if (val != null) {
195 val.used = true;
196 return val.expression;
198 return null;
201 public string? get_string (ArgumentType arg) {
202 var lit = get_expression (arg) as StringLiteral;
203 if (lit != null) {
204 return lit.eval ();
206 return null;
209 public int get_integer (ArgumentType arg) {
210 var unary = get_expression (arg) as UnaryExpression;
211 if (unary != null && unary.operator == UnaryOperator.MINUS) {
212 var lit = unary.inner as IntegerLiteral;
213 if (lit != null) {
214 return -int.parse (lit.value);
216 } else {
217 var lit = get_expression (arg) as IntegerLiteral;
218 if (lit != null) {
219 return int.parse (lit.value);
223 return 0;
226 public bool get_bool (ArgumentType arg, bool default_value = false) {
227 var lit = get_expression (arg) as BooleanLiteral;
228 if (lit != null) {
229 return lit.value;
231 return default_value;
234 public SourceReference? get_source_reference (ArgumentType arg) {
235 var val = args.get (arg);
236 if (val != null) {
237 return val.source_reference;
239 return null;
243 class MetadataParser {
245 * Grammar:
246 * metadata ::= [ rule [ '\n' relativerule ]* ]
247 * rule ::= pattern ' ' [ args ]
248 * relativerule ::= '.' rule
249 * pattern ::= glob [ '#' selector ] [ '.' pattern ]
251 private Metadata tree = new Metadata ("");
252 private Scanner scanner;
253 private SourceLocation begin;
254 private SourceLocation end;
255 private SourceLocation old_end;
256 private TokenType current;
257 private Metadata parent_metadata;
259 public MetadataParser () {
260 tree.used = true;
263 SourceReference get_current_src () {
264 return new SourceReference (scanner.source_file, begin, end);
267 SourceReference get_src (SourceLocation begin, SourceLocation? end = null) {
268 var e = this.end;
269 if (end != null) {
270 e = end;
272 return new SourceReference (scanner.source_file, begin, e);
275 public Metadata parse_metadata (SourceFile metadata_file) {
276 scanner = new Scanner (metadata_file);
277 next ();
278 while (current != TokenType.EOF) {
279 if (!parse_rule ()) {
280 return Metadata.empty;
283 return tree;
286 TokenType next () {
287 old_end = end;
288 current = scanner.read_token (out begin, out end);
289 return current;
292 bool has_space () {
293 return old_end.pos != begin.pos;
296 bool has_newline () {
297 return old_end.line != begin.line;
300 string get_string (SourceLocation? begin = null, SourceLocation? end = null) {
301 var b = this.begin;
302 var e = this.end;
303 if (begin != null) {
304 b = begin;
306 if (end != null) {
307 e = end;
309 return ((string) b.pos).substring (0, (int) (e.pos - b.pos));
312 string? parse_identifier (bool is_glob) {
313 var begin = this.begin;
315 if (current == TokenType.DOT || current == TokenType.HASH) {
316 if (is_glob) {
317 Report.error (get_src (begin), "expected glob-style pattern");
318 } else {
319 Report.error (get_src (begin), "expected identifier");
321 return null;
324 if (is_glob) {
325 while (current != TokenType.EOF && current != TokenType.DOT && current != TokenType.HASH) {
326 next ();
327 if (has_space ()) {
328 break;
331 } else {
332 next ();
335 return get_string (begin, old_end);
338 string? parse_selector () {
339 if (current != TokenType.HASH || has_space ()) {
340 return null;
342 next ();
344 return parse_identifier (false);
347 Metadata? parse_pattern () {
348 Metadata metadata;
349 bool is_relative = false;
350 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
351 // absolute pattern
352 parent_metadata = tree;
353 } else {
354 // relative pattern
355 if (current != TokenType.DOT) {
356 Report.error (get_current_src (), "expected pattern or `.', got %s".printf (current.to_string ()));
357 return null;
359 next ();
360 is_relative = true;
363 if (parent_metadata == null) {
364 Report.error (get_current_src (), "cannot determinate parent metadata");
365 return null;
368 SourceLocation begin = this.begin;
369 var pattern = parse_identifier (true);
370 if (pattern == null) {
371 return null;
373 metadata = new Metadata (pattern, parse_selector (), get_src (begin));
374 parent_metadata.add_child (metadata);
376 while (current != TokenType.EOF && !has_space ()) {
377 if (current != TokenType.DOT) {
378 Report.error (get_current_src (), "expected `.' got %s".printf (current.to_string ()));
379 break;
381 next ();
383 begin = this.begin;
384 pattern = parse_identifier (true);
385 if (pattern == null) {
386 return null;
388 var child = new Metadata (pattern, parse_selector (), get_src (begin, old_end));
389 metadata.add_child (child);
390 metadata = child;
392 if (!is_relative) {
393 parent_metadata = metadata;
396 return metadata;
399 Expression? parse_expression () {
400 var begin = this.begin;
401 var src = get_current_src ();
402 Expression expr = null;
403 switch (current) {
404 case TokenType.NULL:
405 expr = new NullLiteral (src);
406 break;
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.MINUS:
414 next ();
415 var inner = parse_expression ();
416 if (inner == null) {
417 Report.error (src, "expected expression after `-', got %s".printf (current.to_string ()));
418 } else {
419 expr = new UnaryExpression (UnaryOperator.MINUS, inner, get_src (begin));
421 return expr;
422 case TokenType.INTEGER_LITERAL:
423 expr = new IntegerLiteral (get_string (), src);
424 break;
425 case TokenType.REAL_LITERAL:
426 expr = new RealLiteral (get_string (), src);
427 break;
428 case TokenType.STRING_LITERAL:
429 expr = new StringLiteral (get_string (), src);
430 break;
431 case TokenType.IDENTIFIER:
432 expr = new MemberAccess (null, get_string (), src);
433 while (next () == TokenType.DOT) {
434 if (next () != TokenType.IDENTIFIER) {
435 Report.error (get_current_src (), "expected identifier got %s".printf (current.to_string ()));
436 break;
438 expr = new MemberAccess (expr, get_string (), get_current_src ());
440 return expr;
441 case TokenType.OPEN_PARENS:
442 // empty tuple => no expression
443 if (next () != TokenType.CLOSE_PARENS) {
444 Report.error (get_current_src (), "expected `)', got %s".printf (current.to_string ()));
445 break;
447 expr = new Tuple (src);
448 break;
449 default:
450 Report.error (src, "expected literal or symbol got %s".printf (current.to_string ()));
451 break;
453 next ();
454 return expr;
457 bool parse_args (Metadata metadata) {
458 while (current != TokenType.EOF && has_space () && !has_newline ()) {
459 SourceLocation begin = this.begin;
460 var id = parse_identifier (false);
461 if (id == null) {
462 return false;
464 var arg_type = ArgumentType.from_string (id);
465 if (arg_type == null) {
466 Report.warning (get_src (begin, old_end), "unknown argument `%s'".printf (id));
467 continue;
470 if (current != TokenType.ASSIGN) {
471 // threat as `true'
472 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, get_src (begin)), get_src (begin)));
473 continue;
475 next ();
477 Expression expr = parse_expression ();
478 if (expr == null) {
479 return false;
481 metadata.add_argument (arg_type, new Argument (expr, get_src (begin)));
484 return true;
487 bool parse_rule () {
488 var old_end = end;
489 var metadata = parse_pattern ();
490 if (metadata == null) {
491 return false;
494 if (current == TokenType.EOF || old_end.line != end.line) {
495 // eof or new rule
496 return true;
498 return parse_args (metadata);
503 * GIR parser
506 class Node {
507 public static ArrayList<Node> new_namespaces = new ArrayList<Node> ();
509 public weak Node parent;
510 public string element_type;
511 public string name;
512 public Map<string,string> girdata = null;
513 public Metadata metadata = Metadata.empty;
514 public SourceReference source_reference = null;
515 public ArrayList<Node> members = new ArrayList<Node> (); // guarantees fields order
516 public HashMap<string, ArrayList<Node>> scope = new HashMap<string, ArrayList<Node>> (str_hash, str_equal);
518 public GirComment comment;
519 public Symbol symbol;
520 public bool new_symbol;
521 public bool merged;
522 public bool processed;
524 // function-specific
525 public int return_array_length_idx = -1;
526 public List<ParameterInfo> parameters;
527 public ArrayList<int> array_length_parameters;
528 public ArrayList<int> closure_parameters;
529 public ArrayList<int> destroy_parameters;
530 // record-specific
531 public UnresolvedSymbol gtype_struct_for;
532 // alias-specific
533 public DataType base_type;
534 // struct-specific
535 public int array_length_idx = -1;
537 public bool deprecated = false;
538 public uint64 deprecated_version = 0;
539 public string? deprecated_since = null;
540 public string? deprecated_replacement = null;
542 public Node (string? name) {
543 this.name = name;
546 public void add_member (Node node) {
547 var nodes = scope[node.name];
548 if (nodes == null) {
549 nodes = new ArrayList<Node> ();
550 scope[node.name] = nodes;
552 nodes.add (node);
553 members.add (node);
554 node.parent = this;
557 public void remove_member (Node node) {
558 var nodes = scope[node.name];
559 nodes.remove (node);
560 if (nodes.size == 0) {
561 scope.remove (node.name);
563 members.remove (node);
564 node.parent = null;
567 public Node? lookup (string name, bool create_namespace = false, SourceReference? source_reference = null) {
568 var nodes = scope[name];
569 Node node = null;
570 if (nodes != null) {
571 node = nodes[0];
573 if (node == null) {
574 Symbol sym = null;
575 if (symbol != null) {
576 sym = symbol.scope.lookup (name);
578 if (sym != null || create_namespace) {
579 node = new Node (name);
580 node.symbol = sym;
581 node.new_symbol = node.symbol == null;
582 node.source_reference = source_reference;
583 add_member (node);
585 if (sym == null) {
586 new_namespaces.add (node);
590 return node;
593 public ArrayList<Node>? lookup_all (string name) {
594 return scope[name];
597 public UnresolvedSymbol get_unresolved_symbol () {
598 if (parent.name == null) {
599 return new UnresolvedSymbol (null, name);
600 } else {
601 return new UnresolvedSymbol (parent.get_unresolved_symbol (), name);
605 public string get_full_name () {
606 if (parent == null) {
607 return name;
610 if (name == null) {
611 return parent.get_full_name ();
614 if (parent.get_full_name () == null) {
615 return name;
618 return "%s.%s".printf (parent.get_full_name (), name);
621 public string get_default_gir_name () {
622 GLib.StringBuilder default_name = new GLib.StringBuilder ();
624 for (unowned Node? node = this ; node != null ; node = node.parent) {
625 if (node.symbol is Namespace) {
626 if (node.symbol.get_attribute_string ("CCode", "gir_namespace") != null) {
627 break;
631 default_name.prepend (node.name);
634 return default_name.str;
637 public string get_gir_name () {
638 var gir_name = girdata["name"];
639 if (gir_name == null) {
640 gir_name = girdata["glib:name"];
642 return gir_name;
645 public string get_lower_case_cprefix () {
646 if (name == null) {
647 return "";
650 var prefix = symbol.get_attribute_string ("CCode", "lower_case_cprefix");
651 if (prefix == null && (symbol is ObjectTypeSymbol || symbol is Struct)) {
652 if (metadata.has_argument (ArgumentType.LOWER_CASE_CPREFIX)) {
653 prefix = metadata.get_string (ArgumentType.LOWER_CASE_CPREFIX);
654 } else if (metadata.has_argument (ArgumentType.CPREFIX)) {
655 prefix = metadata.get_string (ArgumentType.CPREFIX);
656 } else {
657 prefix = symbol.get_attribute_string ("CCode", "cprefix");
661 if (prefix == null && girdata != null && (girdata.contains ("c:symbol-prefix") || girdata.contains("c:symbol-prefixes"))) {
662 /* Use the prefix in the gir. We look up prefixes up to the root.
663 If some node does not have girdata, we ignore it as i might be
664 a namespace created due to reparenting. */
665 unowned Node cur = this;
666 do {
667 if (cur.girdata != null) {
668 var p = cur.girdata["c:symbol-prefix"];
669 if (p == null) {
670 p = cur.girdata["c:symbol-prefixes"];
671 if (p != null) {
672 var idx = p.index_of (",");
673 if (idx >= 0) {
674 p = p.substring (0, idx);
679 if (p != null) {
680 prefix = "%s_%s".printf (p, prefix ?? "");
684 cur = cur.parent;
685 } while (cur != null);
688 if (prefix == null) {
689 prefix = get_default_lower_case_cprefix ();
691 return prefix;
694 public string get_default_lower_case_cprefix () {
695 return "%s%s_".printf (parent.get_lower_case_cprefix (), get_lower_case_csuffix ());
698 public string get_lower_case_csuffix () {
699 var suffix = symbol.get_attribute_string ("CCode", "lower_case_csuffix");
701 // we can't rely on gir suffix if metadata changed the name
702 if (suffix == null && girdata != null && girdata["c:symbol-prefix"] != null && !metadata.has_argument (ArgumentType.NAME)) {
703 suffix = girdata["c:symbol-prefix"];
705 if (suffix == null) {
706 suffix = get_default_lower_case_csuffix ();
708 return suffix;
711 public string get_default_lower_case_csuffix () {
712 return Symbol.camel_case_to_lower_case (name);
715 public string get_cprefix () {
716 if (name == null) {
717 return "";
719 string prefix;
720 if (metadata.has_argument (ArgumentType.CPREFIX)) {
721 prefix = metadata.get_string (ArgumentType.CPREFIX);
722 } else {
723 prefix = symbol.get_attribute_string ("CCode", "cprefix");
725 if (prefix == null && girdata != null && girdata["c:identifier-prefixes"] != null) {
726 prefix = girdata["c:identifier-prefixes"];
727 int idx = prefix.index_of (",");
728 if (idx != -1) {
729 prefix = prefix.substring (0, idx);
732 if (prefix == null) {
733 if (symbol is Enum || symbol is ErrorDomain) {
734 prefix = "%s%s".printf (parent.get_lower_case_cprefix ().ascii_up (), name);
735 } else {
736 prefix = get_cname ();
739 return prefix;
742 public string get_cname () {
743 if (name == null) {
744 return "";
746 string cname;
747 if (metadata.has_argument (ArgumentType.CNAME)) {
748 cname = metadata.get_string (ArgumentType.CNAME);
749 } else {
750 cname = symbol.get_attribute_string ("CCode", "cname");
752 if (girdata != null) {
753 if (cname == null) {
754 cname = girdata["c:identifier"];
756 if (cname == null) {
757 cname = girdata["c:type"];
760 if (cname == null) {
761 cname = get_default_cname ();
763 return cname;
766 public string get_default_cname () {
767 if (name == null) {
768 return "";
770 if (symbol is Field) {
771 if (((Field) symbol).binding == MemberBinding.STATIC) {
772 return parent.get_lower_case_cprefix () + name;
773 } else {
774 return name;
776 } else if (symbol is Method) {
777 return "%s%s".printf (parent.get_lower_case_cprefix (), name);
778 } else {
779 return "%s%s".printf (parent.get_cprefix (), name);
783 public string get_finish_cname () {
784 var finish_cname = symbol.get_attribute_string ("CCode", "finish_name");
785 if (finish_cname == null) {
786 finish_cname = get_cname ();
787 if (finish_cname.has_suffix ("_async")) {
788 finish_cname = finish_cname.substring (0, finish_cname.length - "_async".length);
790 finish_cname = "%s_finish".printf (finish_cname);
792 return finish_cname;
795 public string get_cheader_filename () {
796 if (metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
797 return metadata.get_string (ArgumentType.CHEADER_FILENAME);
799 var cheader_filename = symbol.get_attribute_string ("CCode", "cheader_filename");
800 if (cheader_filename != null) {
801 return cheader_filename;
803 if (parent.name != null) {
804 return parent.get_cheader_filename ();
805 } else if (symbol.source_reference != null) {
806 return symbol.source_reference.file.get_cinclude_filename ();
808 return "";
811 private static uint64 parse_version_string (string version) {
812 int64 res = 0;
813 int shift = 16;
814 string[] tokens = version.split (".", 3);
816 foreach (unowned string token in tokens) {
817 int64 t;
819 if (!int64.try_parse (token, out t))
820 return 0;
821 if (t > 0xffff)
822 return 0;
824 res |= (t << shift);
825 shift -= 8;
828 return res;
831 public void process (GirParser parser) {
832 if (processed) {
833 return;
836 if (symbol is Namespace && parent == parser.root) {
837 // first process aliases since they have no assigned symbol
838 foreach (var node in members) {
839 if (node.element_type == "alias") {
840 parser.process_alias (node);
844 // auto reparent namespace methods, allowing node removals
845 for (int i=0; i < members.size; i++) {
846 var node = members[i];
847 if (node.symbol is Method && node.new_symbol) {
848 parser.process_namespace_method (this, node);
849 if (i < members.size && members[i] != node) {
850 // node removed in the middle
851 i--;
857 if (symbol is Class && girdata != null) {
858 var class_struct = girdata["glib:type-struct"];
859 if (class_struct != null) {
860 var klass = parser.resolve_node (parent, parser.parse_symbol_from_string (class_struct, source_reference));
861 if (klass != null) {
862 var i = 0;
863 while ( i < klass.members.size ) {
864 var node = klass.members[i];
865 if (node.symbol is Method) {
866 klass.remove_member (node);
867 this.add_member (node);
869 Method m = (Method) node.symbol;
870 m.binding = MemberBinding.CLASS;
871 } else {
872 i++;
879 // process children
880 foreach (var node in members) {
881 node.process (parser);
884 if (girdata != null) {
885 // GIR node processing
886 if (symbol is Method) {
887 var m = (Method) symbol;
888 parser.process_callable (this);
890 var colliding = parent.lookup_all (name);
891 foreach (var node in colliding) {
892 var sym = node.symbol;
893 if (sym is Field && !(m.return_type is VoidType) && m.get_parameters().size == 0) {
894 // assume method is getter
895 merged = true;
896 } else if (sym is Signal) {
897 node.process (parser);
898 var sig = (Signal) sym;
899 if (m.is_virtual || m.is_abstract) {
900 sig.is_virtual = true;
901 } else {
902 sig.set_attribute ("HasEmitter", true);
904 parser.assume_parameter_names (sig, m, false);
905 if (m.get_parameters().size != sig.get_parameters().size) {
906 Report.warning (symbol.source_reference, "Signal `%s' conflicts with method of the same name".printf (get_full_name ()));
908 merged = true;
909 } else if (sym is Method && !(sym is CreationMethod) && node != this) {
910 if (m.is_virtual || m.is_abstract) {
911 bool different_invoker = false;
912 var attr = m.get_attribute ("NoWrapper");
913 if (attr != null) {
914 /* no invoker but this method has the same name,
915 most probably the invoker has a different name
916 and g-ir-scanner missed it */
917 var invoker = parser.find_invoker (this);
918 if (invoker != null) {
919 m.set_attribute_string ("CCode", "vfunc_name", m.name);
920 m.name = invoker.symbol.name;
921 m.set_attribute ("NoWrapper", false);
922 invoker.merged = true;
923 different_invoker = true;
926 if (!different_invoker) {
927 if (attr != null) {
928 Report.warning (symbol.source_reference, "Virtual method `%s' conflicts with method of the same name".printf (get_full_name ()));
930 node.merged = true;
935 if (!(m is CreationMethod)) {
936 if (metadata.has_argument (ArgumentType.DESTROYS_INSTANCE)) {
937 m.set_attribute ("DestroysInstance", metadata.get_bool (ArgumentType.DESTROYS_INSTANCE));
939 if (metadata.has_argument (ArgumentType.RETURNS_MODIFIED_POINTER)) {
940 m.set_attribute ("ReturnsModifiedPointer", metadata.get_bool (ArgumentType.RETURNS_MODIFIED_POINTER));
942 // merge custom vfunc
943 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
944 var vfunc = parent.lookup (metadata.get_string (ArgumentType.VFUNC_NAME));
945 if (vfunc != null && vfunc != this) {
946 vfunc.processed = true;
947 vfunc.merged = true;
951 if (m.coroutine) {
952 parser.process_async_method (this);
954 } else if (symbol is Property) {
955 var colliding = parent.lookup_all (name);
956 foreach (var node in colliding) {
957 if (node.symbol is Signal) {
958 // properties take precedence
959 node.processed = true;
960 node.merged = true;
961 } else if (node.symbol is Method) {
962 // getter in C, but not in Vala
963 node.merged = true;
967 var prop = (Property) symbol;
969 // add accessors, can't do this before gir symbol resolution
970 var readable = girdata["readable"];
971 var writable = girdata["writable"];
972 var construct_ = girdata["construct"];
973 var construct_only = girdata["construct-only"];
974 if (readable != "0") {
975 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
977 if (writable == "1" || construct_only == "1") {
978 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
981 // find virtual/abstract accessors to handle abstract properties properly
983 Node getter = null;
984 var getters = parent.lookup_all ("get_%s".printf (name));
985 if (getters != null) {
986 foreach (var g in getters) {
987 if ((getter == null || !g.merged) && g.get_cname () == "%sget_%s".printf (parent.get_lower_case_cprefix (), name)) {
988 getter = g;
993 Node setter = null;
994 var setters = parent.lookup_all ("set_%s".printf (name));
995 if (setters != null) {
996 foreach (var s in setters) {
997 if ((setter == null || !s.merged) && s.get_cname () == "%sset_%s".printf (parent.get_lower_case_cprefix (), name)) {
998 setter = s;
1003 prop.set_attribute ("NoAccessorMethod", (readable == "0" && construct_only == "1"));
1004 if (prop.get_accessor != null) {
1005 var m = getter != null ? getter.symbol as Method : null;
1006 // ensure getter vfunc if the property is abstract
1007 if (m != null) {
1008 getter.process (parser);
1009 if (m.return_type is VoidType || m.get_parameters().size != 0 || m.get_error_types ().size > 0) {
1010 prop.set_attribute ("NoAccessorMethod", true);
1011 } else {
1012 if (getter.name == name) {
1013 foreach (var node in colliding) {
1014 if (node.symbol is Method) {
1015 node.merged = true;
1020 prop.get_accessor.value_type.value_owned = m.return_type.value_owned;
1022 if (!m.is_abstract && !m.is_virtual && prop.is_abstract) {
1023 prop.set_attribute ("ConcreteAccessor", true);
1026 } else {
1027 prop.set_attribute ("NoAccessorMethod", true);
1031 if (prop.get_attribute ("NoAccessorMethod") == null && prop.set_accessor != null && prop.set_accessor.writable) {
1032 var m = setter != null ? setter.symbol as Method : null;
1033 // ensure setter vfunc if the property is abstract
1034 if (m != null) {
1035 setter.process (parser);
1036 if (!(m.return_type is VoidType || m.return_type is BooleanType) || m.get_parameters ().size != 1 || m.get_error_types ().size > 0) {
1037 prop.set_attribute ("NoAccessorMethod", true);
1038 prop.set_attribute ("ConcreteAccessor", false);
1039 } else {
1040 prop.set_accessor.value_type.value_owned = m.get_parameters()[0].variable_type.value_owned;
1041 if (prop.get_attribute ("ConcreteAccessor") != null && !m.is_abstract && !m.is_virtual && prop.is_abstract) {
1042 prop.set_attribute ("ConcreteAccessor", true);
1043 prop.set_attribute ("NoAccessorMethod", false);
1046 } else {
1047 prop.set_attribute ("NoAccessorMethod", true);
1048 prop.set_attribute ("ConcreteAccessor", false);
1052 if (prop.get_attribute ("NoAccessorMethod") != null) {
1053 if (!prop.overrides && parent.symbol is Class) {
1054 // bug 742012
1055 // find base interface property with ConcreteAccessor and this overriding property with NoAccessorMethod
1056 var base_prop_node = parser.base_interface_property (this);
1057 if (base_prop_node != null) {
1058 base_prop_node.process (parser);
1060 var base_property = (Property) base_prop_node.symbol;
1061 if (base_property.get_attribute ("ConcreteAccessor") != null) {
1062 prop.set_attribute ("NoAccessorMethod", false);
1063 if (prop.get_accessor != null) {
1064 prop.get_accessor.value_type.value_owned = base_property.get_accessor.value_type.value_owned;
1066 if (prop.set_accessor != null) {
1067 prop.set_accessor.value_type.value_owned = base_property.set_accessor.value_type.value_owned;
1075 if (metadata.has_argument (ArgumentType.NO_ACCESSOR_METHOD)) {
1076 prop.set_attribute ("NoAccessorMethod", metadata.get_bool (ArgumentType.NO_ACCESSOR_METHOD));
1079 if (prop.get_attribute ("NoAccessorMethod") != null) {
1080 // gobject defaults
1081 if (prop.get_accessor != null) {
1082 prop.get_accessor.value_type.value_owned = true;
1084 if (prop.set_accessor != null) {
1085 prop.set_accessor.value_type.value_owned = false;
1088 } else if (symbol is Field) {
1089 var field = (Field) symbol;
1090 var colliding = parent.lookup_all (name);
1091 if (colliding.size > 1) {
1092 // whatelse has precedence over the field
1093 merged = true;
1096 if (metadata.has_argument (ArgumentType.DELEGATE_TARGET_CNAME)) {
1097 field.set_attribute_string ("CCode", "delegate_target_cname", metadata.get_string (ArgumentType.DELEGATE_TARGET_CNAME));
1100 if (field.variable_type is DelegateType && parent.gtype_struct_for != null) {
1101 // virtual method field
1102 var d = ((DelegateType) field.variable_type).delegate_symbol;
1103 parser.process_virtual_method_field (this, d, parent.gtype_struct_for);
1104 merged = true;
1105 } else if (field.variable_type is ArrayType) {
1106 Node array_length;
1107 if (metadata.has_argument (ArgumentType.ARRAY_LENGTH_FIELD)) {
1108 array_length = parent.lookup (metadata.get_string (ArgumentType.ARRAY_LENGTH_FIELD));
1109 } else if (array_length_idx > -1 && parent.members.size > array_length_idx) {
1110 array_length = parent.members[array_length_idx];
1111 } else {
1112 array_length = parent.lookup ("n_%s".printf (field.name));
1113 if (array_length == null) {
1114 array_length = parent.lookup ("num_%s".printf (field.name));
1115 if (array_length == null) {
1116 array_length = parent.lookup ("%s_length".printf (field.name));
1120 if (array_length != null && array_length.symbol is Field) {
1121 var length_field = (Field) array_length.symbol;
1122 // array has length
1123 field.set_attribute_string ("CCode", "array_length_cname", length_field.name);
1124 var length_type = length_field.variable_type.to_qualified_string ();
1125 if (length_type != "int") {
1126 var st = parser.root.lookup (length_type);
1127 if (st != null) {
1128 field.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1131 field.remove_attribute_argument ("CCode", "array_length");
1132 field.remove_attribute_argument ("CCode", "array_null_terminated");
1135 } else if (symbol is Signal || symbol is Delegate) {
1136 parser.process_callable (this);
1137 } else if (symbol is Interface) {
1138 parser.process_interface (this);
1139 } else if (symbol is Struct) {
1140 if (parent.symbol is ObjectTypeSymbol || parent.symbol is Struct) {
1141 // nested struct
1142 foreach (var fn in members) {
1143 var f = fn.symbol as Field;
1144 if (f != null) {
1145 if (f.binding == MemberBinding.INSTANCE) {
1146 f.set_attribute_string ("CCode", "cname", "%s.%s".printf (name, fn.get_cname ()));
1148 f.name = "%s_%s".printf (symbol.name, f.name);
1149 fn.name = f.name;
1150 parent.add_member (fn);
1153 merged = true;
1154 } else {
1155 // record for a gtype
1156 var gtype_struct_for = girdata["glib:is-gtype-struct-for"];
1157 if (gtype_struct_for != null) {
1158 var iface = parser.resolve_node (parent, parser.parse_symbol_from_string (gtype_struct_for, source_reference));
1159 if (iface != null && iface.symbol is Interface && "%sIface".printf (iface.get_cname ()) != get_cname ()) {
1160 // set the interface struct name
1161 iface.symbol.set_attribute_string ("CCode", "type_cname", get_cname ());
1163 merged = true;
1168 // deprecated
1169 if (metadata.has_argument (ArgumentType.REPLACEMENT)) {
1170 deprecated = true;
1171 deprecated_replacement = metadata.get_string (ArgumentType.REPLACEMENT);
1173 if (metadata.has_argument (ArgumentType.DEPRECATED_SINCE)) {
1174 deprecated = true;
1175 deprecated_since = metadata.get_string (ArgumentType.DEPRECATED_SINCE);
1176 } else if (girdata["deprecated-version"] != null) {
1177 deprecated = true;
1178 deprecated_since = girdata.get ("deprecated-version");
1180 if (metadata.has_argument (ArgumentType.DEPRECATED)) {
1181 deprecated = metadata.get_bool (ArgumentType.DEPRECATED, true);
1182 if (!deprecated) {
1183 deprecated_since = null;
1184 deprecated_replacement = null;
1186 } else if (girdata["deprecated"] != null) {
1187 deprecated = true;
1189 if (deprecated_since != null) {
1190 deprecated_version = parse_version_string (deprecated_since);
1193 // experimental
1194 if (metadata.has_argument (ArgumentType.EXPERIMENTAL)) {
1195 symbol.set_attribute_bool ("Version", "experimental", metadata.get_bool (ArgumentType.EXPERIMENTAL));
1198 // since
1199 if (metadata.has_argument (ArgumentType.SINCE)) {
1200 symbol.version.since = metadata.get_string (ArgumentType.SINCE);
1201 } else if (symbol is Namespace == false && girdata["version"] != null) {
1202 symbol.version.since = girdata.get ("version");
1205 if (parent.symbol is Namespace) {
1206 // always write cheader filename for namespace children
1207 symbol.set_attribute_string ("CCode", "cheader_filename", get_cheader_filename ());
1208 } else if (metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1209 symbol.set_attribute_string ("CCode", "cheader_filename", metadata.get_string (ArgumentType.CHEADER_FILENAME));
1211 if (get_cname () != get_default_cname ()) {
1212 symbol.set_attribute_string ("CCode", "cname", get_cname ());
1215 // lower_case_cprefix
1216 if (get_lower_case_cprefix () != get_default_lower_case_cprefix ()) {
1217 symbol.set_attribute_string ("CCode", "lower_case_cprefix", get_lower_case_cprefix ());
1219 // lower_case_csuffix
1220 if (get_lower_case_csuffix () != get_default_lower_case_csuffix ()) {
1221 symbol.set_attribute_string ("CCode", "lower_case_csuffix", get_lower_case_csuffix ());
1224 // set gir name if the symbol has been renamed
1225 string gir_name = get_gir_name ();
1226 string default_gir_name = get_default_gir_name ();
1227 if (is_container (symbol) && !(symbol is Namespace) && (name != gir_name || gir_name != default_gir_name)) {
1228 symbol.set_attribute_string ("GIR", "name", gir_name);
1232 if (!(new_symbol && merged) && is_container (symbol)) {
1233 foreach (var node in members) {
1234 if (this.deprecated_version > 0 && node.deprecated_version > 0) {
1235 if (this.deprecated_version <= node.deprecated_version) {
1236 node.deprecated = false;
1237 node.deprecated_since = null;
1238 node.deprecated_replacement = null;
1241 if (node.deprecated) {
1242 node.symbol.version.deprecated = true;
1244 if (node.deprecated_since != null) {
1245 node.symbol.version.deprecated_since = node.deprecated_since;
1247 if (node.deprecated_replacement != null) {
1248 node.symbol.version.replacement = node.deprecated_replacement;
1251 if (node.new_symbol && !node.merged && !metadata.get_bool (ArgumentType.HIDDEN)) {
1252 add_symbol_to_container (symbol, node.symbol);
1256 var cl = symbol as Class;
1257 if (cl != null && !cl.is_compact && cl.default_construction_method == null) {
1258 // always provide constructor in generated bindings
1259 // to indicate that implicit Object () chainup is allowed
1260 var cm = new CreationMethod (null, null, cl.source_reference);
1261 cm.has_construct_function = false;
1262 cm.access = SymbolAccessibility.PROTECTED;
1263 cl.add_method (cm);
1267 processed = true;
1270 public string to_string () {
1271 if (parent.name == null) {
1272 return name;
1273 } else {
1274 return "%s.%s".printf (parent.to_string (), name);
1279 static GLib.Regex type_from_string_regex;
1281 MarkupReader reader;
1283 CodeContext context;
1284 Namespace glib_ns;
1286 SourceFile current_source_file;
1287 Node root;
1288 ArrayList<Metadata> metadata_roots = new ArrayList<Metadata> ();
1290 SourceLocation begin;
1291 SourceLocation end;
1292 MarkupTokenType current_token;
1294 string[] cheader_filenames;
1296 ArrayList<Metadata> metadata_stack;
1297 Metadata metadata;
1298 ArrayList<Node> tree_stack;
1299 Node current;
1300 Node old_current;
1302 Set<string> provided_namespaces = new HashSet<string> (str_hash, str_equal);
1303 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
1304 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
1305 ArrayList<DataType> unresolved_type_arguments = new ArrayList<DataType> ();
1308 * Parses all .gir source files in the specified code
1309 * context and builds a code tree.
1311 * @param context a code context
1313 public void parse (CodeContext context) {
1314 this.context = context;
1315 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
1317 root = new Node (null);
1318 root.symbol = context.root;
1319 tree_stack = new ArrayList<Node> ();
1320 current = root;
1322 map_vala_to_gir ();
1324 context.accept (this);
1326 resolve_gir_symbols ();
1327 create_new_namespaces ();
1328 resolve_type_arguments ();
1330 root.process (this);
1332 foreach (var metadata in metadata_roots) {
1333 report_unused_metadata (metadata);
1336 this.context = null;
1339 void map_vala_to_gir () {
1340 foreach (var source_file in context.get_source_files ()) {
1341 string gir_namespace = source_file.gir_namespace;
1342 string gir_version = source_file.gir_version;
1343 Namespace ns = null;
1344 if (gir_namespace == null) {
1345 foreach (var node in source_file.get_nodes ()) {
1346 if (node is Namespace) {
1347 ns = (Namespace) node;
1348 gir_namespace = ns.get_attribute_string ("CCode", "gir_namespace");
1349 if (gir_namespace != null) {
1350 gir_version = ns.get_attribute_string ("CCode", "gir_version");
1351 break;
1356 if (gir_namespace == null) {
1357 continue;
1360 provided_namespaces.add ("%s-%s".printf (gir_namespace, gir_version));
1362 var gir_symbol = new UnresolvedSymbol (null, gir_namespace);
1363 if (gir_namespace != ns.name) {
1364 set_symbol_mapping (gir_symbol, ns);
1367 foreach (var node in source_file.get_nodes ()) {
1368 if (node.has_attribute_argument ("GIR", "name")) {
1369 var map_from = new UnresolvedSymbol (gir_symbol, node.get_attribute_string ("GIR", "name"));
1370 set_symbol_mapping (map_from, (Symbol) node);
1376 public override void visit_source_file (SourceFile source_file) {
1377 if (source_file.filename.has_suffix (".gir")) {
1378 parse_file (source_file);
1382 public void parse_file (SourceFile source_file) {
1383 var has_global_context = (context != null);
1384 if (!has_global_context) {
1385 context = source_file.context;
1388 metadata_stack = new ArrayList<Metadata> ();
1389 metadata = Metadata.empty;
1390 cheader_filenames = null;
1392 this.current_source_file = source_file;
1393 reader = new MarkupReader (source_file.filename);
1395 // xml prolog
1396 next ();
1397 next ();
1399 next ();
1400 parse_repository ();
1402 reader = null;
1403 this.current_source_file = null;
1404 if (!has_global_context) {
1405 context = null;
1409 void next () {
1410 current_token = reader.read_token (out begin, out end);
1413 void start_element (string name) {
1414 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
1415 // error
1416 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
1420 void end_element (string name) {
1421 while (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
1422 Report.warning (get_current_src (), "expected end element of `%s'".printf (name));
1423 skip_element ();
1425 next ();
1428 SourceReference get_current_src () {
1429 return new SourceReference (this.current_source_file, begin, end);
1432 const string GIR_VERSION = "1.2";
1434 static void add_symbol_to_container (Symbol container, Symbol sym) {
1435 if (container is Class) {
1436 unowned Class cl = (Class) container;
1438 if (sym is Class) {
1439 cl.add_class ((Class) sym);
1440 } else if (sym is Constant) {
1441 cl.add_constant ((Constant) sym);
1442 } else if (sym is Enum) {
1443 cl.add_enum ((Enum) sym);
1444 } else if (sym is Field) {
1445 cl.add_field ((Field) sym);
1446 } else if (sym is Method) {
1447 cl.add_method ((Method) sym);
1448 } else if (sym is Property) {
1449 cl.add_property ((Property) sym);
1450 } else if (sym is Signal) {
1451 cl.add_signal ((Signal) sym);
1452 } else if (sym is Struct) {
1453 cl.add_struct ((Struct) sym);
1455 } else if (container is Enum) {
1456 unowned Enum en = (Enum) container;
1458 if (sym is EnumValue) {
1459 en.add_value ((EnumValue) sym);
1460 } else if (sym is Constant) {
1461 en.add_constant ((Constant) sym);
1462 } else if (sym is Method) {
1463 en.add_method ((Method) sym);
1465 } else if (container is Interface) {
1466 unowned Interface iface = (Interface) container;
1468 if (sym is Class) {
1469 iface.add_class ((Class) sym);
1470 } else if (sym is Constant) {
1471 iface.add_constant ((Constant) sym);
1472 } else if (sym is Enum) {
1473 iface.add_enum ((Enum) sym);
1474 } else if (sym is Field) {
1475 iface.add_field ((Field) sym);
1476 } else if (sym is Method) {
1477 iface.add_method ((Method) sym);
1478 } else if (sym is Property) {
1479 iface.add_property ((Property) sym);
1480 } else if (sym is Signal) {
1481 iface.add_signal ((Signal) sym);
1482 } else if (sym is Struct) {
1483 iface.add_struct ((Struct) sym);
1485 } else if (container is Namespace) {
1486 unowned Namespace ns = (Namespace) container;
1488 if (sym is Namespace) {
1489 ns.add_namespace ((Namespace) sym);
1490 } else if (sym is Class) {
1491 ns.add_class ((Class) sym);
1492 } else if (sym is Constant) {
1493 ns.add_constant ((Constant) sym);
1494 } else if (sym is Delegate) {
1495 ns.add_delegate ((Delegate) sym);
1496 } else if (sym is Enum) {
1497 ns.add_enum ((Enum) sym);
1498 } else if (sym is ErrorDomain) {
1499 ns.add_error_domain ((ErrorDomain) sym);
1500 } else if (sym is Field) {
1501 ns.add_field ((Field) sym);
1502 } else if (sym is Interface) {
1503 ns.add_interface ((Interface) sym);
1504 } else if (sym is Method) {
1505 ns.add_method ((Method) sym);
1506 } else if (sym is Struct) {
1507 ns.add_struct ((Struct) sym);
1509 } else if (container is Struct) {
1510 unowned Struct st = (Struct) container;
1512 if (sym is Constant) {
1513 st.add_constant ((Constant) sym);
1514 } else if (sym is Field) {
1515 st.add_field ((Field) sym);
1516 } else if (sym is Method) {
1517 st.add_method ((Method) sym);
1518 } else if (sym is Property) {
1519 st.add_property ((Property) sym);
1521 } else if (container is ErrorDomain) {
1522 unowned ErrorDomain ed = (ErrorDomain) container;
1524 if (sym is ErrorCode) {
1525 ed.add_code ((ErrorCode) sym);
1526 } else if (sym is Method) {
1527 ed.add_method ((Method) sym);
1529 } else {
1530 Report.error (sym.source_reference, "impossible to add `%s' to container `%s'".printf (sym.name, container.name));
1534 static bool is_container (Symbol sym) {
1535 return sym is ObjectTypeSymbol || sym is Struct || sym is Namespace || sym is ErrorDomain || sym is Enum;
1538 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
1539 UnresolvedSymbol? sym = null;
1540 foreach (unowned string s in symbol_string.split (".")) {
1541 sym = new UnresolvedSymbol (sym, s, source_reference);
1543 if (sym == null) {
1544 Report.error (source_reference, "a symbol must be specified");
1546 return sym;
1549 void set_symbol_mapping (UnresolvedSymbol map_from, Symbol map_to) {
1550 // last mapping is the most up-to-date
1551 if (map_from is UnresolvedSymbol) {
1552 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
1556 void assume_parameter_names (Signal sig, Symbol sym, bool skip_first) {
1557 var iter = ((Callable) sym).get_parameters ().iterator ();
1558 bool first = true;
1559 foreach (var param in sig.get_parameters ()) {
1560 if (!iter.next ()) {
1561 // unreachable for valid GIR
1562 break;
1564 if (skip_first && first) {
1565 if (!iter.next ()) {
1566 // unreachable for valid GIR
1567 break;
1569 first = false;
1571 param.name = iter.get ().name;
1575 Node? find_invoker (Node node) {
1576 /* most common use case is invoker has at least the given method prefix
1577 and the same parameter names */
1578 var m = (Method) node.symbol;
1579 var prefix = "%s_".printf (m.name);
1580 foreach (var n in node.parent.members) {
1581 if (!n.name.has_prefix (prefix)) {
1582 continue;
1584 Method? invoker = n.symbol as Method;
1585 if (invoker == null || (m.get_parameters().size != invoker.get_parameters().size)) {
1586 continue;
1588 var iter = invoker.get_parameters ().iterator ();
1589 foreach (var param in m.get_parameters ()) {
1590 assert (iter.next ());
1591 if (param.name != iter.get().name) {
1592 invoker = null;
1593 break;
1596 if (invoker != null) {
1597 return n;
1601 return null;
1604 Metadata get_current_metadata () {
1605 var selector = reader.name;
1606 var child_name = reader.get_attribute ("name");
1607 if (child_name == null) {
1608 child_name = reader.get_attribute ("glib:name");
1610 // Give a transparent union the generic name "union"
1611 if (selector == "union" && child_name == null) {
1612 child_name = "union";
1614 if (child_name == null) {
1615 return Metadata.empty;
1617 selector = selector.replace ("-", "_");
1618 child_name = child_name.replace ("-", "_");
1620 if (selector.has_prefix ("glib:")) {
1621 selector = selector.substring ("glib:".length);
1624 return metadata.match_child (child_name, selector);
1627 bool push_metadata () {
1628 var new_metadata = get_current_metadata ();
1629 // skip ?
1630 if (new_metadata.has_argument (ArgumentType.SKIP)) {
1631 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1632 return false;
1634 } else if (reader.get_attribute ("introspectable") == "0" || reader.get_attribute ("private") == "1") {
1635 return false;
1638 metadata_stack.add (metadata);
1639 metadata = new_metadata;
1641 return true;
1644 void pop_metadata () {
1645 metadata = metadata_stack.remove_at (metadata_stack.size - 1);
1648 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1649 int type_arguments_length = (int) type_arguments.length;
1650 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1652 int depth = 0;
1653 for (var c = 0 ; c < type_arguments_length ; c++) {
1654 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1655 depth++;
1656 current.append_unichar (type_arguments[c]);
1657 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1658 depth--;
1659 current.append_unichar (type_arguments[c]);
1660 } else if (type_arguments[c] == ',') {
1661 if (depth == 0) {
1662 var dt = parse_type_from_string (current.str, true, source_reference);
1663 if (dt == null) {
1664 return false;
1666 parent_type.add_type_argument (dt);
1667 current.truncate ();
1668 } else {
1669 current.append_unichar (type_arguments[c]);
1671 } else {
1672 current.append_unichar (type_arguments[c]);
1676 var dt = parse_type_from_string (current.str, true, source_reference);
1677 if (dt == null) {
1678 return false;
1680 parent_type.add_type_argument (dt);
1682 return true;
1685 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1686 if (type_from_string_regex == null) {
1687 try {
1688 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[,*\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1689 } catch (GLib.RegexError e) {
1690 GLib.error ("Unable to compile regex: %s", e.message);
1694 GLib.MatchInfo match;
1695 if (!type_from_string_regex.match (type_string, 0, out match)) {
1696 Report.error (source_reference, "unable to parse type");
1697 return null;
1700 DataType? type = null;
1702 var ownership_data = match.fetch (1);
1703 var type_name = match.fetch (2);
1704 var type_arguments_data = match.fetch (3);
1705 var pointers_data = match.fetch (4);
1706 var array_data = match.fetch (5);
1707 var nullable_data = match.fetch (6);
1709 var nullable = nullable_data != null && nullable_data.length > 0;
1711 if (ownership_data == null && type_name == "void") {
1712 if (array_data == null && !nullable) {
1713 type = new VoidType (source_reference);
1714 if (pointers_data != null) {
1715 for (int i=0; i < pointers_data.length; i++) {
1716 type = new PointerType (type);
1719 return type;
1720 } else {
1721 Report.error (source_reference, "invalid void type");
1722 return null;
1726 bool value_owned = owned_by_default;
1728 if (ownership_data == "owned") {
1729 if (owned_by_default) {
1730 Report.error (source_reference, "unexpected `owned' keyword");
1731 } else {
1732 value_owned = true;
1734 } else if (ownership_data == "unowned") {
1735 if (owned_by_default) {
1736 value_owned = false;
1737 } else {
1738 Report.error (source_reference, "unexpected `unowned' keyword");
1739 return null;
1743 var sym = parse_symbol_from_string (type_name, source_reference);
1744 if (sym == null) {
1745 return null;
1747 type = new UnresolvedType.from_symbol (sym, source_reference);
1749 if (type_arguments_data != null && type_arguments_data.length > 0) {
1750 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1751 return null;
1755 if (pointers_data != null) {
1756 for (int i=0; i < pointers_data.length; i++) {
1757 type = new PointerType (type);
1761 if (array_data != null && array_data.length != 0) {
1762 type.value_owned = true;
1763 type = new ArrayType (type, (int) array_data.length - 1, source_reference);
1766 type.nullable = nullable;
1767 type.value_owned = value_owned;
1768 return type;
1771 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1772 if (metadata.has_argument (arg_type)) {
1773 return metadata.get_string (arg_type);
1774 } else {
1775 return reader.get_attribute (attribute_name);
1780 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1782 DataType? element_get_type (DataType orig_type, bool owned_by_default, ref bool no_array_length, ref bool array_null_terminated, out bool changed = null) {
1783 changed = false;
1784 var type = orig_type;
1786 if (metadata.has_argument (ArgumentType.TYPE)) {
1787 type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1788 changed = true;
1789 } else if (!(type is VoidType)) {
1790 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1791 type.remove_all_type_arguments ();
1792 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1795 if (!(type is ArrayType) && metadata.get_bool (ArgumentType.ARRAY)) {
1796 type.value_owned = true;
1797 type = new ArrayType (type, 1, type.source_reference);
1798 changed = true;
1801 if (owned_by_default) {
1802 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED, !type.value_owned);
1803 } else {
1804 type.value_owned = metadata.get_bool (ArgumentType.OWNED, type.value_owned);
1806 type.nullable = metadata.get_bool (ArgumentType.NULLABLE, type.nullable);
1809 if (type is ArrayType) {
1810 if (!(orig_type is ArrayType)) {
1811 no_array_length = true;
1813 array_null_terminated = metadata.get_bool (ArgumentType.ARRAY_NULL_TERMINATED, array_null_terminated);
1816 return type;
1819 string? element_get_name (string? gir_name = null) {
1820 var name = gir_name;
1821 if (name == null) {
1822 name = reader.get_attribute ("name");
1824 var pattern = metadata.get_string (ArgumentType.NAME);
1825 if (pattern != null) {
1826 if (pattern.index_of_char ('(') < 0) {
1827 // shortcut for "(.+)/replacement"
1828 name = pattern;
1829 } else {
1830 try {
1831 string replacement = "\\1"; // replace the whole name with the match by default
1832 var split = pattern.split ("/");
1833 if (split.length > 1) {
1834 pattern = split[0];
1835 replacement = split[1];
1837 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1838 name = regex.replace (name, -1, 0, replacement);
1839 } catch (Error e) {
1840 name = pattern;
1843 } else {
1844 if (name != null && name.has_suffix ("Enum")) {
1845 name = name.substring (0, name.length - "Enum".length);
1849 return name;
1852 string? element_get_type_id () {
1853 var type_id = metadata.get_string (ArgumentType.TYPE_ID);
1854 if (type_id != null) {
1855 return type_id;
1858 type_id = reader.get_attribute ("glib:get-type");
1859 if (type_id != null) {
1860 type_id += " ()";
1862 return type_id;
1865 void set_array_ccode (Symbol sym, ParameterInfo info) {
1866 sym.set_attribute_double ("CCode", "array_length_pos", info.vala_idx);
1867 if (sym is Parameter) {
1868 sym.set_attribute_string ("CCode", "array_length_cname", info.param.name);
1870 var type_name = info.param.variable_type.to_qualified_string ();
1871 if (type_name != "int") {
1872 var st = root.lookup (type_name);
1873 if (st != null) {
1874 if (sym is Method) {
1875 var m = (Method) sym;
1876 m.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1877 } else {
1878 var param = (Parameter) sym;
1879 param.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1885 void set_type_id_ccode (Symbol sym) {
1886 if (sym.has_attribute_argument ("CCode", "has_type_id")
1887 || sym.has_attribute_argument ("CCode", "type_id"))
1888 return;
1890 var type_id = element_get_type_id ();
1891 if (type_id == null) {
1892 sym.set_attribute_bool ("CCode", "has_type_id", false);
1893 } else {
1894 sym.set_attribute_string ("CCode", "type_id", type_id);
1898 void parse_repository () {
1899 start_element ("repository");
1900 if (reader.get_attribute ("version") != GIR_VERSION) {
1901 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1902 return;
1904 next ();
1905 while (current_token == MarkupTokenType.START_ELEMENT) {
1906 if (reader.name == "namespace") {
1907 parse_namespace ();
1908 } else if (reader.name == "include") {
1909 parse_include ();
1910 } else if (reader.name == "package") {
1911 var pkg = parse_package ();
1912 this.current_source_file.package_name = pkg;
1913 if (context.has_package (pkg)) {
1914 // package already provided elsewhere, stop parsing this GIR
1915 // if it was not passed explicitly
1916 if (!this.current_source_file.explicit) {
1917 return;
1919 } else {
1920 context.add_package (pkg);
1922 } else if (reader.name == "c:include") {
1923 parse_c_include ();
1924 } else {
1925 // error
1926 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1927 skip_element ();
1930 end_element ("repository");
1933 void parse_include () {
1934 start_element ("include");
1935 var pkg = reader.get_attribute ("name");
1936 var version = reader.get_attribute ("version");
1937 if (version != null) {
1938 pkg = "%s-%s".printf (pkg, version);
1940 // add the package to the queue
1941 context.add_external_package (pkg);
1942 next ();
1943 end_element ("include");
1946 string parse_package () {
1947 start_element ("package");
1948 var pkg = reader.get_attribute ("name");
1949 next ();
1950 end_element ("package");
1951 return pkg;
1954 void parse_c_include () {
1955 start_element ("c:include");
1956 cheader_filenames += reader.get_attribute ("name");
1957 next ();
1958 end_element ("c:include");
1961 void skip_element () {
1962 next ();
1964 int level = 1;
1965 while (level > 0) {
1966 if (current_token == MarkupTokenType.START_ELEMENT) {
1967 level++;
1968 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1969 level--;
1970 } else if (current_token == MarkupTokenType.EOF) {
1971 Report.error (get_current_src (), "unexpected end of file");
1972 break;
1974 next ();
1978 Node? resolve_node (Node parent_scope, UnresolvedSymbol unresolved_sym, bool create_namespace = false) {
1979 if (unresolved_sym.inner == null) {
1980 var scope = parent_scope;
1981 while (scope != null) {
1982 var node = scope.lookup (unresolved_sym.name, create_namespace, unresolved_sym.source_reference);
1983 if (node != null) {
1984 return node;
1986 scope = scope.parent;
1988 } else {
1989 var inner = resolve_node (parent_scope, unresolved_sym.inner, create_namespace);
1990 if (inner != null) {
1991 return inner.lookup (unresolved_sym.name, create_namespace, unresolved_sym.source_reference);
1994 return null;
1997 Symbol? resolve_symbol (Node parent_scope, UnresolvedSymbol unresolved_sym) {
1998 var node = resolve_node (parent_scope, unresolved_sym);
1999 if (node != null) {
2000 return node.symbol;
2002 return null;
2005 void push_node (string name, bool merge) {
2006 var parent = current;
2007 if (metadata.has_argument (ArgumentType.PARENT)) {
2008 var target = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
2009 parent = resolve_node (root, target, true);
2012 var node = parent.lookup (name);
2013 if (node == null || (node.symbol != null && !merge)) {
2014 node = new Node (name);
2015 node.new_symbol = true;
2016 parent.add_member (node);
2017 } else {
2018 Node.new_namespaces.remove (node);
2020 node.element_type = reader.name;
2021 node.girdata = reader.get_attributes ();
2022 node.metadata = metadata;
2023 node.source_reference = get_current_src ();
2025 var gir_name = node.get_gir_name ();
2026 if (parent != current || gir_name != name) {
2027 set_symbol_mapping (new UnresolvedSymbol (null, gir_name), node.get_unresolved_symbol ());
2030 tree_stack.add (current);
2031 current = node;
2034 void pop_node () {
2035 old_current = current;
2036 current = tree_stack.remove_at (tree_stack.size - 1);
2039 void parse_namespace () {
2040 start_element ("namespace");
2042 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
2043 if (cprefix != null) {
2044 int idx = cprefix.index_of (",");
2045 if (idx != -1) {
2046 cprefix = cprefix.substring (0, idx);
2050 string? lower_case_cprefix = reader.get_attribute ("c:symbol-prefixes");
2051 string vala_namespace = cprefix;
2052 string gir_namespace = reader.get_attribute ("name");
2053 string gir_version = reader.get_attribute ("version");
2055 if (lower_case_cprefix != null) {
2056 int idx = lower_case_cprefix.index_of (",");
2057 if (idx != -1) {
2058 lower_case_cprefix = lower_case_cprefix.substring (0, idx);
2062 if (provided_namespaces.contains ("%s-%s".printf (gir_namespace, gir_version))) {
2063 skip_element ();
2064 return;
2067 // load metadata, first look into metadata directories then in the same directory of the .gir.
2068 string? metadata_filename = context.get_metadata_path (current_source_file.filename);
2069 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
2070 var metadata_parser = new MetadataParser ();
2071 var metadata_file = new SourceFile (context, current_source_file.file_type, metadata_filename);
2072 context.add_source_file (metadata_file);
2073 metadata = metadata_parser.parse_metadata (metadata_file);
2074 metadata_roots.add (metadata);
2077 var ns_metadata = metadata.match_child (gir_namespace);
2078 if (ns_metadata.has_argument (ArgumentType.NAME)) {
2079 vala_namespace = ns_metadata.get_string (ArgumentType.NAME);
2081 if (vala_namespace == null) {
2082 vala_namespace = gir_namespace;
2085 current_source_file.gir_namespace = gir_namespace;
2086 current_source_file.gir_version = gir_version;
2088 Namespace ns;
2089 push_node (vala_namespace, true);
2090 if (current.new_symbol) {
2091 ns = new Namespace (vala_namespace, current.source_reference);
2092 current.symbol = ns;
2093 } else {
2094 ns = (Namespace) current.symbol;
2095 ns.attributes = null;
2096 ns.source_reference = current.source_reference;
2099 current.metadata = ns_metadata;
2101 if (ns_metadata.has_argument (ArgumentType.CPREFIX)) {
2102 cprefix = ns_metadata.get_string (ArgumentType.CPREFIX);
2105 if (ns_metadata.has_argument (ArgumentType.LOWER_CASE_CPREFIX)) {
2106 lower_case_cprefix = ns_metadata.get_string (ArgumentType.LOWER_CASE_CPREFIX);
2107 } else if (lower_case_cprefix != null) {
2108 lower_case_cprefix += "_";
2111 ns.set_attribute_string ("CCode", "gir_namespace", gir_namespace);
2112 ns.set_attribute_string ("CCode", "gir_version", gir_version);
2114 if (cprefix != null) {
2115 ns.set_attribute_string ("CCode", "cprefix", cprefix);
2116 if (lower_case_cprefix == null) {
2117 ns.set_attribute_string ("CCode", "lower_case_cprefix", Symbol.camel_case_to_lower_case (cprefix) + "_");
2121 if (lower_case_cprefix != null) {
2122 ns.set_attribute_string ("CCode", "lower_case_cprefix", lower_case_cprefix);
2125 if (cheader_filenames != null) {
2126 ns.set_attribute_string ("CCode", "cheader_filename", string.joinv (",", cheader_filenames));
2129 next ();
2130 while (current_token == MarkupTokenType.START_ELEMENT) {
2131 if (!push_metadata ()) {
2132 skip_element ();
2133 continue;
2136 if (reader.name == "alias") {
2137 parse_alias ();
2138 } else if (reader.name == "enumeration") {
2139 if (metadata.has_argument (ArgumentType.ERRORDOMAIN)) {
2140 if (metadata.get_bool (ArgumentType.ERRORDOMAIN)) {
2141 parse_error_domain ();
2142 } else {
2143 parse_enumeration ();
2145 } else {
2146 if ((reader.get_attribute ("glib:error-quark") != null) || (reader.get_attribute ("glib:error-domain") != null)) {
2147 parse_error_domain ();
2148 } else {
2149 parse_enumeration ();
2152 } else if (reader.name == "bitfield") {
2153 parse_bitfield ();
2154 } else if (reader.name == "function") {
2155 parse_method ("function");
2156 } else if (reader.name == "callback") {
2157 parse_callback ();
2158 } else if (reader.name == "record") {
2159 if (metadata.has_argument (ArgumentType.STRUCT)) {
2160 if (metadata.get_bool (ArgumentType.STRUCT)) {
2161 parse_record ();
2162 } else {
2163 parse_boxed ("record");
2165 } else if (element_get_type_id () != null) {
2166 parse_boxed ("record");
2167 } else if (!reader.get_attribute ("name").has_suffix ("Private")) {
2168 if (reader.get_attribute ("glib:is-gtype-struct-for") == null && reader.get_attribute ("disguised") == "1") {
2169 parse_boxed ("record");
2170 } else {
2171 parse_record ();
2173 } else {
2174 skip_element ();
2176 } else if (reader.name == "class") {
2177 parse_class ();
2178 } else if (reader.name == "interface") {
2179 parse_interface ();
2180 } else if (reader.name == "glib:boxed") {
2181 parse_boxed ("glib:boxed");
2182 } else if (reader.name == "union") {
2183 if (element_get_type_id () != null && !metadata.get_bool (ArgumentType.STRUCT)) {
2184 parse_boxed ("union");
2185 } else {
2186 parse_union ();
2188 } else if (reader.name == "constant") {
2189 parse_constant ();
2190 } else {
2191 // error
2192 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
2193 skip_element ();
2196 pop_metadata ();
2198 pop_node ();
2199 end_element ("namespace");
2202 void parse_alias () {
2203 start_element ("alias");
2204 push_node (element_get_name (), true);
2205 // not enough information, symbol will be created while processing the tree
2207 next ();
2209 if (current.comment == null) {
2210 current.comment = parse_symbol_doc ();
2211 } else {
2212 parse_symbol_doc ();
2215 bool no_array_length = false;
2216 bool array_null_terminated = false;
2217 current.base_type = element_get_type (parse_type (null, null, true), true, ref no_array_length, ref array_null_terminated);
2219 if (metadata.has_argument (ArgumentType.BASE_TYPE)) {
2220 current.base_type = parse_type_from_string (metadata.get_string (ArgumentType.BASE_TYPE), true, metadata.get_source_reference (ArgumentType.BASE_TYPE));
2223 pop_node ();
2224 end_element ("alias");
2227 private void calculate_common_prefix (ref string? common_prefix, string cname) {
2228 if (common_prefix == null) {
2229 common_prefix = cname;
2230 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
2231 // FIXME: could easily be made faster
2232 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2234 } else {
2235 while (!cname.has_prefix (common_prefix)) {
2236 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2239 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
2240 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
2241 // enum values may not consist solely of digits
2242 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2246 GirComment? parse_symbol_doc () {
2247 GirComment? comment = null;
2249 while (current_token == MarkupTokenType.START_ELEMENT) {
2250 unowned string reader_name = reader.name;
2252 if (reader_name == "doc") {
2253 start_element ("doc");
2254 next ();
2257 if (current_token == MarkupTokenType.TEXT) {
2258 comment = new GirComment (reader.content, current.source_reference);
2259 next ();
2262 end_element ("doc");
2263 } else if (reader_name == "doc-version" || reader_name == "doc-deprecated" || reader_name == "doc-stability") {
2264 skip_element ();
2265 } else {
2266 break;
2270 return comment;
2273 Comment? parse_doc () {
2274 Comment? comment = null;
2276 while (current_token == MarkupTokenType.START_ELEMENT) {
2277 unowned string reader_name = reader.name;
2279 if (reader_name == "doc") {
2280 start_element ("doc");
2281 next ();
2284 if (current_token == MarkupTokenType.TEXT) {
2285 comment = new Comment (reader.content, current.source_reference);
2286 next ();
2289 end_element ("doc");
2290 } else if (reader_name == "doc-version" || reader_name == "doc-deprecated" || reader_name == "doc-stability") {
2291 skip_element ();
2292 } else {
2293 break;
2297 return comment;
2300 void parse_enumeration (string element_name = "enumeration", bool error_domain = false) {
2301 start_element (element_name);
2302 push_node (element_get_name (), true);
2304 Symbol sym;
2305 if (current.new_symbol) {
2306 if (error_domain) {
2307 sym = new ErrorDomain (current.name, current.source_reference);
2308 } else {
2309 var en = new Enum (current.name, current.source_reference);
2310 if (element_name == "bitfield") {
2311 en.set_attribute ("Flags", true);
2313 sym = en;
2315 current.symbol = sym;
2316 } else {
2317 sym = current.symbol;
2320 if (!error_domain)
2321 set_type_id_ccode (sym);
2323 sym.external = true;
2324 sym.access = SymbolAccessibility.PUBLIC;
2326 string common_prefix = null;
2327 bool has_member = false;
2329 next ();
2331 sym.comment = parse_symbol_doc ();
2333 while (current_token == MarkupTokenType.START_ELEMENT) {
2334 if (!push_metadata ()) {
2335 skip_element ();
2336 continue;
2339 if (reader.name == "member") {
2340 has_member = true;
2341 if (error_domain) {
2342 parse_error_member ();
2343 calculate_common_prefix (ref common_prefix, old_current.get_cname ());
2344 } else {
2345 parse_enumeration_member ();
2346 calculate_common_prefix (ref common_prefix, old_current.get_cname ());
2348 } else if (reader.name == "function") {
2349 skip_element ();
2350 } else {
2351 // error
2352 Report.error (get_current_src (), "unknown child element `%s' in `%s'".printf (reader.name, element_name));
2353 skip_element ();
2356 pop_metadata ();
2359 if (!has_member) {
2360 Report.error (get_current_src (), "%s `%s' has no members".printf (element_name, current.name));
2363 if (common_prefix != null) {
2364 sym.set_attribute_string ("CCode", "cprefix", common_prefix);
2367 pop_node ();
2368 end_element (element_name);
2371 void parse_error_domain () {
2372 parse_enumeration ("enumeration", true);
2375 void parse_bitfield () {
2376 parse_enumeration ("bitfield");
2379 void parse_enumeration_member () {
2380 start_element ("member");
2381 push_node (element_get_name().ascii_up().replace ("-", "_"), false);
2383 var ev = new EnumValue (current.name, metadata.get_expression (ArgumentType.DEFAULT), current.source_reference);
2384 current.symbol = ev;
2385 next ();
2387 ev.comment = parse_symbol_doc ();
2389 pop_node ();
2390 end_element ("member");
2393 void parse_error_member () {
2394 start_element ("member");
2395 push_node (element_get_name().ascii_up().replace ("-", "_"), false);
2397 ErrorCode ec;
2398 string value = reader.get_attribute ("value");
2399 if (value != null) {
2400 ec = new ErrorCode.with_value (current.name, new IntegerLiteral (value));
2401 } else {
2402 ec = new ErrorCode (current.name);
2404 current.symbol = ec;
2405 next ();
2407 ec.comment = parse_symbol_doc ();
2409 pop_node ();
2410 end_element ("member");
2413 DataType parse_return_value (out string? ctype = null, out int array_length_idx = null, out bool no_array_length = null, out bool array_null_terminated = null, out Comment? comment = null) {
2414 start_element ("return-value");
2416 string transfer = reader.get_attribute ("transfer-ownership");
2417 string nullable = reader.get_attribute ("nullable");
2418 string allow_none = reader.get_attribute ("allow-none");
2419 next ();
2421 comment = parse_doc ();
2423 var transfer_elements = transfer != "container";
2424 var type = parse_type (out ctype, out array_length_idx, transfer_elements, out no_array_length, out array_null_terminated);
2425 if (transfer == "full" || transfer == "container") {
2426 type.value_owned = true;
2428 if (nullable == "1" || allow_none == "1") {
2429 type.nullable = true;
2431 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2433 end_element ("return-value");
2434 return type;
2437 Parameter parse_parameter (out int array_length_idx = null, out int closure_idx = null, out int destroy_idx = null, out string? scope = null, out Comment? comment = null, string? default_name = null) {
2438 Parameter param;
2440 array_length_idx = -1;
2441 closure_idx = -1;
2442 destroy_idx = -1;
2444 string element_type = reader.name;
2445 if (current_token != MarkupTokenType.START_ELEMENT || (element_type != "parameter" && element_type != "instance-parameter")) {
2446 Report.error (get_current_src (), "expected start element of `parameter' or `instance-parameter'");
2448 start_element (element_type);
2449 var name = metadata.get_string (ArgumentType.NAME);
2450 if (name == null) {
2451 name = reader.get_attribute ("name");
2453 if (name == null) {
2454 name = default_name;
2456 string direction = null;
2457 if (metadata.has_argument (ArgumentType.OUT)) {
2458 if (metadata.get_bool (ArgumentType.OUT)) {
2459 direction = "out";
2460 } // null otherwise
2461 } else if (metadata.has_argument (ArgumentType.REF)) {
2462 if (metadata.get_bool (ArgumentType.REF)) {
2463 direction = "inout";
2464 } // null otherwise
2465 } else {
2466 direction = reader.get_attribute ("direction");
2468 string transfer = reader.get_attribute ("transfer-ownership");
2469 string nullable = reader.get_attribute ("nullable");
2470 string allow_none = reader.get_attribute ("allow-none");
2472 scope = element_get_string ("scope", ArgumentType.SCOPE);
2474 string closure = reader.get_attribute ("closure");
2475 string destroy = reader.get_attribute ("destroy");
2476 if (closure != null && &closure_idx != null) {
2477 closure_idx = int.parse (closure);
2479 if (destroy != null && &destroy_idx != null) {
2480 destroy_idx = int.parse (destroy);
2482 if (metadata.has_argument (ArgumentType.CLOSURE)) {
2483 closure_idx = metadata.get_integer (ArgumentType.CLOSURE);
2485 if (metadata.has_argument (ArgumentType.DESTROY)) {
2486 destroy_idx = metadata.get_integer (ArgumentType.DESTROY);
2489 next ();
2491 comment = parse_doc ();
2493 if (reader.name == "varargs") {
2494 start_element ("varargs");
2495 next ();
2496 param = new Parameter.with_ellipsis (get_current_src ());
2497 end_element ("varargs");
2498 } else {
2499 string ctype;
2500 bool no_array_length;
2501 bool array_null_terminated;
2502 var type = parse_type (out ctype, out array_length_idx, transfer != "container", out no_array_length, out array_null_terminated);
2503 if (transfer == "full" || transfer == "container" || destroy != null) {
2504 type.value_owned = true;
2506 if (nullable == "1" || (allow_none == "1" && direction != "out")) {
2507 type.nullable = true;
2510 bool changed;
2511 type = element_get_type (type, direction == "out" || direction == "inout", ref no_array_length, ref array_null_terminated, out changed);
2512 if (!changed) {
2513 // discard ctype, duplicated information
2514 ctype = null;
2517 param = new Parameter (name, type, get_current_src ());
2518 if (ctype != null) {
2519 param.set_attribute_string ("CCode", "type", ctype);
2521 if (direction == "out") {
2522 param.direction = ParameterDirection.OUT;
2523 } else if (direction == "inout") {
2524 param.direction = ParameterDirection.REF;
2526 if (type is ArrayType) {
2527 if (metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
2528 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
2529 } else {
2530 if (no_array_length || array_null_terminated) {
2531 param.set_attribute_bool ("CCode", "array_length", !no_array_length);
2533 if (array_null_terminated) {
2534 param.set_attribute_bool ("CCode", "array_null_terminated", array_null_terminated);
2538 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
2540 // empty tuple used for parameters without initializer
2541 if (param.initializer is Tuple) {
2542 param.initializer = null;
2545 end_element (element_type);
2546 return param;
2549 DataType parse_type (out string? ctype = null, out int array_length_idx = null, bool transfer_elements = true, out bool no_array_length = null, out bool array_null_terminated = null) {
2550 bool is_array = false;
2551 string type_name = reader.get_attribute ("name");
2552 ctype = null;
2554 var fixed_length = -1;
2555 array_length_idx = -1;
2556 no_array_length = true;
2557 array_null_terminated = true;
2559 if (reader.name == "array") {
2560 is_array = true;
2561 start_element ("array");
2563 var src = get_current_src ();
2565 if (type_name == null) {
2566 if (reader.get_attribute ("length") != null) {
2567 array_length_idx = int.parse (reader.get_attribute ("length"));
2568 no_array_length = false;
2569 array_null_terminated = false;
2571 if (reader.get_attribute ("fixed-size") != null) {
2572 fixed_length = int.parse (reader.get_attribute ("fixed-size"));
2573 array_null_terminated = false;
2575 if (reader.get_attribute ("c:type") == "GStrv") {
2576 no_array_length = true;
2577 array_null_terminated = true;
2579 if (reader.get_attribute ("zero-terminated") != null) {
2580 array_null_terminated = int.parse (reader.get_attribute ("zero-terminated")) != 0;
2582 next ();
2583 var element_type = parse_type ();
2584 element_type.value_owned = transfer_elements;
2585 end_element ("array");
2587 var array_type = new ArrayType (element_type, 1, src);
2588 if (fixed_length > 0) {
2589 array_type.fixed_length = true;
2590 array_type.length = new IntegerLiteral (fixed_length.to_string ());
2592 return array_type;
2594 } else if (reader.name == "callback"){
2595 parse_callback ();
2596 return new DelegateType ((Delegate) old_current.symbol);
2597 } else {
2598 start_element ("type");
2601 ctype = reader.get_attribute("c:type");
2603 next ();
2605 if (type_name == "GLib.PtrArray"
2606 && current_token == MarkupTokenType.START_ELEMENT) {
2607 type_name = "GLib.GenericArray";
2610 if (type_name == null) {
2611 type_name = ctype;
2614 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
2616 // type arguments / element types
2617 while (current_token == MarkupTokenType.START_ELEMENT) {
2618 if (type_name == "GLib.ByteArray") {
2619 skip_element ();
2620 continue;
2622 var element_type = parse_type ();
2623 element_type.value_owned = transfer_elements;
2624 type.add_type_argument (element_type);
2625 unresolved_type_arguments.add (element_type);
2628 end_element (is_array ? "array" : "type");
2629 return type;
2632 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
2633 no_array_length = false;
2634 array_null_terminated = false;
2636 DataType? type = null;
2637 if (type_name == "none") {
2638 type = new VoidType (get_current_src ());
2639 } else if (type_name == "gpointer") {
2640 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
2641 } else if (type_name == "GObject.Strv") {
2642 var element_type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string"));
2643 element_type.value_owned = true;
2644 type = new ArrayType (element_type, 1, get_current_src ());
2645 no_array_length = true;
2646 array_null_terminated = true;
2647 } else {
2648 bool known_type = true;
2649 if (type_name == "utf8") {
2650 type_name = "string";
2651 } else if (type_name == "gboolean") {
2652 type = new BooleanType ((Struct) context.root.scope.lookup ("bool"));
2653 } else if (type_name == "gchar") {
2654 type_name = "char";
2655 } else if (type_name == "gshort") {
2656 type_name = "short";
2657 } else if (type_name == "gushort") {
2658 type_name = "ushort";
2659 } else if (type_name == "gint") {
2660 type_name = "int";
2661 } else if (type_name == "guint") {
2662 type_name = "uint";
2663 } else if (type_name == "glong") {
2664 if (ctype != null && ctype.has_prefix ("gssize")) {
2665 type_name = "ssize_t";
2666 } else if (ctype != null && ctype.has_prefix ("gintptr")) {
2667 type_name = "intptr";
2668 } else {
2669 type_name = "long";
2671 } else if (type_name == "gulong") {
2672 if (ctype != null && ctype.has_prefix ("gsize")) {
2673 type_name = "size_t";
2674 } else if (ctype != null && ctype.has_prefix ("guintptr")) {
2675 type_name = "uintptr";
2676 } else {
2677 type_name = "ulong";
2679 } else if (type_name == "gint8") {
2680 type_name = "int8";
2681 } else if (type_name == "guint8") {
2682 type_name = "uint8";
2683 } else if (type_name == "gint16") {
2684 type_name = "int16";
2685 } else if (type_name == "guint16") {
2686 type_name = "uint16";
2687 } else if (type_name == "gint32") {
2688 type_name = "int32";
2689 } else if (type_name == "guint32") {
2690 type_name = "uint32";
2691 } else if (type_name == "gint64") {
2692 type_name = "int64";
2693 } else if (type_name == "guint64") {
2694 type_name = "uint64";
2695 } else if (type_name == "gfloat") {
2696 type_name = "float";
2697 } else if (type_name == "gdouble") {
2698 type_name = "double";
2699 } else if (type_name == "filename") {
2700 type_name = "string";
2701 } else if (type_name == "GLib.offset") {
2702 type_name = "int64";
2703 } else if (type_name == "gsize") {
2704 type_name = "size_t";
2705 } else if (type_name == "gssize") {
2706 type_name = "ssize_t";
2707 } else if (type_name == "guintptr") {
2708 type_name = "uintptr";
2709 } else if (type_name == "gintptr") {
2710 type_name = "intptr";
2711 } else if (type_name == "GType") {
2712 type_name = "GLib.Type";
2713 } else if (type_name == "GLib.String") {
2714 type_name = "GLib.StringBuilder";
2715 } else if (type_name == "GObject.Class") {
2716 type_name = "GLib.ObjectClass";
2717 } else if (type_name == "gunichar") {
2718 type_name = "unichar";
2719 } else if (type_name == "GLib.Data") {
2720 type_name = "GLib.Datalist";
2721 } else if (type_name == "Atk.ImplementorIface") {
2722 type_name = "Atk.Implementor";
2723 } else {
2724 known_type = false;
2727 if (type == null) {
2728 var sym = parse_symbol_from_string (type_name, get_current_src ());
2729 type = new UnresolvedType.from_symbol (sym, get_current_src ());
2730 if (!known_type) {
2731 unresolved_gir_symbols.add (sym);
2736 return type;
2739 void parse_record () {
2740 start_element ("record");
2741 push_node (element_get_name (), true);
2743 Struct st;
2744 if (current.new_symbol) {
2745 st = new Struct (element_get_name (), current.source_reference);
2746 current.symbol = st;
2747 } else {
2748 st = (Struct) current.symbol;
2751 set_type_id_ccode (st);
2753 st.external = true;
2754 st.access = SymbolAccessibility.PUBLIC;
2756 var gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
2757 if (gtype_struct_for != null) {
2758 current.gtype_struct_for = parse_symbol_from_string (gtype_struct_for, current.source_reference);
2759 unresolved_gir_symbols.add (current.gtype_struct_for);
2762 bool first_field = true;
2763 next ();
2765 st.comment = parse_symbol_doc ();
2767 while (current_token == MarkupTokenType.START_ELEMENT) {
2768 if (!push_metadata ()) {
2769 if (first_field && reader.name == "field") {
2770 first_field = false;
2772 skip_element ();
2773 continue;
2776 if (reader.name == "field") {
2777 if (reader.get_attribute ("name") != "priv" && !(first_field && gtype_struct_for != null)) {
2778 parse_field ();
2779 } else {
2780 skip_element ();
2782 first_field = false;
2783 } else if (reader.name == "constructor") {
2784 parse_constructor ();
2785 } else if (reader.name == "method") {
2786 parse_method ("method");
2787 } else if (reader.name == "function") {
2788 skip_element ();
2789 } else if (reader.name == "union") {
2790 parse_union ();
2791 } else {
2792 // error
2793 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
2794 skip_element ();
2797 pop_metadata ();
2800 pop_node ();
2801 end_element ("record");
2804 void parse_class () {
2805 start_element ("class");
2806 push_node (element_get_name (), true);
2808 Class cl;
2809 var parent = reader.get_attribute ("parent");
2810 if (current.new_symbol) {
2811 cl = new Class (current.name, current.source_reference);
2812 cl.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT, reader.get_attribute ("abstract") == "1");
2814 if (parent != null) {
2815 cl.add_base_type (parse_type_from_gir_name (parent));
2817 current.symbol = cl;
2818 } else {
2819 cl = (Class) current.symbol;
2822 set_type_id_ccode (cl);
2824 cl.access = SymbolAccessibility.PUBLIC;
2825 cl.external = true;
2827 next ();
2829 cl.comment = parse_symbol_doc ();
2831 var first_field = true;
2832 while (current_token == MarkupTokenType.START_ELEMENT) {
2833 if (!push_metadata ()) {
2834 if (first_field && reader.name == "field") {
2835 first_field = false;
2837 skip_element ();
2838 continue;
2841 if (reader.name == "implements") {
2842 start_element ("implements");
2843 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
2844 next ();
2845 end_element ("implements");
2846 } else if (reader.name == "constant") {
2847 parse_constant ();
2848 } else if (reader.name == "field") {
2849 if (first_field && parent != null) {
2850 // first field is guaranteed to be the parent instance
2851 skip_element ();
2852 } else {
2853 if (reader.get_attribute ("name") != "priv") {
2854 parse_field ();
2855 } else {
2856 skip_element ();
2859 first_field = false;
2860 } else if (reader.name == "property") {
2861 parse_property ();
2862 } else if (reader.name == "constructor") {
2863 parse_constructor ();
2864 } else if (reader.name == "function") {
2865 parse_method ("function");
2866 } else if (reader.name == "method") {
2867 parse_method ("method");
2868 } else if (reader.name == "virtual-method") {
2869 parse_method ("virtual-method");
2870 } else if (reader.name == "union") {
2871 parse_union ();
2872 } else if (reader.name == "glib:signal") {
2873 parse_signal ();
2874 } else {
2875 // error
2876 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2877 skip_element ();
2880 pop_metadata ();
2883 pop_node ();
2884 end_element ("class");
2887 void parse_interface () {
2888 start_element ("interface");
2889 push_node (element_get_name (), true);
2891 Interface iface;
2892 if (current.new_symbol) {
2893 iface = new Interface (current.name, current.source_reference);
2894 current.symbol = iface;
2895 } else {
2896 iface = (Interface) current.symbol;
2899 set_type_id_ccode (iface);
2901 iface.access = SymbolAccessibility.PUBLIC;
2902 iface.external = true;
2905 next ();
2907 iface.comment = parse_symbol_doc ();
2909 while (current_token == MarkupTokenType.START_ELEMENT) {
2910 if (!push_metadata ()) {
2911 skip_element ();
2912 continue;
2915 if (reader.name == "prerequisite") {
2916 start_element ("prerequisite");
2917 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2918 next ();
2919 end_element ("prerequisite");
2920 } else if (reader.name == "field") {
2921 parse_field ();
2922 } else if (reader.name == "property") {
2923 parse_property ();
2924 } else if (reader.name == "virtual-method") {
2925 parse_method ("virtual-method");
2926 } else if (reader.name == "function") {
2927 parse_method ("function");
2928 } else if (reader.name == "method") {
2929 parse_method ("method");
2930 } else if (reader.name == "glib:signal") {
2931 parse_signal ();
2932 } else {
2933 // error
2934 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2935 skip_element ();
2938 pop_metadata ();
2941 pop_node ();
2942 end_element ("interface");
2945 void parse_field () {
2946 start_element ("field");
2947 push_node (element_get_name (), false);
2949 string nullable = reader.get_attribute ("nullable");
2950 string allow_none = reader.get_attribute ("allow-none");
2951 next ();
2953 var comment = parse_symbol_doc ();
2955 bool no_array_length;
2956 bool array_null_terminated;
2957 int array_length_idx;
2958 var type = parse_type (null, out array_length_idx, true, out no_array_length, out array_null_terminated);
2959 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2961 string name = current.name;
2962 string cname = current.girdata["name"];
2964 var field = new Field (name, type, null, current.source_reference);
2965 field.access = SymbolAccessibility.PUBLIC;
2966 field.comment = comment;
2967 if (name != cname) {
2968 field.set_attribute_string ("CCode", "cname", cname);
2970 if (type is ArrayType) {
2971 if (!no_array_length && array_length_idx > -1) {
2972 current.array_length_idx = array_length_idx;
2974 if (no_array_length || array_null_terminated) {
2975 field.set_attribute_bool ("CCode", "array_length", !no_array_length);
2977 if (array_null_terminated) {
2978 field.set_attribute_bool ("CCode", "array_null_terminated", true);
2981 if (nullable == "1" || allow_none == "1") {
2982 type.nullable = true;
2984 current.symbol = field;
2986 pop_node ();
2987 end_element ("field");
2990 Property parse_property () {
2991 start_element ("property");
2992 push_node (element_get_name().replace ("-", "_"), false);
2993 bool is_abstract = metadata.get_bool (ArgumentType.ABSTRACT, current.parent.symbol is Interface);
2994 string transfer = reader.get_attribute ("transfer-ownership");
2996 next ();
2998 var comment = parse_symbol_doc ();
3000 bool no_array_length;
3001 bool array_null_terminated;
3002 var type = parse_type (null, null, transfer != "container", out no_array_length, out array_null_terminated);
3003 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
3004 var prop = new Property (current.name, type, null, null, current.source_reference);
3005 prop.comment = comment;
3006 prop.access = SymbolAccessibility.PUBLIC;
3007 prop.external = true;
3008 prop.is_abstract = is_abstract;
3009 if (no_array_length || array_null_terminated) {
3010 prop.set_attribute_bool ("CCode", "array_length", !no_array_length);
3012 if (array_null_terminated) {
3013 prop.set_attribute_bool ("CCode", "array_null_terminated", true);
3015 current.symbol = prop;
3017 pop_node ();
3018 end_element ("property");
3019 return prop;
3022 void parse_callback () {
3023 parse_function ("callback");
3026 void parse_constructor () {
3027 parse_function ("constructor");
3030 class ParameterInfo {
3031 public ParameterInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx, bool is_async = false) {
3032 this.param = param;
3033 this.array_length_idx = array_length_idx;
3034 this.closure_idx = closure_idx;
3035 this.destroy_idx = destroy_idx;
3036 this.vala_idx = 0.0F;
3037 this.keep = true;
3038 this.is_async = is_async;
3041 public Parameter param;
3042 public float vala_idx;
3043 public int array_length_idx;
3044 public int closure_idx;
3045 public int destroy_idx;
3046 public bool keep;
3047 public bool is_async;
3050 void parse_function (string element_name) {
3051 start_element (element_name);
3052 push_node (element_get_name (reader.get_attribute ("invoker")).replace ("-", "_"), false);
3054 string symbol_type;
3055 if (metadata.has_argument (ArgumentType.SYMBOL_TYPE)) {
3056 symbol_type = metadata.get_string (ArgumentType.SYMBOL_TYPE);
3057 } else {
3058 symbol_type = element_name;
3061 string name = current.name;
3062 string throws_string = reader.get_attribute ("throws");
3063 string invoker = reader.get_attribute ("invoker");
3065 next ();
3067 var comment = parse_symbol_doc ();
3069 DataType return_type;
3070 string return_ctype = null;
3071 int return_array_length_idx = -1;
3072 bool return_no_array_length = false;
3073 bool return_array_null_terminated = false;
3074 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
3075 Comment? return_comment;
3076 return_type = parse_return_value (out return_ctype, out return_array_length_idx, out return_no_array_length, out return_array_null_terminated, out return_comment);
3077 if (return_comment != null) {
3078 if (comment == null) {
3079 comment = new GirComment (null, current.source_reference);
3081 comment.return_content = return_comment;
3083 } else {
3084 return_type = new VoidType ();
3087 Symbol s;
3089 if (symbol_type == "callback") {
3090 s = new Delegate (name, return_type, current.source_reference);
3091 ((Delegate) s).has_target = false;
3092 } else if (symbol_type == "constructor") {
3093 if (name == "new") {
3094 name = null;
3095 } else if (name.has_prefix ("new_")) {
3096 name = name.substring ("new_".length);
3098 var m = new CreationMethod (null, name, current.source_reference);
3099 m.has_construct_function = false;
3101 if (name != null && !current.name.has_prefix ("new_")) {
3102 m.set_attribute_string ("CCode", "cname", current.girdata["c:identifier"]);
3105 string parent_ctype = null;
3106 if (current.parent.symbol is Class) {
3107 parent_ctype = current.parent.get_cname ();
3109 if (return_ctype != null && (parent_ctype == null || return_ctype != parent_ctype + "*")) {
3110 m.set_attribute_string ("CCode", "type", return_ctype);
3112 s = m;
3113 } else if (symbol_type == "glib:signal") {
3114 s = new Signal (name, return_type, current.source_reference);
3115 } else {
3116 s = new Method (name, return_type, current.source_reference);
3119 s.access = SymbolAccessibility.PUBLIC;
3120 s.comment = comment;
3121 s.external = true;
3123 // Transform fixed-array properties of return-type into ccode-attribute
3124 var array_type = return_type as ArrayType;
3125 if (array_type != null && array_type.fixed_length) {
3126 s.set_attribute_string ("CCode", "array_length_cexpr", ((IntegerLiteral) array_type.length).value);
3127 array_type.fixed_length = false;
3128 array_type.length = null;
3131 if (s is Signal) {
3132 if (current.girdata["name"] != name.replace ("_", "-")) {
3133 s.set_attribute_string ("CCode", "cname", current.girdata["name"]);
3137 if (s is Method) {
3138 var m = (Method) s;
3139 if (symbol_type == "virtual-method" || symbol_type == "callback") {
3140 if (current.parent.symbol is Interface) {
3141 m.is_abstract = true;
3142 } else {
3143 m.is_virtual = true;
3145 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
3146 s.set_attribute ("NoWrapper", true, s.source_reference);
3147 } if (current.girdata["name"] != name) {
3148 m.set_attribute_string ("CCode", "vfunc_name", current.girdata["name"]);
3150 } else if (symbol_type == "function") {
3151 m.binding = MemberBinding.STATIC;
3153 if (metadata.has_argument (ArgumentType.FLOATING)) {
3154 m.returns_floating_reference = metadata.get_bool (ArgumentType.FLOATING);
3155 m.return_type.value_owned = true;
3159 if (s is Method && !(s is CreationMethod)) {
3160 var method = (Method) s;
3161 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
3162 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
3163 method.is_abstract = false;
3164 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
3165 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
3166 method.is_virtual = false;
3168 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
3169 method.set_attribute_string ("CCode", "vfunc_name", metadata.get_string (ArgumentType.VFUNC_NAME));
3170 method.is_virtual = true;
3172 if (metadata.has_argument (ArgumentType.FINISH_VFUNC_NAME)) {
3173 method.set_attribute_string ("CCode", "finish_vfunc_name", metadata.get_string (ArgumentType.FINISH_VFUNC_NAME));
3174 method.is_virtual = true;
3178 if (!(metadata.get_expression (ArgumentType.THROWS) is NullLiteral)) {
3179 if (metadata.has_argument (ArgumentType.THROWS)) {
3180 var error_types = metadata.get_string(ArgumentType.THROWS).split(",");
3181 foreach (var error_type in error_types) {
3182 s.add_error_type (parse_type_from_string (error_type, true, metadata.get_source_reference (ArgumentType.THROWS)));
3184 } else if (throws_string == "1") {
3185 s.add_error_type (new ErrorType (null, null));
3189 if (s is Method) {
3190 var m = (Method) s;
3191 m.set_attribute ("PrintfFormat", metadata.get_bool (ArgumentType.PRINTF_FORMAT));
3192 if (metadata.has_argument (ArgumentType.SENTINEL)) {
3193 m.set_attribute_string ("CCode", "sentinel", metadata.get_string (ArgumentType.SENTINEL));
3197 if (return_type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
3198 return_array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
3199 } else {
3200 if (return_no_array_length || return_array_null_terminated) {
3201 s.set_attribute_bool ("CCode", "array_length", !return_no_array_length);
3203 if (return_array_null_terminated) {
3204 s.set_attribute_bool ("CCode", "array_null_terminated", true);
3207 current.return_array_length_idx = return_array_length_idx;
3209 current.symbol = s;
3211 if (metadata.has_argument (ArgumentType.FINISH_NAME)) {
3212 s.set_attribute_string ("CCode", "finish_name", metadata.get_string (ArgumentType.FINISH_NAME));
3214 if (metadata.has_argument (ArgumentType.FINISH_INSTANCE)) {
3215 s.set_attribute_bool ("CCode", "finish_instance", metadata.get_bool (ArgumentType.FINISH_INSTANCE));
3218 int instance_idx = -2;
3219 if (element_name == "function" && symbol_type == "method") {
3220 if (metadata.has_argument (ArgumentType.INSTANCE_IDX)) {
3221 instance_idx = metadata.get_integer (ArgumentType.INSTANCE_IDX);
3222 if (instance_idx != 0) {
3223 s.set_attribute_double ("CCode", "instance_pos", instance_idx + 0.5);
3225 } else {
3226 Report.error (get_current_src (), "instance_idx required when converting function to method");
3230 var parameters = new ArrayList<ParameterInfo> ();
3231 current.array_length_parameters = new ArrayList<int> ();
3232 current.closure_parameters = new ArrayList<int> ();
3233 current.destroy_parameters = new ArrayList<int> ();
3234 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
3235 start_element ("parameters");
3236 next ();
3238 var current_parameter_idx = -1;
3239 while (current_token == MarkupTokenType.START_ELEMENT) {
3240 current_parameter_idx++;
3242 if (reader.name == "instance-parameter" &&
3243 !(symbol_type == "function" || symbol_type == "constructor")) {
3244 skip_element ();
3245 continue;
3248 if (instance_idx > -2 && instance_idx == current_parameter_idx) {
3249 skip_element ();
3250 continue;
3253 if (!push_metadata ()) {
3254 skip_element ();
3255 continue;
3258 int array_length_idx, closure_idx, destroy_idx;
3259 string scope;
3260 string default_param_name = null;
3261 Comment? param_comment;
3262 default_param_name = "arg%d".printf (parameters.size);
3263 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, out param_comment, default_param_name);
3264 if (array_length_idx != -1) {
3265 if (instance_idx > -2 && instance_idx < array_length_idx) {
3266 array_length_idx--;
3268 current.array_length_parameters.add (array_length_idx);
3270 if (closure_idx != -1) {
3271 if (instance_idx > -2 && instance_idx < closure_idx) {
3272 closure_idx--;
3274 if (current.closure_parameters.index_of (current_parameter_idx) < 0) {
3275 current.closure_parameters.add (closure_idx);
3278 if (destroy_idx != -1) {
3279 if (instance_idx > -2 && instance_idx < destroy_idx) {
3280 destroy_idx--;
3282 if (current.destroy_parameters.index_of (current_parameter_idx) < 0) {
3283 current.destroy_parameters.add (destroy_idx);
3286 if (param_comment != null) {
3287 if (comment == null) {
3288 comment = new GirComment (null, s.source_reference);
3289 s.comment = comment;
3292 comment.add_content_for_parameter ((param.ellipsis)? "..." : param.name, param_comment);
3295 var info = new ParameterInfo (param, array_length_idx, closure_idx, destroy_idx, scope == "async");
3297 if (s is Method && scope == "async") {
3298 var unresolved_type = param.variable_type as UnresolvedType;
3299 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
3300 // GAsync-style method
3301 ((Method) s).coroutine = true;
3302 info.keep = false;
3306 parameters.add (info);
3307 pop_metadata ();
3309 end_element ("parameters");
3311 current.parameters = parameters;
3313 for (int param_n = parameters.size - 1 ; param_n >= 0 ; param_n--) {
3314 ParameterInfo pi = parameters[param_n];
3315 if (!pi.param.ellipsis && pi.param.initializer == null) {
3316 string type_string = pi.param.variable_type.to_string ();
3317 if (type_string == "Gio.Cancellable?") {
3318 pi.param.initializer = new Vala.NullLiteral ();
3319 } else {
3320 break;
3325 pop_node ();
3326 end_element (element_name);
3329 void parse_method (string element_name) {
3330 parse_function (element_name);
3333 void parse_signal () {
3334 parse_function ("glib:signal");
3337 void parse_boxed (string element_name) {
3338 start_element (element_name);
3339 string name = reader.get_attribute ("name");
3340 if (name == null) {
3341 name = reader.get_attribute ("glib:name");
3343 push_node (element_get_name (name), true);
3345 Class cl;
3346 bool require_copy_free = false;
3347 if (current.new_symbol) {
3348 cl = new Class (current.name, current.source_reference);
3349 cl.is_compact = true;
3350 current.symbol = cl;
3351 } else {
3352 cl = (Class) current.symbol;
3355 set_type_id_ccode (cl);
3356 require_copy_free = cl.has_attribute_argument ("CCode", "type_id");
3358 cl.access = SymbolAccessibility.PUBLIC;
3359 cl.external = true;
3361 if (metadata.has_argument (ArgumentType.BASE_TYPE)) {
3362 cl.add_base_type (parse_type_from_string (metadata.get_string (ArgumentType.BASE_TYPE), true, metadata.get_source_reference (ArgumentType.BASE_TYPE)));
3365 next ();
3367 cl.comment = parse_symbol_doc ();
3369 Node? ref_method = null;
3370 Node? unref_method = null;
3372 while (current_token == MarkupTokenType.START_ELEMENT) {
3373 if (!push_metadata ()) {
3374 skip_element ();
3375 continue;
3378 if (reader.name == "field") {
3379 parse_field ();
3380 } else if (reader.name == "constructor") {
3381 parse_constructor ();
3382 } else if (reader.name == "method") {
3383 parse_method ("method");
3384 var cname = old_current.get_cname ();
3385 if (cname.has_suffix ("_ref") && (ref_method == null || old_current.name == "ref")) {
3386 ref_method = old_current;
3387 } else if (cname.has_suffix ("_unref") && (unref_method == null || old_current.name == "unref")) {
3388 unref_method = old_current;
3390 } else if (reader.name == "function") {
3391 skip_element ();
3392 } else if (reader.name == "union") {
3393 parse_union ();
3394 } else {
3395 // error
3396 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
3397 skip_element ();
3400 pop_metadata ();
3403 // Add ccode-attributes for ref/unref methodes if available
3404 // otherwise fallback to default g_boxed_copy/free
3405 if (cl.has_attribute_argument ("CCode", "ref_function") || cl.has_attribute_argument ("CCode", "unref_function")
3406 || cl.has_attribute_argument ("CCode", "copy_function") || cl.has_attribute_argument ("CCode", "free_function")) {
3407 //do nothing
3408 } else if (ref_method != null && unref_method != null) {
3409 cl.set_attribute_string ("CCode", "ref_function", ref_method.get_cname ());
3410 cl.set_attribute_string ("CCode", "unref_function", unref_method.get_cname ());
3411 } else if (require_copy_free) {
3412 cl.set_attribute_string ("CCode", "copy_function", "g_boxed_copy");
3413 cl.set_attribute_string ("CCode", "free_function", "g_boxed_free");
3416 pop_node ();
3417 end_element (element_name);
3420 void parse_union () {
3421 start_element ("union");
3423 string? element_name = element_get_name ();
3424 if (element_name == null) {
3425 next ();
3427 while (current_token == MarkupTokenType.START_ELEMENT) {
3428 if (!push_metadata ()) {
3429 skip_element ();
3430 continue;
3433 if (reader.name == "field") {
3434 parse_field ();
3435 } else {
3436 // error
3437 Report.error (get_current_src (), "unknown child element `%s' in `transparent union'".printf (reader.name));
3438 skip_element ();
3441 pop_metadata ();
3444 end_element ("union");
3445 return;
3448 push_node (element_name, true);
3450 Struct st;
3451 if (current.new_symbol) {
3452 st = new Struct (reader.get_attribute ("name"), current.source_reference);
3453 current.symbol = st;
3454 } else {
3455 st = (Struct) current.symbol;
3457 st.access = SymbolAccessibility.PUBLIC;
3458 st.external = true;
3460 next ();
3462 st.comment = parse_symbol_doc ();
3464 while (current_token == MarkupTokenType.START_ELEMENT) {
3465 if (!push_metadata ()) {
3466 skip_element ();
3467 continue;
3470 if (reader.name == "field") {
3471 parse_field ();
3472 } else if (reader.name == "constructor") {
3473 parse_constructor ();
3474 } else if (reader.name == "method") {
3475 parse_method ("method");
3476 } else if (reader.name == "function") {
3477 skip_element ();
3478 } else if (reader.name == "record") {
3479 parse_record ();
3480 } else {
3481 // error
3482 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
3483 skip_element ();
3486 pop_metadata ();
3489 pop_node ();
3490 end_element ("union");
3493 void parse_constant () {
3494 start_element ("constant");
3495 push_node (element_get_name (), false);
3497 next ();
3499 var comment = parse_symbol_doc ();
3501 var type = parse_type ();
3502 var c = new Constant (current.name, type, null, current.source_reference);
3503 current.symbol = c;
3504 c.access = SymbolAccessibility.PUBLIC;
3505 c.comment = comment;
3506 c.external = true;
3508 pop_node ();
3509 end_element ("constant");
3512 /* Reporting */
3513 void report_unused_metadata (Metadata metadata) {
3514 if (metadata == Metadata.empty) {
3515 return;
3518 if (metadata.args.size == 0 && metadata.children.size == 0) {
3519 Report.warning (metadata.source_reference, "empty metadata");
3520 return;
3523 foreach (var arg_type in metadata.args.get_keys ()) {
3524 var arg = metadata.args[arg_type];
3525 if (!arg.used) {
3526 // if metadata is used and argument is not, then it's a unexpected argument
3527 Report.warning (arg.source_reference, "argument never used");
3531 foreach (var child in metadata.children) {
3532 if (!child.used) {
3533 Report.warning (child.source_reference, "metadata never used");
3534 } else {
3535 report_unused_metadata (child);
3540 /* Post-parsing */
3542 void resolve_gir_symbols () {
3543 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
3544 foreach (var map_from in unresolved_gir_symbols) {
3545 while (map_from != null) {
3546 var map_to = unresolved_symbols_map[map_from];
3547 if (map_to != null) {
3548 // remap the original symbol to match the target
3549 map_from.inner = null;
3550 map_from.name = map_to.name;
3551 if (map_to is UnresolvedSymbol) {
3552 var umap_to = (UnresolvedSymbol) map_to;
3553 while (umap_to.inner != null) {
3554 umap_to = umap_to.inner;
3555 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
3556 map_from = map_from.inner;
3558 } else {
3559 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
3560 map_to = map_to.parent_symbol;
3561 map_from.inner = new UnresolvedSymbol (null, map_to.name);
3562 map_from = map_from.inner;
3565 break;
3567 map_from = map_from.inner;
3572 void create_new_namespaces () {
3573 foreach (var node in Node.new_namespaces) {
3574 if (node.symbol == null) {
3575 node.symbol = new Namespace (node.name, node.source_reference);
3580 void resolve_type_arguments () {
3581 // box structs in type arguments
3582 foreach (var element_type in unresolved_type_arguments) {
3583 TypeSymbol sym = null;
3584 if (element_type is UnresolvedType) {
3585 sym = (TypeSymbol) resolve_symbol (root, ((UnresolvedType) element_type).unresolved_symbol);
3586 } else if (element_type.data_type != null) {
3587 sym = element_type.data_type;
3589 var st = sym as Struct;
3590 if (st != null && !st.is_integer_type () && !st.is_floating_type ()) {
3591 element_type.nullable = true;
3596 void process_interface (Node iface_node) {
3597 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
3598 ensure we have at least one instantiable prerequisite */
3599 Interface iface = (Interface) iface_node.symbol;
3600 bool has_instantiable_prereq = false;
3601 foreach (DataType prereq in iface.get_prerequisites ()) {
3602 Symbol sym = null;
3603 if (prereq is UnresolvedType) {
3604 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
3605 sym = resolve_symbol (iface_node.parent, unresolved_symbol);
3606 } else {
3607 sym = prereq.data_type;
3609 if (sym is Class) {
3610 has_instantiable_prereq = true;
3611 break;
3615 if (!has_instantiable_prereq) {
3616 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
3620 void process_alias (Node alias) {
3621 /* this is unfortunate because <alias> tag has no type information, thus we have
3622 to guess it from the base type */
3623 DataType base_type = null;
3624 Symbol type_sym = null;
3625 Node base_node = null;
3626 bool simple_type = false;
3627 if (alias.base_type is UnresolvedType) {
3628 base_type = alias.base_type;
3629 base_node = resolve_node (alias.parent, ((UnresolvedType) base_type).unresolved_symbol);
3630 if (base_node != null) {
3631 type_sym = base_node.symbol;
3633 } else if (alias.base_type is PointerType && ((PointerType) alias.base_type).base_type is VoidType) {
3634 // gpointer, if it's a struct make it a simpletype
3635 simple_type = true;
3636 } else {
3637 base_type = alias.base_type;
3638 type_sym = base_type.data_type;
3639 if (type_sym != null) {
3640 base_node = resolve_node (alias.parent, parse_symbol_from_string (type_sym.get_full_name (), alias.source_reference));
3644 if (type_sym is Struct && ((Struct) type_sym).is_simple_type ()) {
3645 simple_type = true;
3648 if (base_type == null || type_sym == null || type_sym is Struct) {
3649 var st = new Struct (alias.name, alias.source_reference);
3650 st.access = SymbolAccessibility.PUBLIC;
3651 if (base_type != null) {
3652 // threat target="none" as a new struct
3653 st.base_type = base_type;
3655 st.comment = alias.comment;
3656 st.external = true;
3657 st.set_simple_type (simple_type);
3658 alias.symbol = st;
3659 } else if (type_sym is Class) {
3660 var cl = new Class (alias.name, alias.source_reference);
3661 cl.access = SymbolAccessibility.PUBLIC;
3662 if (base_type != null) {
3663 cl.add_base_type (base_type);
3665 cl.comment = alias.comment;
3666 cl.external = true;
3667 cl.is_compact = ((Class) type_sym).is_compact;
3668 alias.symbol = cl;
3669 } else if (type_sym is Interface) {
3670 // this is not a correct alias, but what can we do otherwise?
3671 var iface = new Interface (alias.name, alias.source_reference);
3672 iface.access = SymbolAccessibility.PUBLIC;
3673 if (base_type != null) {
3674 iface.add_prerequisite (base_type);
3676 iface.comment = alias.comment;
3677 iface.external = true;
3678 alias.symbol = iface;
3679 } else if (type_sym is Delegate) {
3680 var orig = (Delegate) type_sym;
3681 if (base_node != null) {
3682 base_node.process (this);
3683 orig = (Delegate) base_node.symbol;
3686 var deleg = new Delegate (alias.name, orig.return_type.copy (), alias.source_reference);
3687 deleg.access = orig.access;
3689 foreach (var param in orig.get_parameters ()) {
3690 deleg.add_parameter (param.copy ());
3693 foreach (var error_type in orig.get_error_types ()) {
3694 deleg.add_error_type (error_type.copy ());
3697 foreach (var attribute in orig.attributes) {
3698 deleg.attributes.append (attribute);
3701 deleg.external = true;
3703 alias.symbol = deleg;
3706 // inherit atributes, like type_id
3707 if (type_sym is Class || (type_sym is Struct && !simple_type)) {
3708 if (type_sym.has_attribute_argument ("CCode", "has_type_id")) {
3709 alias.symbol.set_attribute_bool ("CCode", "has_type_id", type_sym.get_attribute_bool ("CCode", "has_type_id"));
3710 } else if (type_sym.has_attribute_argument ("CCode", "type_id")) {
3711 alias.symbol.set_attribute_string ("CCode", "type_id", type_sym.get_attribute_string ("CCode", "type_id"));
3716 void process_callable (Node node) {
3717 if (node.element_type == "alias" && node.symbol is Delegate) {
3718 // processed in parse_alias
3719 return;
3722 var s = node.symbol;
3723 List<ParameterInfo> parameters = node.parameters;
3725 DataType return_type = null;
3726 if (s is Callable) {
3727 return_type = ((Callable) s).return_type;
3730 if (return_type is ArrayType && node.return_array_length_idx >= 0) {
3731 if (node.return_array_length_idx >= parameters.size) {
3732 Report.error (return_type.source_reference, "invalid array length index");
3733 } else {
3734 parameters[node.return_array_length_idx].keep = false;
3735 node.array_length_parameters.add (node.return_array_length_idx);
3737 } else if (return_type is VoidType && parameters.size > 0) {
3738 int n_out_parameters = 0;
3739 foreach (var info in parameters) {
3740 if (info.param.direction == ParameterDirection.OUT) {
3741 n_out_parameters++;
3745 if (n_out_parameters == 1) {
3746 ParameterInfo last_param = parameters[parameters.size-1];
3747 if (last_param.param.direction == ParameterDirection.OUT) {
3748 // use last out real-non-null-struct parameter as return type
3749 if (last_param.param.variable_type is UnresolvedType) {
3750 var st = resolve_symbol (node.parent, ((UnresolvedType) last_param.param.variable_type).unresolved_symbol) as Struct;
3751 if (st != null && !st.is_simple_type () && !last_param.param.variable_type.nullable) {
3752 if (!node.metadata.get_bool (ArgumentType.RETURN_VOID, false)) {
3753 last_param.keep = false;
3754 return_type = last_param.param.variable_type.copy ();
3760 } else {
3761 if (return_type is UnresolvedType && !return_type.nullable) {
3762 var st = resolve_symbol (node.parent, ((UnresolvedType) return_type).unresolved_symbol) as Struct;
3763 if (st != null) {
3764 bool is_simple_type = false;
3765 Struct? base_st = st;
3767 while (base_st != null) {
3768 if (base_st.is_simple_type ()) {
3769 is_simple_type = true;
3770 break;
3773 if (base_st.base_type is UnresolvedType) {
3774 base_st = resolve_symbol (node.parent, ((UnresolvedType) base_st.base_type).unresolved_symbol) as Struct;
3775 } else {
3776 base_st = base_st.base_struct;
3780 if (!is_simple_type) {
3781 return_type.nullable = true;
3787 // Do not mark out-parameters as nullable if they are simple-types,
3788 // since it would result in a boxed-type in vala
3789 foreach (ParameterInfo info in parameters) {
3790 var type = info.param.variable_type;
3791 if (info.param.direction == ParameterDirection.OUT && type.nullable) {
3792 Struct? st = null;
3793 if (type is UnresolvedType) {
3794 st = resolve_symbol (node.parent, ((UnresolvedType) type).unresolved_symbol) as Struct;
3795 } else if (type is ValueType) {
3796 st = type.data_type as Struct;
3798 if (st != null && st.is_simple_type ()) {
3799 type.nullable = false;
3804 if (parameters.size > 1) {
3805 ParameterInfo last_param = parameters[parameters.size-1];
3806 if (last_param.param.ellipsis) {
3807 var first_vararg_param = parameters[parameters.size-2];
3808 if (first_vararg_param.param.name.has_prefix ("first_")) {
3809 first_vararg_param.keep = false;
3814 int i = 0, j=1;
3816 int last = -1;
3817 foreach (ParameterInfo info in parameters) {
3818 if (s is Delegate && info.closure_idx == i) {
3819 var d = (Delegate) s;
3820 d.has_target = true;
3821 d.set_attribute_double ("CCode", "instance_pos", j - 0.1);
3822 info.keep = false;
3823 } else if (info.keep
3824 && !node.array_length_parameters.contains (i)
3825 && !node.closure_parameters.contains (i)
3826 && !node.destroy_parameters.contains (i)) {
3827 info.vala_idx = (float) j;
3828 info.keep = true;
3830 /* interpolate for vala_idx between this and last*/
3831 float last_idx = 0.0F;
3832 if (last != -1) {
3833 last_idx = parameters[last].vala_idx;
3835 for (int k=last+1; k < i; k++) {
3836 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
3838 last = i;
3839 j++;
3840 } else {
3841 info.keep = false;
3842 // make sure that vala_idx is always set
3843 // the above if branch does not set vala_idx for
3844 // hidden parameters at the end of the parameter list
3845 info.vala_idx = (j - 1) + (i - last) * 0.1F;
3847 i++;
3850 foreach (ParameterInfo info in parameters) {
3851 if (!info.keep) {
3852 continue;
3855 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
3856 so do it first*/
3857 if (s is Callable) {
3858 ((Callable) s).add_parameter (info.param);
3861 if (info.array_length_idx != -1) {
3862 if ((info.array_length_idx) >= parameters.size) {
3863 Report.error (info.param.source_reference, "invalid array_length index");
3864 continue;
3866 set_array_ccode (info.param, parameters[info.array_length_idx]);
3869 if (info.closure_idx != -1) {
3870 if ((info.closure_idx) >= parameters.size) {
3871 Report.error (info.param.source_reference, "invalid closure index");
3872 continue;
3874 if ("%g".printf (parameters[info.closure_idx].vala_idx) != "%g".printf (info.vala_idx + 0.1)) {
3875 info.param.set_attribute_double ("CCode", "delegate_target_pos", parameters[info.closure_idx].vala_idx);
3878 if (info.destroy_idx != -1) {
3879 if (info.destroy_idx >= parameters.size) {
3880 Report.error (info.param.source_reference, "invalid destroy index");
3881 continue;
3883 if ("%g".printf (parameters[info.destroy_idx].vala_idx) != "%g".printf (info.vala_idx + 0.2)) {
3884 info.param.set_attribute_double ("CCode", "destroy_notify_pos", parameters[info.destroy_idx].vala_idx);
3888 if (info.is_async) {
3889 var resolved_type = info.param.variable_type;
3890 if (resolved_type is UnresolvedType) {
3891 var resolved_symbol = resolve_symbol (node.parent, ((UnresolvedType) resolved_type).unresolved_symbol);
3892 if (resolved_symbol is Delegate) {
3893 resolved_type = new DelegateType ((Delegate) resolved_symbol);
3897 if (resolved_type is DelegateType) {
3898 var d = ((DelegateType) resolved_type).delegate_symbol;
3899 if (!(d.name == "DestroyNotify" && d.parent_symbol.name == "GLib")) {
3900 info.param.set_attribute_string ("CCode", "scope", "async");
3901 info.param.variable_type.value_owned = (info.closure_idx != -1 && info.destroy_idx != -1);
3904 } else {
3905 var resolved_type = info.param.variable_type;
3906 if (resolved_type is DelegateType) {
3907 info.param.variable_type.value_owned = (info.closure_idx != -1 && info.destroy_idx != -1);
3912 if (return_type is ArrayType && node.return_array_length_idx >= 0) {
3913 set_array_ccode (s, parameters[node.return_array_length_idx]);
3916 if (s is Callable) {
3917 ((Callable) s).return_type = return_type;
3921 void find_parent (string cname, Node current, ref Node best, ref int match) {
3922 var old_best = best;
3923 if (current.symbol is Namespace) {
3924 foreach (var child in current.members) {
3925 // symbol is null only for aliases that aren't yet processed
3926 if ((child.symbol == null || is_container (child.symbol)) && cname.has_prefix (child.get_lower_case_cprefix ())) {
3927 find_parent (cname, child, ref best, ref match);
3931 if (best != old_best) {
3932 // child is better
3933 return;
3936 var current_match = current.get_lower_case_cprefix().length;
3937 if (current_match > match) {
3938 match = current_match;
3939 best = current;
3943 bool same_gir (Symbol gir_component, Symbol sym) {
3944 var gir_name = gir_component.source_reference.file.gir_namespace;
3945 var gir_version = gir_component.source_reference.file.gir_version;
3946 return "%s-%s".printf (gir_name, gir_version) in sym.source_reference.file.filename;
3949 void process_namespace_method (Node ns, Node node) {
3950 /* transform static methods into instance methods if possible.
3951 In most of cases this is a .gir fault we are going to fix */
3953 var ns_cprefix = ns.get_lower_case_cprefix ();
3954 var method = (Method) node.symbol;
3955 var cname = node.get_cname ();
3957 Parameter first_param = null;
3958 if (node.parameters.size > 0) {
3959 first_param = node.parameters[0].param;
3961 if (first_param != null && first_param.direction == ParameterDirection.IN && first_param.variable_type is UnresolvedType) {
3962 // check if it's a missed instance method (often happens for structs)
3963 var sym = ((UnresolvedType) first_param.variable_type).unresolved_symbol;
3964 var parent = resolve_node (ns, sym);
3965 if (parent != null && same_gir (method, parent.symbol) && parent.parent == ns && is_container (parent.symbol) && cname.has_prefix (parent.get_lower_case_cprefix ())) {
3966 // instance method
3967 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
3968 if (parent.lookup (new_name) == null) {
3969 ns.remove_member (node);
3970 node.name = new_name;
3971 node.parameters.remove_at (0);
3972 method.name = new_name;
3973 method.binding = MemberBinding.INSTANCE;
3974 parent.add_member (node);
3976 return;
3980 int match = 0;
3981 Node parent = ns;
3982 find_parent (cname, ns, ref parent, ref match);
3983 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
3984 if (same_gir (method, parent.symbol) && parent.lookup (new_name) == null) {
3985 ns.remove_member (node);
3986 node.name = new_name;
3987 method.name = new_name;
3988 parent.add_member (node);
3992 void process_virtual_method_field (Node node, Delegate d, UnresolvedSymbol gtype_struct_for) {
3993 var gtype_node = resolve_node (node.parent, gtype_struct_for);
3994 if (gtype_node == null || !(gtype_node.symbol is ObjectTypeSymbol)) {
3995 Report.error (gtype_struct_for.source_reference, "Unknown symbol `%s' for virtual method field `%s'".printf (gtype_struct_for.to_string (), node.to_string ()));
3997 var nodes = gtype_node.lookup_all (d.name);
3998 if (nodes == null) {
3999 return;
4001 foreach (var n in nodes) {
4002 if (node != n) {
4003 n.process (this);
4006 foreach (var n in nodes) {
4007 if (n.merged) {
4008 continue;
4010 var sym = n.symbol;
4011 if (sym is Signal) {
4012 var sig = (Signal) sym;
4013 sig.is_virtual = true;
4014 assume_parameter_names (sig, d, true);
4015 } else if (sym is Property) {
4016 var prop = (Property) sym;
4017 prop.is_virtual = true;
4022 void process_async_method (Node node) {
4023 var m = (Method) node.symbol;
4024 string finish_method_base;
4025 if (m.name == null) {
4026 assert (m is CreationMethod);
4027 finish_method_base = "new";
4028 } else if (m.name.has_suffix ("_async")) {
4029 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
4030 } else {
4031 finish_method_base = m.name;
4033 var finish_method_node = node.parent.lookup (finish_method_base + "_finish");
4035 // check if the method is using non-standard finish method name
4036 if (finish_method_node == null) {
4037 var method_cname = node.get_finish_cname ();
4038 foreach (var n in node.parent.members) {
4039 if (n.symbol is Method && n.get_cname () == method_cname) {
4040 finish_method_node = n;
4041 break;
4046 Method method = m;
4048 if (finish_method_node != null && finish_method_node.symbol is Method) {
4049 finish_method_node.process (this);
4050 var finish_method = (Method) finish_method_node.symbol;
4051 if (finish_method is CreationMethod) {
4052 method = new CreationMethod (((CreationMethod) finish_method).class_name, null, m.source_reference);
4053 method.access = m.access;
4054 method.binding = m.binding;
4055 method.external = true;
4056 method.coroutine = true;
4057 method.has_construct_function = finish_method.has_construct_function;
4059 // cannot use List.copy()
4060 // as it returns a list of unowned elements
4061 foreach (Attribute a in m.attributes) {
4062 method.attributes.append (a);
4065 method.set_attribute_string ("CCode", "cname", node.get_cname ());
4066 if (finish_method_base == "new") {
4067 method.name = null;
4068 } else if (finish_method_base.has_prefix ("new_")) {
4069 method.name = m.name.substring ("new_".length);
4071 foreach (var param in m.get_parameters ()) {
4072 method.add_parameter (param);
4074 node.symbol = method;
4075 } else {
4076 method.return_type = finish_method.return_type.copy ();
4077 var a = finish_method.get_attribute ("CCode");
4078 if (a != null && a.has_argument ("array_length")) {
4079 method.set_attribute_bool ("CCode", "array_length", a.get_bool ("array_length"));
4081 if (a != null && a.has_argument ("array_null_terminated")) {
4082 method.set_attribute_bool ("CCode", "array_null_terminated", a.get_bool ("array_null_terminated"));
4086 foreach (var param in finish_method.get_parameters ()) {
4087 if (param.direction == ParameterDirection.OUT) {
4088 var async_param = param.copy ();
4089 if (method.scope.lookup (param.name) != null) {
4090 // parameter name conflict
4091 async_param.name += "_out";
4093 method.add_parameter (async_param);
4097 foreach (DataType error_type in finish_method.get_error_types ()) {
4098 method.add_error_type (error_type.copy ());
4100 finish_method_node.processed = true;
4101 finish_method_node.merged = true;
4105 /* Hash and equal functions */
4107 static uint unresolved_symbol_hash (UnresolvedSymbol? sym) {
4108 var builder = new StringBuilder ();
4109 while (sym != null) {
4110 builder.append (sym.name);
4111 sym = sym.inner;
4113 return builder.str.hash ();
4116 static bool unresolved_symbol_equal (UnresolvedSymbol? sym1, UnresolvedSymbol? sym2) {
4117 while (sym1 != sym2) {
4118 if (sym1 == null || sym2 == null) {
4119 return false;
4121 if (sym1.name != sym2.name) {
4122 return false;
4124 sym1 = sym1.inner;
4125 sym2 = sym2.inner;
4127 return true;
4130 /* Helper methods */
4132 Node? base_interface_property (Node prop_node) {
4133 var cl = prop_node.parent.symbol as Class;
4134 if (cl == null) {
4135 return null;
4138 foreach (DataType type in cl.get_base_types ()) {
4139 if (!(type is UnresolvedType)) {
4140 continue;
4143 var base_node = resolve_node (prop_node.parent, ((UnresolvedType) type).unresolved_symbol);
4144 if (base_node != null && base_node.symbol is Interface) {
4145 var base_prop_node = base_node.lookup (prop_node.name);
4146 if (base_prop_node != null && base_prop_node.symbol is Property) {
4147 var base_property = (Property) base_prop_node.symbol;
4148 if (base_property.is_abstract || base_property.is_virtual) {
4149 // found
4150 return base_prop_node;
4156 return null;