manual: Update from wiki.gnome.org
[vala-gnome.git] / vala / valagirparser.vala
blob24dc8dabe9dddaae5d11a90f5658838fb4ef7fb1
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;
91 public static ArgumentType? from_string (string name) {
92 var enum_class = (EnumClass) typeof(ArgumentType).class_ref ();
93 var nick = name.replace ("_", "-");
94 unowned GLib.EnumValue? enum_value = enum_class.get_value_by_nick (nick);
95 if (enum_value != null) {
96 ArgumentType value = (ArgumentType) enum_value.value;
97 return value;
99 return null;
103 class Argument {
104 public Expression expression;
105 public SourceReference source_reference;
107 public bool used = false;
109 public Argument (Expression expression, SourceReference? source_reference = null) {
110 this.expression = expression;
111 this.source_reference = source_reference;
115 class MetadataSet : Metadata {
116 public MetadataSet (string? selector = null) {
117 base ("", selector);
120 public void add_sibling (Metadata metadata) {
121 foreach (var child in metadata.children) {
122 add_child (child);
124 // merge arguments and take precedence
125 foreach (var key in metadata.args.get_keys ()) {
126 args[key] = metadata.args[key];
131 class Metadata {
132 private static Metadata _empty = null;
133 public static Metadata empty {
134 get {
135 if (_empty == null) {
136 _empty = new Metadata ("");
138 return _empty;
142 public PatternSpec pattern_spec;
143 public string? selector;
144 public SourceReference source_reference;
146 public bool used = false;
147 public Vala.Map<ArgumentType,Argument> args = new HashMap<ArgumentType,Argument> ();
148 public ArrayList<Metadata> children = new ArrayList<Metadata> ();
150 public Metadata (string pattern, string? selector = null, SourceReference? source_reference = null) {
151 this.pattern_spec = new PatternSpec (pattern);
152 this.selector = selector;
153 this.source_reference = source_reference;
156 public void add_child (Metadata metadata) {
157 children.add (metadata);
160 public Metadata match_child (string name, string? selector = null) {
161 var result = Metadata.empty;
162 foreach (var metadata in children) {
163 if ((selector == null || metadata.selector == null || metadata.selector == selector) && metadata.pattern_spec.match_string (name)) {
164 metadata.used = true;
165 if (result == Metadata.empty) {
166 // first match
167 result = metadata;
168 } else {
169 var ms = result as MetadataSet;
170 if (ms == null) {
171 // second match
172 ms = new MetadataSet (selector);
173 ms.add_sibling (result);
175 ms.add_sibling (metadata);
176 result = ms;
180 return result;
183 public void add_argument (ArgumentType key, Argument value) {
184 args.set (key, value);
187 public bool has_argument (ArgumentType key) {
188 return args.contains (key);
191 public Expression? get_expression (ArgumentType arg) {
192 var val = args.get (arg);
193 if (val != null) {
194 val.used = true;
195 return val.expression;
197 return null;
200 public string? get_string (ArgumentType arg) {
201 var lit = get_expression (arg) as StringLiteral;
202 if (lit != null) {
203 return lit.eval ();
205 return null;
208 public int get_integer (ArgumentType arg) {
209 var unary = get_expression (arg) as UnaryExpression;
210 if (unary != null && unary.operator == UnaryOperator.MINUS) {
211 var lit = unary.inner as IntegerLiteral;
212 if (lit != null) {
213 return -int.parse (lit.value);
215 } else {
216 var lit = get_expression (arg) as IntegerLiteral;
217 if (lit != null) {
218 return int.parse (lit.value);
222 return 0;
225 public bool get_bool (ArgumentType arg, bool default_value = false) {
226 var lit = get_expression (arg) as BooleanLiteral;
227 if (lit != null) {
228 return lit.value;
230 return default_value;
233 public SourceReference? get_source_reference (ArgumentType arg) {
234 var val = args.get (arg);
235 if (val != null) {
236 return val.source_reference;
238 return null;
242 class MetadataParser {
244 * Grammar:
245 * metadata ::= [ rule [ '\n' relativerule ]* ]
246 * rule ::= pattern ' ' [ args ]
247 * relativerule ::= '.' rule
248 * pattern ::= glob [ '#' selector ] [ '.' pattern ]
250 private Metadata tree = new Metadata ("");
251 private Scanner scanner;
252 private SourceLocation begin;
253 private SourceLocation end;
254 private SourceLocation old_end;
255 private TokenType current;
256 private Metadata parent_metadata;
258 public MetadataParser () {
259 tree.used = true;
262 SourceReference get_current_src () {
263 return new SourceReference (scanner.source_file, begin, end);
266 SourceReference get_src (SourceLocation begin, SourceLocation? end = null) {
267 var e = this.end;
268 if (end != null) {
269 e = end;
271 return new SourceReference (scanner.source_file, begin, e);
274 public Metadata parse_metadata (SourceFile metadata_file) {
275 scanner = new Scanner (metadata_file);
276 next ();
277 while (current != TokenType.EOF) {
278 if (!parse_rule ()) {
279 return Metadata.empty;
282 return tree;
285 TokenType next () {
286 old_end = end;
287 current = scanner.read_token (out begin, out end);
288 return current;
291 bool has_space () {
292 return old_end.pos != begin.pos;
295 bool has_newline () {
296 return old_end.line != begin.line;
299 string get_string (SourceLocation? begin = null, SourceLocation? end = null) {
300 var b = this.begin;
301 var e = this.end;
302 if (begin != null) {
303 b = begin;
305 if (end != null) {
306 e = end;
308 return ((string) b.pos).substring (0, (int) (e.pos - b.pos));
311 string? parse_identifier (bool is_glob) {
312 var begin = this.begin;
314 if (current == TokenType.DOT || current == TokenType.HASH) {
315 if (is_glob) {
316 Report.error (get_src (begin), "expected glob-style pattern");
317 } else {
318 Report.error (get_src (begin), "expected identifier");
320 return null;
323 if (is_glob) {
324 while (current != TokenType.EOF && current != TokenType.DOT && current != TokenType.HASH) {
325 next ();
326 if (has_space ()) {
327 break;
330 } else {
331 next ();
334 return get_string (begin, old_end);
337 string? parse_selector () {
338 if (current != TokenType.HASH || has_space ()) {
339 return null;
341 next ();
343 return parse_identifier (false);
346 Metadata? parse_pattern () {
347 Metadata metadata;
348 bool is_relative = false;
349 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
350 // absolute pattern
351 parent_metadata = tree;
352 } else {
353 // relative pattern
354 if (current != TokenType.DOT) {
355 Report.error (get_current_src (), "expected pattern or `.', got %s".printf (current.to_string ()));
356 return null;
358 next ();
359 is_relative = true;
362 if (parent_metadata == null) {
363 Report.error (get_current_src (), "cannot determinate parent metadata");
364 return null;
367 SourceLocation begin = this.begin;
368 var pattern = parse_identifier (true);
369 if (pattern == null) {
370 return null;
372 metadata = new Metadata (pattern, parse_selector (), get_src (begin));
373 parent_metadata.add_child (metadata);
375 while (current != TokenType.EOF && !has_space ()) {
376 if (current != TokenType.DOT) {
377 Report.error (get_current_src (), "expected `.' got %s".printf (current.to_string ()));
378 break;
380 next ();
382 begin = this.begin;
383 pattern = parse_identifier (true);
384 if (pattern == null) {
385 return null;
387 var child = new Metadata (pattern, parse_selector (), get_src (begin, old_end));
388 metadata.add_child (child);
389 metadata = child;
391 if (!is_relative) {
392 parent_metadata = metadata;
395 return metadata;
398 Expression? parse_expression () {
399 var begin = this.begin;
400 var src = get_current_src ();
401 Expression expr = null;
402 switch (current) {
403 case TokenType.NULL:
404 expr = new NullLiteral (src);
405 break;
406 case TokenType.TRUE:
407 expr = new BooleanLiteral (true, src);
408 break;
409 case TokenType.FALSE:
410 expr = new BooleanLiteral (false, src);
411 break;
412 case TokenType.MINUS:
413 next ();
414 var inner = parse_expression ();
415 if (inner == null) {
416 Report.error (src, "expected expression after `-', got %s".printf (current.to_string ()));
417 } else {
418 expr = new UnaryExpression (UnaryOperator.MINUS, inner, get_src (begin));
420 return expr;
421 case TokenType.INTEGER_LITERAL:
422 expr = new IntegerLiteral (get_string (), src);
423 break;
424 case TokenType.REAL_LITERAL:
425 expr = new RealLiteral (get_string (), src);
426 break;
427 case TokenType.STRING_LITERAL:
428 expr = new StringLiteral (get_string (), src);
429 break;
430 case TokenType.IDENTIFIER:
431 expr = new MemberAccess (null, get_string (), src);
432 while (next () == TokenType.DOT) {
433 if (next () != TokenType.IDENTIFIER) {
434 Report.error (get_current_src (), "expected identifier got %s".printf (current.to_string ()));
435 break;
437 expr = new MemberAccess (expr, get_string (), get_current_src ());
439 return expr;
440 case TokenType.OPEN_PARENS:
441 // empty tuple => no expression
442 if (next () != TokenType.CLOSE_PARENS) {
443 Report.error (get_current_src (), "expected `)', got %s".printf (current.to_string ()));
444 break;
446 expr = new Tuple (src);
447 break;
448 default:
449 Report.error (src, "expected literal or symbol got %s".printf (current.to_string ()));
450 break;
452 next ();
453 return expr;
456 bool parse_args (Metadata metadata) {
457 while (current != TokenType.EOF && has_space () && !has_newline ()) {
458 SourceLocation begin = this.begin;
459 var id = parse_identifier (false);
460 if (id == null) {
461 return false;
463 var arg_type = ArgumentType.from_string (id);
464 if (arg_type == null) {
465 Report.warning (get_src (begin, old_end), "unknown argument `%s'".printf (id));
466 continue;
469 if (current != TokenType.ASSIGN) {
470 // threat as `true'
471 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, get_src (begin)), get_src (begin)));
472 continue;
474 next ();
476 Expression expr = parse_expression ();
477 if (expr == null) {
478 return false;
480 metadata.add_argument (arg_type, new Argument (expr, get_src (begin)));
483 return true;
486 bool parse_rule () {
487 var old_end = end;
488 var metadata = parse_pattern ();
489 if (metadata == null) {
490 return false;
493 if (current == TokenType.EOF || old_end.line != end.line) {
494 // eof or new rule
495 return true;
497 return parse_args (metadata);
502 * GIR parser
505 class Node {
506 public static ArrayList<Node> new_namespaces = new ArrayList<Node> ();
508 public weak Node parent;
509 public string element_type;
510 public string name;
511 public Map<string,string> girdata = null;
512 public Metadata metadata = Metadata.empty;
513 public SourceReference source_reference = null;
514 public ArrayList<Node> members = new ArrayList<Node> (); // guarantees fields order
515 public HashMap<string, ArrayList<Node>> scope = new HashMap<string, ArrayList<Node>> (str_hash, str_equal);
517 public GirComment comment;
518 public Symbol symbol;
519 public bool new_symbol;
520 public bool merged;
521 public bool processed;
523 // function-specific
524 public int return_array_length_idx = -1;
525 public List<ParameterInfo> parameters;
526 public ArrayList<int> array_length_parameters;
527 public ArrayList<int> closure_parameters;
528 public ArrayList<int> destroy_parameters;
529 // record-specific
530 public UnresolvedSymbol gtype_struct_for;
531 // alias-specific
532 public DataType base_type;
533 // struct-specific
534 public int array_length_idx = -1;
536 public bool deprecated = false;
537 public uint64 deprecated_version = 0;
538 public string? deprecated_since = null;
539 public string? deprecated_replacement = null;
541 public Node (string? name) {
542 this.name = name;
545 public void add_member (Node node) {
546 var nodes = scope[node.name];
547 if (nodes == null) {
548 nodes = new ArrayList<Node> ();
549 scope[node.name] = nodes;
551 nodes.add (node);
552 members.add (node);
553 node.parent = this;
556 public void remove_member (Node node) {
557 var nodes = scope[node.name];
558 nodes.remove (node);
559 if (nodes.size == 0) {
560 scope.remove (node.name);
562 members.remove (node);
563 node.parent = null;
566 public Node? lookup (string name, bool create_namespace = false, SourceReference? source_reference = null) {
567 var nodes = scope[name];
568 Node node = null;
569 if (nodes != null) {
570 node = nodes[0];
572 if (node == null) {
573 Symbol sym = null;
574 if (symbol != null) {
575 sym = symbol.scope.lookup (name);
577 if (sym != null || create_namespace) {
578 node = new Node (name);
579 node.symbol = sym;
580 node.new_symbol = node.symbol == null;
581 node.source_reference = source_reference;
582 add_member (node);
584 if (sym == null) {
585 new_namespaces.add (node);
589 return node;
592 public ArrayList<Node>? lookup_all (string name) {
593 return scope[name];
596 public UnresolvedSymbol get_unresolved_symbol () {
597 if (parent.name == null) {
598 return new UnresolvedSymbol (null, name);
599 } else {
600 return new UnresolvedSymbol (parent.get_unresolved_symbol (), name);
604 public string get_full_name () {
605 if (parent == null) {
606 return name;
609 if (name == null) {
610 return parent.get_full_name ();
613 if (parent.get_full_name () == null) {
614 return name;
617 return "%s.%s".printf (parent.get_full_name (), name);
620 public string get_default_gir_name () {
621 GLib.StringBuilder default_name = new GLib.StringBuilder ();
623 for (unowned Node? node = this ; node != null ; node = node.parent) {
624 if (node.symbol is Namespace) {
625 if (node.symbol.get_attribute_string ("CCode", "gir_namespace") != null) {
626 break;
630 default_name.prepend (node.name);
633 return default_name.str;
636 public string get_gir_name () {
637 var gir_name = girdata["name"];
638 if (gir_name == null) {
639 gir_name = girdata["glib:name"];
641 return gir_name;
644 public string get_lower_case_cprefix () {
645 if (name == null) {
646 return "";
649 var prefix = symbol.get_attribute_string ("CCode", "lower_case_cprefix");
650 if (prefix == null && (symbol is ObjectTypeSymbol || symbol is Struct)) {
651 if (metadata.has_argument (ArgumentType.LOWER_CASE_CPREFIX)) {
652 prefix = metadata.get_string (ArgumentType.LOWER_CASE_CPREFIX);
653 } else if (metadata.has_argument (ArgumentType.CPREFIX)) {
654 prefix = metadata.get_string (ArgumentType.CPREFIX);
655 } else {
656 prefix = symbol.get_attribute_string ("CCode", "cprefix");
660 if (prefix == null && girdata != null && (girdata.contains ("c:symbol-prefix") || girdata.contains("c:symbol-prefixes"))) {
661 /* Use the prefix in the gir. We look up prefixes up to the root.
662 If some node does not have girdata, we ignore it as i might be
663 a namespace created due to reparenting. */
664 unowned Node cur = this;
665 do {
666 if (cur.girdata != null) {
667 var p = cur.girdata["c:symbol-prefix"];
668 if (p == null) {
669 p = cur.girdata["c:symbol-prefixes"];
670 if (p != null) {
671 var idx = p.index_of (",");
672 if (idx >= 0) {
673 p = p.substring (0, idx);
678 if (p != null) {
679 prefix = "%s_%s".printf (p, prefix ?? "");
683 cur = cur.parent;
684 } while (cur != null);
687 if (prefix == null) {
688 prefix = get_default_lower_case_cprefix ();
690 return prefix;
693 public string get_default_lower_case_cprefix () {
694 return "%s%s_".printf (parent.get_lower_case_cprefix (), get_lower_case_csuffix ());
697 public string get_lower_case_csuffix () {
698 var suffix = symbol.get_attribute_string ("CCode", "lower_case_csuffix");
700 // we can't rely on gir suffix if metadata changed the name
701 if (suffix == null && girdata != null && girdata["c:symbol-prefix"] != null && !metadata.has_argument (ArgumentType.NAME)) {
702 suffix = girdata["c:symbol-prefix"];
704 if (suffix == null) {
705 suffix = get_default_lower_case_csuffix ();
707 return suffix;
710 public string get_default_lower_case_csuffix () {
711 return Symbol.camel_case_to_lower_case (name);
714 public string get_cprefix () {
715 if (name == null) {
716 return "";
718 var prefix = symbol.get_attribute_string ("CCode", "cprefix");
719 if (prefix == null && girdata != null && girdata["c:identifier-prefixes"] != null) {
720 prefix = girdata["c:identifier-prefixes"];
721 int idx = prefix.index_of (",");
722 if (idx != -1) {
723 prefix = prefix.substring (0, idx);
726 if (prefix == null) {
727 if (symbol is Enum || symbol is ErrorDomain) {
728 prefix = "%s%s".printf (parent.get_lower_case_cprefix ().ascii_up (), name);
729 } else {
730 prefix = get_cname ();
733 return prefix;
736 public string get_cname () {
737 if (name == null) {
738 return "";
740 var cname = symbol.get_attribute_string ("CCode", "cname");
741 if (girdata != null) {
742 if (cname == null) {
743 cname = girdata["c:identifier"];
745 if (cname == null) {
746 cname = girdata["c:type"];
749 if (cname == null) {
750 cname = get_default_cname ();
752 return cname;
755 public string get_default_cname () {
756 if (name == null) {
757 return "";
759 if (symbol is Field) {
760 if (((Field) symbol).binding == MemberBinding.STATIC) {
761 return parent.get_lower_case_cprefix () + name;
762 } else {
763 return name;
765 } else if (symbol is Method) {
766 return "%s%s".printf (parent.get_lower_case_cprefix (), name);
767 } else {
768 return "%s%s".printf (parent.get_cprefix (), name);
772 public string get_finish_cname () {
773 var finish_cname = symbol.get_attribute_string ("CCode", "finish_name");
774 if (finish_cname == null) {
775 finish_cname = get_cname ();
776 if (finish_cname.has_suffix ("_async")) {
777 finish_cname = finish_cname.substring (0, finish_cname.length - "_async".length);
779 finish_cname = "%s_finish".printf (finish_cname);
781 return finish_cname;
784 public string get_cheader_filename () {
785 if (metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
786 return metadata.get_string (ArgumentType.CHEADER_FILENAME);
788 var cheader_filename = symbol.get_attribute_string ("CCode", "cheader_filename");
789 if (cheader_filename != null) {
790 return cheader_filename;
792 if (parent.name != null) {
793 return parent.get_cheader_filename ();
794 } else if (symbol.source_reference != null) {
795 return symbol.source_reference.file.get_cinclude_filename ();
797 return "";
800 private static uint64 parse_version_string (string version) {
801 int64 res = 0;
802 int shift = 16;
803 string[] tokens = version.split (".", 3);
805 foreach (unowned string token in tokens) {
806 int64 t;
808 if (!int64.try_parse (token, out t))
809 return 0;
810 if (t > 0xffff)
811 return 0;
813 res |= (t << shift);
814 shift -= 8;
817 return res;
820 public void process (GirParser parser) {
821 if (processed) {
822 return;
825 if (symbol is Namespace && parent == parser.root) {
826 // first process aliases since they have no assigned symbol
827 foreach (var node in members) {
828 if (node.element_type == "alias") {
829 parser.process_alias (node);
833 // auto reparent namespace methods, allowing node removals
834 for (int i=0; i < members.size; i++) {
835 var node = members[i];
836 if (node.symbol is Method && node.new_symbol) {
837 parser.process_namespace_method (this, node);
838 if (i < members.size && members[i] != node) {
839 // node removed in the middle
840 i--;
846 if (symbol is Class && girdata != null) {
847 var class_struct = girdata["glib:type-struct"];
848 if (class_struct != null) {
849 var klass = parser.resolve_node (parent, parser.parse_symbol_from_string (class_struct, source_reference));
850 if (klass != null) {
851 var i = 0;
852 while ( i < klass.members.size ) {
853 var node = klass.members[i];
854 if (node.symbol is Method) {
855 klass.remove_member (node);
856 this.add_member (node);
858 Method m = (Method) node.symbol;
859 m.binding = MemberBinding.CLASS;
860 } else {
861 i++;
868 // process children
869 foreach (var node in members) {
870 node.process (parser);
873 if (girdata != null) {
874 // GIR node processing
875 if (symbol is Method) {
876 var m = (Method) symbol;
877 parser.process_callable (this);
879 var colliding = parent.lookup_all (name);
880 foreach (var node in colliding) {
881 var sym = node.symbol;
882 if (sym is Field && !(m.return_type is VoidType) && m.get_parameters().size == 0) {
883 // assume method is getter
884 merged = true;
885 } else if (sym is Signal) {
886 node.process (parser);
887 var sig = (Signal) sym;
888 if (m.is_virtual || m.is_abstract) {
889 sig.is_virtual = true;
890 } else {
891 sig.set_attribute ("HasEmitter", true);
893 parser.assume_parameter_names (sig, m, false);
894 if (m.get_parameters().size != sig.get_parameters().size) {
895 Report.warning (symbol.source_reference, "Signal `%s' conflicts with method of the same name".printf (get_full_name ()));
897 merged = true;
898 } else if (sym is Method && !(sym is CreationMethod) && node != this) {
899 if (m.is_virtual || m.is_abstract) {
900 bool different_invoker = false;
901 var attr = m.get_attribute ("NoWrapper");
902 if (attr != null) {
903 /* no invoker but this method has the same name,
904 most probably the invoker has a different name
905 and g-ir-scanner missed it */
906 var invoker = parser.find_invoker (this);
907 if (invoker != null) {
908 m.set_attribute_string ("CCode", "vfunc_name", m.name);
909 m.name = invoker.symbol.name;
910 m.set_attribute ("NoWrapper", false);
911 invoker.merged = true;
912 different_invoker = true;
915 if (!different_invoker) {
916 if (attr != null) {
917 Report.warning (symbol.source_reference, "Virtual method `%s' conflicts with method of the same name".printf (get_full_name ()));
919 node.merged = true;
924 if (!(m is CreationMethod)) {
925 if (metadata.has_argument (ArgumentType.DESTROYS_INSTANCE)) {
926 m.set_attribute ("DestroysInstance", metadata.get_bool (ArgumentType.DESTROYS_INSTANCE));
928 if (metadata.has_argument (ArgumentType.RETURNS_MODIFIED_POINTER)) {
929 m.set_attribute ("ReturnsModifiedPointer", metadata.get_bool (ArgumentType.RETURNS_MODIFIED_POINTER));
931 // merge custom vfunc
932 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
933 var vfunc = parent.lookup (metadata.get_string (ArgumentType.VFUNC_NAME));
934 if (vfunc != null && vfunc != this) {
935 vfunc.processed = true;
936 vfunc.merged = true;
940 if (m.coroutine) {
941 parser.process_async_method (this);
943 } else if (symbol is Property) {
944 var colliding = parent.lookup_all (name);
945 foreach (var node in colliding) {
946 if (node.symbol is Signal) {
947 // properties take precedence
948 node.processed = true;
949 node.merged = true;
950 } else if (node.symbol is Method) {
951 // getter in C, but not in Vala
952 node.merged = true;
956 var prop = (Property) symbol;
958 // add accessors, can't do this before gir symbol resolution
959 var readable = girdata["readable"];
960 var writable = girdata["writable"];
961 var construct_ = girdata["construct"];
962 var construct_only = girdata["construct-only"];
963 if (readable != "0") {
964 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
966 if (writable == "1" || construct_only == "1") {
967 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
970 // find virtual/abstract accessors to handle abstract properties properly
972 Node getter = null;
973 var getters = parent.lookup_all ("get_%s".printf (name));
974 if (getters != null) {
975 foreach (var g in getters) {
976 if ((getter == null || !g.merged) && g.get_cname () == "%sget_%s".printf (parent.get_lower_case_cprefix (), name)) {
977 getter = g;
982 Node setter = null;
983 var setters = parent.lookup_all ("set_%s".printf (name));
984 if (setters != null) {
985 foreach (var s in setters) {
986 if ((setter == null || !s.merged) && s.get_cname () == "%sset_%s".printf (parent.get_lower_case_cprefix (), name)) {
987 setter = s;
992 prop.set_attribute ("NoAccessorMethod", (readable == "0" && construct_only == "1"));
993 if (prop.get_accessor != null) {
994 var m = getter != null ? getter.symbol as Method : null;
995 // ensure getter vfunc if the property is abstract
996 if (m != null) {
997 getter.process (parser);
998 if (m.return_type is VoidType || m.get_parameters().size != 0 || m.get_error_types ().size > 0) {
999 prop.set_attribute ("NoAccessorMethod", true);
1000 } else {
1001 if (getter.name == name) {
1002 foreach (var node in colliding) {
1003 if (node.symbol is Method) {
1004 node.merged = true;
1009 prop.get_accessor.value_type.value_owned = m.return_type.value_owned;
1011 if (!m.is_abstract && !m.is_virtual && prop.is_abstract) {
1012 prop.set_attribute ("ConcreteAccessor", true);
1015 } else {
1016 prop.set_attribute ("NoAccessorMethod", true);
1020 if (prop.get_attribute ("NoAccessorMethod") == null && prop.set_accessor != null && prop.set_accessor.writable) {
1021 var m = setter != null ? setter.symbol as Method : null;
1022 // ensure setter vfunc if the property is abstract
1023 if (m != null) {
1024 setter.process (parser);
1025 if (!(m.return_type is VoidType || m.return_type is BooleanType) || m.get_parameters ().size != 1 || m.get_error_types ().size > 0) {
1026 prop.set_attribute ("NoAccessorMethod", true);
1027 prop.set_attribute ("ConcreteAccessor", false);
1028 } else {
1029 prop.set_accessor.value_type.value_owned = m.get_parameters()[0].variable_type.value_owned;
1030 if (prop.get_attribute ("ConcreteAccessor") != null && !m.is_abstract && !m.is_virtual && prop.is_abstract) {
1031 prop.set_attribute ("ConcreteAccessor", true);
1032 prop.set_attribute ("NoAccessorMethod", false);
1035 } else {
1036 prop.set_attribute ("NoAccessorMethod", true);
1037 prop.set_attribute ("ConcreteAccessor", false);
1041 if (prop.get_attribute ("NoAccessorMethod") != null) {
1042 if (!prop.overrides && parent.symbol is Class) {
1043 // bug 742012
1044 // find base interface property with ConcreteAccessor and this overriding property with NoAccessorMethod
1045 var base_prop_node = parser.base_interface_property (this);
1046 if (base_prop_node != null) {
1047 base_prop_node.process (parser);
1049 var base_property = (Property) base_prop_node.symbol;
1050 if (base_property.get_attribute ("ConcreteAccessor") != null) {
1051 prop.set_attribute ("NoAccessorMethod", false);
1052 if (prop.get_accessor != null) {
1053 prop.get_accessor.value_type.value_owned = base_property.get_accessor.value_type.value_owned;
1055 if (prop.set_accessor != null) {
1056 prop.set_accessor.value_type.value_owned = base_property.set_accessor.value_type.value_owned;
1064 if (metadata.has_argument (ArgumentType.NO_ACCESSOR_METHOD)) {
1065 prop.set_attribute ("NoAccessorMethod", metadata.get_bool (ArgumentType.NO_ACCESSOR_METHOD));
1068 if (prop.get_attribute ("NoAccessorMethod") != null) {
1069 // gobject defaults
1070 if (prop.get_accessor != null) {
1071 prop.get_accessor.value_type.value_owned = true;
1073 if (prop.set_accessor != null) {
1074 prop.set_accessor.value_type.value_owned = false;
1077 } else if (symbol is Field) {
1078 var field = (Field) symbol;
1079 var colliding = parent.lookup_all (name);
1080 if (colliding.size > 1) {
1081 // whatelse has precedence over the field
1082 merged = true;
1085 if (metadata.has_argument (ArgumentType.DELEGATE_TARGET_CNAME)) {
1086 field.set_attribute_string ("CCode", "delegate_target_cname", metadata.get_string (ArgumentType.DELEGATE_TARGET_CNAME));
1089 if (field.variable_type is DelegateType && parent.gtype_struct_for != null) {
1090 // virtual method field
1091 var d = ((DelegateType) field.variable_type).delegate_symbol;
1092 parser.process_virtual_method_field (this, d, parent.gtype_struct_for);
1093 merged = true;
1094 } else if (field.variable_type is ArrayType) {
1095 Node array_length;
1096 if (metadata.has_argument (ArgumentType.ARRAY_LENGTH_FIELD)) {
1097 array_length = parent.lookup (metadata.get_string (ArgumentType.ARRAY_LENGTH_FIELD));
1098 } else if (array_length_idx > -1 && parent.members.size > array_length_idx) {
1099 array_length = parent.members[array_length_idx];
1100 } else {
1101 array_length = parent.lookup ("n_%s".printf (field.name));
1102 if (array_length == null) {
1103 array_length = parent.lookup ("num_%s".printf (field.name));
1104 if (array_length == null) {
1105 array_length = parent.lookup ("%s_length".printf (field.name));
1109 if (array_length != null && array_length.symbol is Field) {
1110 var length_field = (Field) array_length.symbol;
1111 // array has length
1112 field.set_attribute_string ("CCode", "array_length_cname", length_field.name);
1113 var length_type = length_field.variable_type.to_qualified_string ();
1114 if (length_type != "int") {
1115 var st = parser.root.lookup (length_type);
1116 if (st != null) {
1117 field.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1120 field.remove_attribute_argument ("CCode", "array_length");
1121 field.remove_attribute_argument ("CCode", "array_null_terminated");
1124 } else if (symbol is Signal || symbol is Delegate) {
1125 parser.process_callable (this);
1126 } else if (symbol is Interface) {
1127 parser.process_interface (this);
1128 } else if (symbol is Struct) {
1129 if (parent.symbol is ObjectTypeSymbol || parent.symbol is Struct) {
1130 // nested struct
1131 foreach (var fn in members) {
1132 var f = fn.symbol as Field;
1133 if (f != null) {
1134 if (f.binding == MemberBinding.INSTANCE) {
1135 f.set_attribute_string ("CCode", "cname", "%s.%s".printf (name, fn.get_cname ()));
1137 f.name = "%s_%s".printf (symbol.name, f.name);
1138 fn.name = f.name;
1139 parent.add_member (fn);
1142 merged = true;
1143 } else {
1144 // record for a gtype
1145 var gtype_struct_for = girdata["glib:is-gtype-struct-for"];
1146 if (gtype_struct_for != null) {
1147 var iface = parser.resolve_node (parent, parser.parse_symbol_from_string (gtype_struct_for, source_reference));
1148 if (iface != null && iface.symbol is Interface && "%sIface".printf (iface.get_cname ()) != get_cname ()) {
1149 // set the interface struct name
1150 iface.symbol.set_attribute_string ("CCode", "type_cname", get_cname ());
1152 merged = true;
1157 // deprecated
1158 if (metadata.has_argument (ArgumentType.REPLACEMENT)) {
1159 deprecated = true;
1160 deprecated_replacement = metadata.get_string (ArgumentType.REPLACEMENT);
1162 if (metadata.has_argument (ArgumentType.DEPRECATED_SINCE)) {
1163 deprecated = true;
1164 deprecated_since = metadata.get_string (ArgumentType.DEPRECATED_SINCE);
1165 } else if (girdata["deprecated-version"] != null) {
1166 deprecated = true;
1167 deprecated_since = girdata.get ("deprecated-version");
1169 if (metadata.has_argument (ArgumentType.DEPRECATED)) {
1170 deprecated = metadata.get_bool (ArgumentType.DEPRECATED, true);
1171 if (!deprecated) {
1172 deprecated_since = null;
1173 deprecated_replacement = null;
1175 } else if (girdata["deprecated"] != null) {
1176 deprecated = true;
1178 if (deprecated_since != null) {
1179 deprecated_version = parse_version_string (deprecated_since);
1182 // experimental
1183 if (metadata.has_argument (ArgumentType.EXPERIMENTAL)) {
1184 symbol.set_attribute_bool ("Version", "experimental", metadata.get_bool (ArgumentType.EXPERIMENTAL));
1187 // since
1188 if (metadata.has_argument (ArgumentType.SINCE)) {
1189 symbol.version.since = metadata.get_string (ArgumentType.SINCE);
1190 } else if (symbol is Namespace == false && girdata["version"] != null) {
1191 symbol.version.since = girdata.get ("version");
1194 if (parent.symbol is Namespace) {
1195 // always write cheader filename for namespace children
1196 symbol.set_attribute_string ("CCode", "cheader_filename", get_cheader_filename ());
1197 } else if (metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1198 symbol.set_attribute_string ("CCode", "cheader_filename", metadata.get_string (ArgumentType.CHEADER_FILENAME));
1200 if (get_cname () != get_default_cname ()) {
1201 symbol.set_attribute_string ("CCode", "cname", get_cname ());
1204 // lower_case_cprefix
1205 if (get_lower_case_cprefix () != get_default_lower_case_cprefix ()) {
1206 symbol.set_attribute_string ("CCode", "lower_case_cprefix", get_lower_case_cprefix ());
1208 // lower_case_csuffix
1209 if (get_lower_case_csuffix () != get_default_lower_case_csuffix ()) {
1210 symbol.set_attribute_string ("CCode", "lower_case_csuffix", get_lower_case_csuffix ());
1213 // set gir name if the symbol has been renamed
1214 string gir_name = get_gir_name ();
1215 string default_gir_name = get_default_gir_name ();
1216 if (is_container (symbol) && !(symbol is Namespace) && (name != gir_name || gir_name != default_gir_name)) {
1217 symbol.set_attribute_string ("GIR", "name", gir_name);
1221 if (!(new_symbol && merged) && is_container (symbol)) {
1222 foreach (var node in members) {
1223 if (this.deprecated_version > 0 && node.deprecated_version > 0) {
1224 if (this.deprecated_version <= node.deprecated_version) {
1225 node.deprecated = false;
1226 node.deprecated_since = null;
1227 node.deprecated_replacement = null;
1230 if (node.deprecated) {
1231 node.symbol.version.deprecated = true;
1233 if (node.deprecated_since != null) {
1234 node.symbol.version.deprecated_since = node.deprecated_since;
1236 if (node.deprecated_replacement != null) {
1237 node.symbol.version.replacement = node.deprecated_replacement;
1240 if (node.new_symbol && !node.merged && !metadata.get_bool (ArgumentType.HIDDEN)) {
1241 add_symbol_to_container (symbol, node.symbol);
1245 var cl = symbol as Class;
1246 if (cl != null && !cl.is_compact && cl.default_construction_method == null) {
1247 // always provide constructor in generated bindings
1248 // to indicate that implicit Object () chainup is allowed
1249 var cm = new CreationMethod (null, null, cl.source_reference);
1250 cm.has_construct_function = false;
1251 cm.access = SymbolAccessibility.PROTECTED;
1252 cl.add_method (cm);
1256 processed = true;
1259 public string to_string () {
1260 if (parent.name == null) {
1261 return name;
1262 } else {
1263 return "%s.%s".printf (parent.to_string (), name);
1268 static GLib.Regex type_from_string_regex;
1270 MarkupReader reader;
1272 CodeContext context;
1273 Namespace glib_ns;
1275 SourceFile current_source_file;
1276 Node root;
1277 ArrayList<Metadata> metadata_roots = new ArrayList<Metadata> ();
1279 SourceLocation begin;
1280 SourceLocation end;
1281 MarkupTokenType current_token;
1283 string[] cheader_filenames;
1285 ArrayList<Metadata> metadata_stack;
1286 Metadata metadata;
1287 ArrayList<Node> tree_stack;
1288 Node current;
1289 Node old_current;
1291 Set<string> provided_namespaces = new HashSet<string> (str_hash, str_equal);
1292 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
1293 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
1294 ArrayList<DataType> unresolved_type_arguments = new ArrayList<DataType> ();
1297 * Parses all .gir source files in the specified code
1298 * context and builds a code tree.
1300 * @param context a code context
1302 public void parse (CodeContext context) {
1303 this.context = context;
1304 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
1306 root = new Node (null);
1307 root.symbol = context.root;
1308 tree_stack = new ArrayList<Node> ();
1309 current = root;
1311 map_vala_to_gir ();
1313 context.accept (this);
1315 resolve_gir_symbols ();
1316 create_new_namespaces ();
1317 resolve_type_arguments ();
1319 root.process (this);
1321 foreach (var metadata in metadata_roots) {
1322 report_unused_metadata (metadata);
1326 void map_vala_to_gir () {
1327 foreach (var source_file in context.get_source_files ()) {
1328 string gir_namespace = source_file.gir_namespace;
1329 string gir_version = source_file.gir_version;
1330 Namespace ns = null;
1331 if (gir_namespace == null) {
1332 foreach (var node in source_file.get_nodes ()) {
1333 if (node is Namespace) {
1334 ns = (Namespace) node;
1335 gir_namespace = ns.get_attribute_string ("CCode", "gir_namespace");
1336 if (gir_namespace != null) {
1337 gir_version = ns.get_attribute_string ("CCode", "gir_version");
1338 break;
1343 if (gir_namespace == null) {
1344 continue;
1347 provided_namespaces.add ("%s-%s".printf (gir_namespace, gir_version));
1349 var gir_symbol = new UnresolvedSymbol (null, gir_namespace);
1350 if (gir_namespace != ns.name) {
1351 set_symbol_mapping (gir_symbol, ns);
1354 foreach (var node in source_file.get_nodes ()) {
1355 if (node.has_attribute_argument ("GIR", "name")) {
1356 var map_from = new UnresolvedSymbol (gir_symbol, node.get_attribute_string ("GIR", "name"));
1357 set_symbol_mapping (map_from, (Symbol) node);
1363 public override void visit_source_file (SourceFile source_file) {
1364 if (source_file.filename.has_suffix (".gir")) {
1365 parse_file (source_file);
1369 public void parse_file (SourceFile source_file) {
1370 metadata_stack = new ArrayList<Metadata> ();
1371 metadata = Metadata.empty;
1372 cheader_filenames = null;
1374 this.current_source_file = source_file;
1375 reader = new MarkupReader (source_file.filename);
1377 // xml prolog
1378 next ();
1379 next ();
1381 next ();
1382 parse_repository ();
1384 reader = null;
1385 this.current_source_file = null;
1388 void next () {
1389 current_token = reader.read_token (out begin, out end);
1392 void start_element (string name) {
1393 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
1394 // error
1395 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
1399 void end_element (string name) {
1400 while (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
1401 Report.warning (get_current_src (), "expected end element of `%s'".printf (name));
1402 skip_element ();
1404 next ();
1407 SourceReference get_current_src () {
1408 return new SourceReference (this.current_source_file, begin, end);
1411 const string GIR_VERSION = "1.2";
1413 static void add_symbol_to_container (Symbol container, Symbol sym) {
1414 if (container is Class) {
1415 unowned Class cl = (Class) container;
1417 if (sym is Class) {
1418 cl.add_class ((Class) sym);
1419 } else if (sym is Constant) {
1420 cl.add_constant ((Constant) sym);
1421 } else if (sym is Enum) {
1422 cl.add_enum ((Enum) sym);
1423 } else if (sym is Field) {
1424 cl.add_field ((Field) sym);
1425 } else if (sym is Method) {
1426 cl.add_method ((Method) sym);
1427 } else if (sym is Property) {
1428 cl.add_property ((Property) sym);
1429 } else if (sym is Signal) {
1430 cl.add_signal ((Signal) sym);
1431 } else if (sym is Struct) {
1432 cl.add_struct ((Struct) sym);
1434 } else if (container is Enum) {
1435 unowned Enum en = (Enum) container;
1437 if (sym is EnumValue) {
1438 en.add_value ((EnumValue) sym);
1439 } else if (sym is Constant) {
1440 en.add_constant ((Constant) sym);
1441 } else if (sym is Method) {
1442 en.add_method ((Method) sym);
1444 } else if (container is Interface) {
1445 unowned Interface iface = (Interface) container;
1447 if (sym is Class) {
1448 iface.add_class ((Class) sym);
1449 } else if (sym is Constant) {
1450 iface.add_constant ((Constant) sym);
1451 } else if (sym is Enum) {
1452 iface.add_enum ((Enum) sym);
1453 } else if (sym is Field) {
1454 iface.add_field ((Field) sym);
1455 } else if (sym is Method) {
1456 iface.add_method ((Method) sym);
1457 } else if (sym is Property) {
1458 iface.add_property ((Property) sym);
1459 } else if (sym is Signal) {
1460 iface.add_signal ((Signal) sym);
1461 } else if (sym is Struct) {
1462 iface.add_struct ((Struct) sym);
1464 } else if (container is Namespace) {
1465 unowned Namespace ns = (Namespace) container;
1467 if (sym is Namespace) {
1468 ns.add_namespace ((Namespace) sym);
1469 } else if (sym is Class) {
1470 ns.add_class ((Class) sym);
1471 } else if (sym is Constant) {
1472 ns.add_constant ((Constant) sym);
1473 } else if (sym is Delegate) {
1474 ns.add_delegate ((Delegate) sym);
1475 } else if (sym is Enum) {
1476 ns.add_enum ((Enum) sym);
1477 } else if (sym is ErrorDomain) {
1478 ns.add_error_domain ((ErrorDomain) sym);
1479 } else if (sym is Field) {
1480 ns.add_field ((Field) sym);
1481 } else if (sym is Interface) {
1482 ns.add_interface ((Interface) sym);
1483 } else if (sym is Method) {
1484 ns.add_method ((Method) sym);
1485 } else if (sym is Struct) {
1486 ns.add_struct ((Struct) sym);
1488 } else if (container is Struct) {
1489 unowned Struct st = (Struct) container;
1491 if (sym is Constant) {
1492 st.add_constant ((Constant) sym);
1493 } else if (sym is Field) {
1494 st.add_field ((Field) sym);
1495 } else if (sym is Method) {
1496 st.add_method ((Method) sym);
1497 } else if (sym is Property) {
1498 st.add_property ((Property) sym);
1500 } else if (container is ErrorDomain) {
1501 unowned ErrorDomain ed = (ErrorDomain) container;
1503 if (sym is ErrorCode) {
1504 ed.add_code ((ErrorCode) sym);
1505 } else if (sym is Method) {
1506 ed.add_method ((Method) sym);
1508 } else {
1509 Report.error (sym.source_reference, "impossible to add `%s' to container `%s'".printf (sym.name, container.name));
1513 static bool is_container (Symbol sym) {
1514 return sym is ObjectTypeSymbol || sym is Struct || sym is Namespace || sym is ErrorDomain || sym is Enum;
1517 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
1518 UnresolvedSymbol? sym = null;
1519 foreach (unowned string s in symbol_string.split (".")) {
1520 sym = new UnresolvedSymbol (sym, s, source_reference);
1522 if (sym == null) {
1523 Report.error (source_reference, "a symbol must be specified");
1525 return sym;
1528 void set_symbol_mapping (UnresolvedSymbol map_from, Symbol map_to) {
1529 // last mapping is the most up-to-date
1530 if (map_from is UnresolvedSymbol) {
1531 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
1535 void assume_parameter_names (Signal sig, Symbol sym, bool skip_first) {
1536 var iter = ((Callable) sym).get_parameters ().iterator ();
1537 bool first = true;
1538 foreach (var param in sig.get_parameters ()) {
1539 if (!iter.next ()) {
1540 // unreachable for valid GIR
1541 break;
1543 if (skip_first && first) {
1544 if (!iter.next ()) {
1545 // unreachable for valid GIR
1546 break;
1548 first = false;
1550 param.name = iter.get ().name;
1554 Node? find_invoker (Node node) {
1555 /* most common use case is invoker has at least the given method prefix
1556 and the same parameter names */
1557 var m = (Method) node.symbol;
1558 var prefix = "%s_".printf (m.name);
1559 foreach (var n in node.parent.members) {
1560 if (!n.name.has_prefix (prefix)) {
1561 continue;
1563 Method? invoker = n.symbol as Method;
1564 if (invoker == null || (m.get_parameters().size != invoker.get_parameters().size)) {
1565 continue;
1567 var iter = invoker.get_parameters ().iterator ();
1568 foreach (var param in m.get_parameters ()) {
1569 assert (iter.next ());
1570 if (param.name != iter.get().name) {
1571 invoker = null;
1572 break;
1575 if (invoker != null) {
1576 return n;
1580 return null;
1583 Metadata get_current_metadata () {
1584 var selector = reader.name;
1585 var child_name = reader.get_attribute ("name");
1586 if (child_name == null) {
1587 child_name = reader.get_attribute ("glib:name");
1589 // Give a transparent union the generic name "union"
1590 if (selector == "union" && child_name == null) {
1591 child_name = "union";
1593 if (child_name == null) {
1594 return Metadata.empty;
1596 selector = selector.replace ("-", "_");
1597 child_name = child_name.replace ("-", "_");
1599 if (selector.has_prefix ("glib:")) {
1600 selector = selector.substring ("glib:".length);
1603 return metadata.match_child (child_name, selector);
1606 bool push_metadata () {
1607 var new_metadata = get_current_metadata ();
1608 // skip ?
1609 if (new_metadata.has_argument (ArgumentType.SKIP)) {
1610 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1611 return false;
1613 } else if (reader.get_attribute ("introspectable") == "0" || reader.get_attribute ("private") == "1") {
1614 return false;
1617 metadata_stack.add (metadata);
1618 metadata = new_metadata;
1620 return true;
1623 void pop_metadata () {
1624 metadata = metadata_stack[metadata_stack.size - 1];
1625 metadata_stack.remove_at (metadata_stack.size - 1);
1628 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1629 int type_arguments_length = (int) type_arguments.length;
1630 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1632 int depth = 0;
1633 for (var c = 0 ; c < type_arguments_length ; c++) {
1634 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1635 depth++;
1636 current.append_unichar (type_arguments[c]);
1637 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1638 depth--;
1639 current.append_unichar (type_arguments[c]);
1640 } else if (type_arguments[c] == ',') {
1641 if (depth == 0) {
1642 var dt = parse_type_from_string (current.str, true, source_reference);
1643 if (dt == null) {
1644 return false;
1646 parent_type.add_type_argument (dt);
1647 current.truncate ();
1648 } else {
1649 current.append_unichar (type_arguments[c]);
1651 } else {
1652 current.append_unichar (type_arguments[c]);
1656 var dt = parse_type_from_string (current.str, true, source_reference);
1657 if (dt == null) {
1658 return false;
1660 parent_type.add_type_argument (dt);
1662 return true;
1665 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1666 if (type_from_string_regex == null) {
1667 try {
1668 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[,*\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1669 } catch (GLib.RegexError e) {
1670 GLib.error ("Unable to compile regex: %s", e.message);
1674 GLib.MatchInfo match;
1675 if (!type_from_string_regex.match (type_string, 0, out match)) {
1676 Report.error (source_reference, "unable to parse type");
1677 return null;
1680 DataType? type = null;
1682 var ownership_data = match.fetch (1);
1683 var type_name = match.fetch (2);
1684 var type_arguments_data = match.fetch (3);
1685 var pointers_data = match.fetch (4);
1686 var array_data = match.fetch (5);
1687 var nullable_data = match.fetch (6);
1689 var nullable = nullable_data != null && nullable_data.length > 0;
1691 if (ownership_data == null && type_name == "void") {
1692 if (array_data == null && !nullable) {
1693 type = new VoidType (source_reference);
1694 if (pointers_data != null) {
1695 for (int i=0; i < pointers_data.length; i++) {
1696 type = new PointerType (type);
1699 return type;
1700 } else {
1701 Report.error (source_reference, "invalid void type");
1702 return null;
1706 bool value_owned = owned_by_default;
1708 if (ownership_data == "owned") {
1709 if (owned_by_default) {
1710 Report.error (source_reference, "unexpected `owned' keyword");
1711 } else {
1712 value_owned = true;
1714 } else if (ownership_data == "unowned") {
1715 if (owned_by_default) {
1716 value_owned = false;
1717 } else {
1718 Report.error (source_reference, "unexpected `unowned' keyword");
1719 return null;
1723 var sym = parse_symbol_from_string (type_name, source_reference);
1724 if (sym == null) {
1725 return null;
1727 type = new UnresolvedType.from_symbol (sym, source_reference);
1729 if (type_arguments_data != null && type_arguments_data.length > 0) {
1730 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1731 return null;
1735 if (pointers_data != null) {
1736 for (int i=0; i < pointers_data.length; i++) {
1737 type = new PointerType (type);
1741 if (array_data != null && array_data.length != 0) {
1742 type.value_owned = true;
1743 type = new ArrayType (type, (int) array_data.length - 1, source_reference);
1746 type.nullable = nullable;
1747 type.value_owned = value_owned;
1748 return type;
1751 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1752 if (metadata.has_argument (arg_type)) {
1753 return metadata.get_string (arg_type);
1754 } else {
1755 return reader.get_attribute (attribute_name);
1760 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1762 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) {
1763 changed = false;
1764 var type = orig_type;
1766 if (metadata.has_argument (ArgumentType.TYPE)) {
1767 type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1768 changed = true;
1769 } else if (!(type is VoidType)) {
1770 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1771 type.remove_all_type_arguments ();
1772 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1775 if (!(type is ArrayType) && metadata.get_bool (ArgumentType.ARRAY)) {
1776 type.value_owned = true;
1777 type = new ArrayType (type, 1, type.source_reference);
1778 changed = true;
1781 if (owned_by_default) {
1782 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED, !type.value_owned);
1783 } else {
1784 type.value_owned = metadata.get_bool (ArgumentType.OWNED, type.value_owned);
1786 type.nullable = metadata.get_bool (ArgumentType.NULLABLE, type.nullable);
1789 if (type is ArrayType) {
1790 if (!(orig_type is ArrayType)) {
1791 no_array_length = true;
1793 array_null_terminated = metadata.get_bool (ArgumentType.ARRAY_NULL_TERMINATED, array_null_terminated);
1796 return type;
1799 string? element_get_name (string? gir_name = null) {
1800 var name = gir_name;
1801 if (name == null) {
1802 name = reader.get_attribute ("name");
1804 var pattern = metadata.get_string (ArgumentType.NAME);
1805 if (pattern != null) {
1806 if (pattern.index_of_char ('(') < 0) {
1807 // shortcut for "(.+)/replacement"
1808 name = pattern;
1809 } else {
1810 try {
1811 string replacement = "\\1"; // replace the whole name with the match by default
1812 var split = pattern.split ("/");
1813 if (split.length > 1) {
1814 pattern = split[0];
1815 replacement = split[1];
1817 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1818 name = regex.replace (name, -1, 0, replacement);
1819 } catch (Error e) {
1820 name = pattern;
1823 } else {
1824 if (name != null && name.has_suffix ("Enum")) {
1825 name = name.substring (0, name.length - "Enum".length);
1829 return name;
1832 string? element_get_type_id () {
1833 var type_id = metadata.get_string (ArgumentType.TYPE_ID);
1834 if (type_id != null) {
1835 return type_id;
1838 type_id = reader.get_attribute ("glib:get-type");
1839 if (type_id != null) {
1840 type_id += " ()";
1842 return type_id;
1845 void set_array_ccode (Symbol sym, ParameterInfo info) {
1846 sym.set_attribute_double ("CCode", "array_length_pos", info.vala_idx);
1847 if (sym is Parameter) {
1848 sym.set_attribute_string ("CCode", "array_length_cname", info.param.name);
1850 var type_name = info.param.variable_type.to_qualified_string ();
1851 if (type_name != "int") {
1852 var st = root.lookup (type_name);
1853 if (st != null) {
1854 if (sym is Method) {
1855 var m = (Method) sym;
1856 m.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1857 } else {
1858 var param = (Parameter) sym;
1859 param.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1865 void set_type_id_ccode (Symbol sym) {
1866 if (sym.has_attribute_argument ("CCode", "has_type_id")
1867 || sym.has_attribute_argument ("CCode", "type_id"))
1868 return;
1870 var type_id = element_get_type_id ();
1871 if (type_id == null) {
1872 sym.set_attribute_bool ("CCode", "has_type_id", false);
1873 } else {
1874 sym.set_attribute_string ("CCode", "type_id", type_id);
1878 void parse_repository () {
1879 start_element ("repository");
1880 if (reader.get_attribute ("version") != GIR_VERSION) {
1881 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1882 return;
1884 next ();
1885 while (current_token == MarkupTokenType.START_ELEMENT) {
1886 if (reader.name == "namespace") {
1887 parse_namespace ();
1888 } else if (reader.name == "include") {
1889 parse_include ();
1890 } else if (reader.name == "package") {
1891 var pkg = parse_package ();
1892 this.current_source_file.package_name = pkg;
1893 if (context.has_package (pkg)) {
1894 // package already provided elsewhere, stop parsing this GIR
1895 // if it was not passed explicitly
1896 if (!this.current_source_file.explicit) {
1897 return;
1899 } else {
1900 context.add_package (pkg);
1902 } else if (reader.name == "c:include") {
1903 parse_c_include ();
1904 } else {
1905 // error
1906 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1907 skip_element ();
1910 end_element ("repository");
1913 void parse_include () {
1914 start_element ("include");
1915 var pkg = reader.get_attribute ("name");
1916 var version = reader.get_attribute ("version");
1917 if (version != null) {
1918 pkg = "%s-%s".printf (pkg, version);
1920 // add the package to the queue
1921 context.add_external_package (pkg);
1922 next ();
1923 end_element ("include");
1926 string parse_package () {
1927 start_element ("package");
1928 var pkg = reader.get_attribute ("name");
1929 next ();
1930 end_element ("package");
1931 return pkg;
1934 void parse_c_include () {
1935 start_element ("c:include");
1936 cheader_filenames += reader.get_attribute ("name");
1937 next ();
1938 end_element ("c:include");
1941 void skip_element () {
1942 next ();
1944 int level = 1;
1945 while (level > 0) {
1946 if (current_token == MarkupTokenType.START_ELEMENT) {
1947 level++;
1948 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1949 level--;
1950 } else if (current_token == MarkupTokenType.EOF) {
1951 Report.error (get_current_src (), "unexpected end of file");
1952 break;
1954 next ();
1958 Node? resolve_node (Node parent_scope, UnresolvedSymbol unresolved_sym, bool create_namespace = false) {
1959 if (unresolved_sym.inner == null) {
1960 var scope = parent_scope;
1961 while (scope != null) {
1962 var node = scope.lookup (unresolved_sym.name, create_namespace, unresolved_sym.source_reference);
1963 if (node != null) {
1964 return node;
1966 scope = scope.parent;
1968 } else {
1969 var inner = resolve_node (parent_scope, unresolved_sym.inner, create_namespace);
1970 if (inner != null) {
1971 return inner.lookup (unresolved_sym.name, create_namespace, unresolved_sym.source_reference);
1974 return null;
1977 Symbol? resolve_symbol (Node parent_scope, UnresolvedSymbol unresolved_sym) {
1978 var node = resolve_node (parent_scope, unresolved_sym);
1979 if (node != null) {
1980 return node.symbol;
1982 return null;
1985 void push_node (string name, bool merge) {
1986 var parent = current;
1987 if (metadata.has_argument (ArgumentType.PARENT)) {
1988 var target = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
1989 parent = resolve_node (root, target, true);
1992 var node = parent.lookup (name);
1993 if (node == null || (node.symbol != null && !merge)) {
1994 node = new Node (name);
1995 node.new_symbol = true;
1996 parent.add_member (node);
1997 } else {
1998 Node.new_namespaces.remove (node);
2000 node.element_type = reader.name;
2001 node.girdata = reader.get_attributes ();
2002 node.metadata = metadata;
2003 node.source_reference = get_current_src ();
2005 var gir_name = node.get_gir_name ();
2006 if (parent != current || gir_name != name) {
2007 set_symbol_mapping (new UnresolvedSymbol (null, gir_name), node.get_unresolved_symbol ());
2010 tree_stack.add (current);
2011 current = node;
2014 void pop_node () {
2015 old_current = current;
2016 current = tree_stack[tree_stack.size - 1];
2017 tree_stack.remove_at (tree_stack.size - 1);
2020 void parse_namespace () {
2021 start_element ("namespace");
2023 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
2024 if (cprefix != null) {
2025 int idx = cprefix.index_of (",");
2026 if (idx != -1) {
2027 cprefix = cprefix.substring (0, idx);
2031 string? lower_case_cprefix = reader.get_attribute ("c:symbol-prefixes");
2032 string vala_namespace = cprefix;
2033 string gir_namespace = reader.get_attribute ("name");
2034 string gir_version = reader.get_attribute ("version");
2036 if (lower_case_cprefix != null) {
2037 int idx = lower_case_cprefix.index_of (",");
2038 if (idx != -1) {
2039 lower_case_cprefix = lower_case_cprefix.substring (0, idx);
2043 if (provided_namespaces.contains ("%s-%s".printf (gir_namespace, gir_version))) {
2044 skip_element ();
2045 return;
2048 // load metadata, first look into metadata directories then in the same directory of the .gir.
2049 string? metadata_filename = context.get_metadata_path (current_source_file.filename);
2050 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
2051 var metadata_parser = new MetadataParser ();
2052 var metadata_file = new SourceFile (context, current_source_file.file_type, metadata_filename);
2053 context.add_source_file (metadata_file);
2054 metadata = metadata_parser.parse_metadata (metadata_file);
2055 metadata_roots.add (metadata);
2058 var ns_metadata = metadata.match_child (gir_namespace);
2059 if (ns_metadata.has_argument (ArgumentType.NAME)) {
2060 vala_namespace = ns_metadata.get_string (ArgumentType.NAME);
2062 if (vala_namespace == null) {
2063 vala_namespace = gir_namespace;
2066 current_source_file.gir_namespace = gir_namespace;
2067 current_source_file.gir_version = gir_version;
2069 Namespace ns;
2070 push_node (vala_namespace, true);
2071 if (current.new_symbol) {
2072 ns = new Namespace (vala_namespace, current.source_reference);
2073 current.symbol = ns;
2074 } else {
2075 ns = (Namespace) current.symbol;
2076 ns.attributes = null;
2077 ns.source_reference = current.source_reference;
2080 current.metadata = ns_metadata;
2082 if (ns_metadata.has_argument (ArgumentType.CPREFIX)) {
2083 cprefix = ns_metadata.get_string (ArgumentType.CPREFIX);
2086 if (ns_metadata.has_argument (ArgumentType.LOWER_CASE_CPREFIX)) {
2087 lower_case_cprefix = ns_metadata.get_string (ArgumentType.LOWER_CASE_CPREFIX);
2088 } else if (lower_case_cprefix != null) {
2089 lower_case_cprefix += "_";
2092 ns.set_attribute_string ("CCode", "gir_namespace", gir_namespace);
2093 ns.set_attribute_string ("CCode", "gir_version", gir_version);
2095 if (cprefix != null) {
2096 ns.set_attribute_string ("CCode", "cprefix", cprefix);
2097 if (lower_case_cprefix == null) {
2098 ns.set_attribute_string ("CCode", "lower_case_cprefix", Symbol.camel_case_to_lower_case (cprefix) + "_");
2102 if (lower_case_cprefix != null) {
2103 ns.set_attribute_string ("CCode", "lower_case_cprefix", lower_case_cprefix);
2106 if (cheader_filenames != null) {
2107 ns.set_attribute_string ("CCode", "cheader_filename", string.joinv (",", cheader_filenames));
2110 next ();
2111 while (current_token == MarkupTokenType.START_ELEMENT) {
2112 if (!push_metadata ()) {
2113 skip_element ();
2114 continue;
2117 if (reader.name == "alias") {
2118 parse_alias ();
2119 } else if (reader.name == "enumeration") {
2120 if (metadata.has_argument (ArgumentType.ERRORDOMAIN)) {
2121 if (metadata.get_bool (ArgumentType.ERRORDOMAIN)) {
2122 parse_error_domain ();
2123 } else {
2124 parse_enumeration ();
2126 } else {
2127 if ((reader.get_attribute ("glib:error-quark") != null) || (reader.get_attribute ("glib:error-domain") != null)) {
2128 parse_error_domain ();
2129 } else {
2130 parse_enumeration ();
2133 } else if (reader.name == "bitfield") {
2134 parse_bitfield ();
2135 } else if (reader.name == "function") {
2136 parse_method ("function");
2137 } else if (reader.name == "callback") {
2138 parse_callback ();
2139 } else if (reader.name == "record") {
2140 if (metadata.has_argument (ArgumentType.STRUCT)) {
2141 if (metadata.get_bool (ArgumentType.STRUCT)) {
2142 parse_record ();
2143 } else {
2144 parse_boxed ("record");
2146 } else if (element_get_type_id () != null) {
2147 parse_boxed ("record");
2148 } else if (!reader.get_attribute ("name").has_suffix ("Private")) {
2149 if (reader.get_attribute ("glib:is-gtype-struct-for") == null && reader.get_attribute ("disguised") == "1") {
2150 parse_boxed ("record");
2151 } else {
2152 parse_record ();
2154 } else {
2155 skip_element ();
2157 } else if (reader.name == "class") {
2158 parse_class ();
2159 } else if (reader.name == "interface") {
2160 parse_interface ();
2161 } else if (reader.name == "glib:boxed") {
2162 parse_boxed ("glib:boxed");
2163 } else if (reader.name == "union") {
2164 if (element_get_type_id () != null && !metadata.get_bool (ArgumentType.STRUCT)) {
2165 parse_boxed ("union");
2166 } else {
2167 parse_union ();
2169 } else if (reader.name == "constant") {
2170 parse_constant ();
2171 } else {
2172 // error
2173 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
2174 skip_element ();
2177 pop_metadata ();
2179 pop_node ();
2180 end_element ("namespace");
2183 void parse_alias () {
2184 start_element ("alias");
2185 push_node (element_get_name (), true);
2186 // not enough information, symbol will be created while processing the tree
2188 next ();
2190 if (current.comment == null) {
2191 current.comment = parse_symbol_doc ();
2192 } else {
2193 parse_symbol_doc ();
2196 bool no_array_length = false;
2197 bool array_null_terminated = false;
2198 current.base_type = element_get_type (parse_type (null, null, true), true, ref no_array_length, ref array_null_terminated);
2200 if (metadata.has_argument (ArgumentType.BASE_TYPE)) {
2201 current.base_type = parse_type_from_string (metadata.get_string (ArgumentType.BASE_TYPE), true, metadata.get_source_reference (ArgumentType.BASE_TYPE));
2204 pop_node ();
2205 end_element ("alias");
2208 private void calculate_common_prefix (ref string? common_prefix, string cname) {
2209 if (common_prefix == null) {
2210 common_prefix = cname;
2211 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
2212 // FIXME: could easily be made faster
2213 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2215 } else {
2216 while (!cname.has_prefix (common_prefix)) {
2217 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2220 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
2221 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
2222 // enum values may not consist solely of digits
2223 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2227 GirComment? parse_symbol_doc () {
2228 GirComment? comment = null;
2230 while (current_token == MarkupTokenType.START_ELEMENT) {
2231 unowned string reader_name = reader.name;
2233 if (reader_name == "doc") {
2234 start_element ("doc");
2235 next ();
2238 if (current_token == MarkupTokenType.TEXT) {
2239 comment = new GirComment (reader.content, current.source_reference);
2240 next ();
2243 end_element ("doc");
2244 } else if (reader_name == "doc-version" || reader_name == "doc-deprecated" || reader_name == "doc-stability") {
2245 skip_element ();
2246 } else {
2247 break;
2251 return comment;
2254 Comment? parse_doc () {
2255 Comment? comment = null;
2257 while (current_token == MarkupTokenType.START_ELEMENT) {
2258 unowned string reader_name = reader.name;
2260 if (reader_name == "doc") {
2261 start_element ("doc");
2262 next ();
2265 if (current_token == MarkupTokenType.TEXT) {
2266 comment = new Comment (reader.content, current.source_reference);
2267 next ();
2270 end_element ("doc");
2271 } else if (reader_name == "doc-version" || reader_name == "doc-deprecated" || reader_name == "doc-stability") {
2272 skip_element ();
2273 } else {
2274 break;
2278 return comment;
2281 void parse_enumeration (string element_name = "enumeration", bool error_domain = false) {
2282 start_element (element_name);
2283 push_node (element_get_name (), true);
2285 Symbol sym;
2286 if (current.new_symbol) {
2287 if (error_domain) {
2288 sym = new ErrorDomain (current.name, current.source_reference);
2289 } else {
2290 var en = new Enum (current.name, current.source_reference);
2291 if (element_name == "bitfield") {
2292 en.set_attribute ("Flags", true);
2294 sym = en;
2296 current.symbol = sym;
2297 } else {
2298 sym = current.symbol;
2301 if (!error_domain)
2302 set_type_id_ccode (sym);
2304 sym.external = true;
2305 sym.access = SymbolAccessibility.PUBLIC;
2307 string common_prefix = null;
2308 bool has_member = false;
2310 next ();
2312 sym.comment = parse_symbol_doc ();
2314 while (current_token == MarkupTokenType.START_ELEMENT) {
2315 if (!push_metadata ()) {
2316 skip_element ();
2317 continue;
2320 if (reader.name == "member") {
2321 has_member = true;
2322 if (error_domain) {
2323 parse_error_member ();
2324 calculate_common_prefix (ref common_prefix, old_current.get_cname ());
2325 } else {
2326 parse_enumeration_member ();
2327 calculate_common_prefix (ref common_prefix, old_current.get_cname ());
2329 } else if (reader.name == "function") {
2330 skip_element ();
2331 } else {
2332 // error
2333 Report.error (get_current_src (), "unknown child element `%s' in `%s'".printf (reader.name, element_name));
2334 skip_element ();
2337 pop_metadata ();
2340 if (!has_member) {
2341 Report.error (get_current_src (), "%s `%s' has no members".printf (element_name, current.name));
2344 if (common_prefix != null) {
2345 sym.set_attribute_string ("CCode", "cprefix", common_prefix);
2348 pop_node ();
2349 end_element (element_name);
2352 void parse_error_domain () {
2353 parse_enumeration ("enumeration", true);
2356 void parse_bitfield () {
2357 parse_enumeration ("bitfield");
2360 void parse_enumeration_member () {
2361 start_element ("member");
2362 push_node (element_get_name().ascii_up().replace ("-", "_"), false);
2364 var ev = new EnumValue (current.name, metadata.get_expression (ArgumentType.DEFAULT), current.source_reference);
2365 current.symbol = ev;
2366 next ();
2368 ev.comment = parse_symbol_doc ();
2370 pop_node ();
2371 end_element ("member");
2374 void parse_error_member () {
2375 start_element ("member");
2376 push_node (element_get_name().ascii_up().replace ("-", "_"), false);
2378 ErrorCode ec;
2379 string value = reader.get_attribute ("value");
2380 if (value != null) {
2381 ec = new ErrorCode.with_value (current.name, new IntegerLiteral (value));
2382 } else {
2383 ec = new ErrorCode (current.name);
2385 current.symbol = ec;
2386 next ();
2388 ec.comment = parse_symbol_doc ();
2390 pop_node ();
2391 end_element ("member");
2394 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) {
2395 start_element ("return-value");
2397 string transfer = reader.get_attribute ("transfer-ownership");
2398 string nullable = reader.get_attribute ("nullable");
2399 string allow_none = reader.get_attribute ("allow-none");
2400 next ();
2402 comment = parse_doc ();
2404 var transfer_elements = transfer != "container";
2405 var type = parse_type (out ctype, out array_length_idx, transfer_elements, out no_array_length, out array_null_terminated);
2406 if (transfer == "full" || transfer == "container") {
2407 type.value_owned = true;
2409 if (nullable == "1" || allow_none == "1") {
2410 type.nullable = true;
2412 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2414 // FIXME No support for fixed-size array as return-value
2415 var array_type = type as ArrayType;
2416 if (array_type != null && array_type.fixed_length) {
2417 array_type.fixed_length = false;
2418 array_type.length = null;
2421 end_element ("return-value");
2422 return type;
2425 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) {
2426 Parameter param;
2428 array_length_idx = -1;
2429 closure_idx = -1;
2430 destroy_idx = -1;
2432 string element_type = reader.name;
2433 if (current_token != MarkupTokenType.START_ELEMENT || (element_type != "parameter" && element_type != "instance-parameter")) {
2434 Report.error (get_current_src (), "expected start element of `parameter' or `instance-parameter'");
2436 start_element (element_type);
2437 var name = metadata.get_string (ArgumentType.NAME);
2438 if (name == null) {
2439 name = reader.get_attribute ("name");
2441 if (name == null) {
2442 name = default_name;
2444 string direction = null;
2445 if (metadata.has_argument (ArgumentType.OUT)) {
2446 if (metadata.get_bool (ArgumentType.OUT)) {
2447 direction = "out";
2448 } // null otherwise
2449 } else if (metadata.has_argument (ArgumentType.REF)) {
2450 if (metadata.get_bool (ArgumentType.REF)) {
2451 direction = "inout";
2452 } // null otherwise
2453 } else {
2454 direction = reader.get_attribute ("direction");
2456 string transfer = reader.get_attribute ("transfer-ownership");
2457 string nullable = reader.get_attribute ("nullable");
2458 string allow_none = reader.get_attribute ("allow-none");
2460 scope = element_get_string ("scope", ArgumentType.SCOPE);
2462 string closure = reader.get_attribute ("closure");
2463 string destroy = reader.get_attribute ("destroy");
2464 if (closure != null && &closure_idx != null) {
2465 closure_idx = int.parse (closure);
2467 if (destroy != null && &destroy_idx != null) {
2468 destroy_idx = int.parse (destroy);
2470 if (metadata.has_argument (ArgumentType.CLOSURE)) {
2471 closure_idx = metadata.get_integer (ArgumentType.CLOSURE);
2473 if (metadata.has_argument (ArgumentType.DESTROY)) {
2474 destroy_idx = metadata.get_integer (ArgumentType.DESTROY);
2477 next ();
2479 comment = parse_doc ();
2481 if (reader.name == "varargs") {
2482 start_element ("varargs");
2483 next ();
2484 param = new Parameter.with_ellipsis (get_current_src ());
2485 end_element ("varargs");
2486 } else {
2487 string ctype;
2488 bool no_array_length;
2489 bool array_null_terminated;
2490 var type = parse_type (out ctype, out array_length_idx, transfer != "container", out no_array_length, out array_null_terminated);
2491 if (transfer == "full" || transfer == "container" || destroy != null) {
2492 type.value_owned = true;
2494 if (nullable == "1" || (allow_none == "1" && direction != "out")) {
2495 type.nullable = true;
2498 bool changed;
2499 type = element_get_type (type, direction == "out" || direction == "inout", ref no_array_length, ref array_null_terminated, out changed);
2500 if (!changed) {
2501 // discard ctype, duplicated information
2502 ctype = null;
2505 param = new Parameter (name, type, get_current_src ());
2506 if (ctype != null) {
2507 param.set_attribute_string ("CCode", "type", ctype);
2509 if (direction == "out") {
2510 param.direction = ParameterDirection.OUT;
2511 } else if (direction == "inout") {
2512 param.direction = ParameterDirection.REF;
2514 if (type is ArrayType) {
2515 if (metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
2516 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
2517 } else {
2518 if (no_array_length || array_null_terminated) {
2519 param.set_attribute_bool ("CCode", "array_length", !no_array_length);
2521 if (array_null_terminated) {
2522 param.set_attribute_bool ("CCode", "array_null_terminated", array_null_terminated);
2526 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
2528 // empty tuple used for parameters without initializer
2529 if (param.initializer is Tuple) {
2530 param.initializer = null;
2533 end_element (element_type);
2534 return param;
2537 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) {
2538 bool is_array = false;
2539 string type_name = reader.get_attribute ("name");
2540 ctype = null;
2542 var fixed_length = -1;
2543 array_length_idx = -1;
2544 no_array_length = true;
2545 array_null_terminated = true;
2547 if (reader.name == "array") {
2548 is_array = true;
2549 start_element ("array");
2551 var src = get_current_src ();
2553 if (type_name == null) {
2554 if (reader.get_attribute ("length") != null) {
2555 array_length_idx = int.parse (reader.get_attribute ("length"));
2556 no_array_length = false;
2557 array_null_terminated = false;
2559 if (reader.get_attribute ("fixed-size") != null) {
2560 fixed_length = int.parse (reader.get_attribute ("fixed-size"));
2561 array_null_terminated = false;
2563 if (reader.get_attribute ("c:type") == "GStrv") {
2564 no_array_length = true;
2565 array_null_terminated = true;
2567 if (reader.get_attribute ("zero-terminated") != null) {
2568 array_null_terminated = int.parse (reader.get_attribute ("zero-terminated")) != 0;
2570 next ();
2571 var element_type = parse_type ();
2572 element_type.value_owned = transfer_elements;
2573 end_element ("array");
2575 var array_type = new ArrayType (element_type, 1, src);
2576 if (fixed_length > 0) {
2577 array_type.fixed_length = true;
2578 array_type.length = new IntegerLiteral (fixed_length.to_string ());
2580 return array_type;
2582 } else if (reader.name == "callback"){
2583 parse_callback ();
2584 return new DelegateType ((Delegate) old_current.symbol);
2585 } else {
2586 start_element ("type");
2589 ctype = reader.get_attribute("c:type");
2591 next ();
2593 if (type_name == "GLib.PtrArray"
2594 && current_token == MarkupTokenType.START_ELEMENT) {
2595 type_name = "GLib.GenericArray";
2598 if (type_name == null) {
2599 type_name = ctype;
2602 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
2604 // type arguments / element types
2605 while (current_token == MarkupTokenType.START_ELEMENT) {
2606 if (type_name == "GLib.ByteArray") {
2607 skip_element ();
2608 continue;
2610 var element_type = parse_type ();
2611 element_type.value_owned = transfer_elements;
2612 type.add_type_argument (element_type);
2613 unresolved_type_arguments.add (element_type);
2616 end_element (is_array ? "array" : "type");
2617 return type;
2620 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
2621 no_array_length = false;
2622 array_null_terminated = false;
2624 DataType? type = null;
2625 if (type_name == "none") {
2626 type = new VoidType (get_current_src ());
2627 } else if (type_name == "gpointer") {
2628 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
2629 } else if (type_name == "GObject.Strv") {
2630 var element_type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string"));
2631 element_type.value_owned = true;
2632 type = new ArrayType (element_type, 1, get_current_src ());
2633 no_array_length = true;
2634 array_null_terminated = true;
2635 } else {
2636 bool known_type = true;
2637 if (type_name == "utf8") {
2638 type_name = "string";
2639 } else if (type_name == "gboolean") {
2640 type = new BooleanType ((Struct) context.root.scope.lookup ("bool"));
2641 } else if (type_name == "gchar") {
2642 type_name = "char";
2643 } else if (type_name == "gshort") {
2644 type_name = "short";
2645 } else if (type_name == "gushort") {
2646 type_name = "ushort";
2647 } else if (type_name == "gint") {
2648 type_name = "int";
2649 } else if (type_name == "guint") {
2650 type_name = "uint";
2651 } else if (type_name == "glong") {
2652 if (ctype != null && ctype.has_prefix ("gssize")) {
2653 type_name = "ssize_t";
2654 } else if (ctype != null && ctype.has_prefix ("gintptr")) {
2655 type_name = "intptr";
2656 } else {
2657 type_name = "long";
2659 } else if (type_name == "gulong") {
2660 if (ctype != null && ctype.has_prefix ("gsize")) {
2661 type_name = "size_t";
2662 } else if (ctype != null && ctype.has_prefix ("guintptr")) {
2663 type_name = "uintptr";
2664 } else {
2665 type_name = "ulong";
2667 } else if (type_name == "gint8") {
2668 type_name = "int8";
2669 } else if (type_name == "guint8") {
2670 type_name = "uint8";
2671 } else if (type_name == "gint16") {
2672 type_name = "int16";
2673 } else if (type_name == "guint16") {
2674 type_name = "uint16";
2675 } else if (type_name == "gint32") {
2676 type_name = "int32";
2677 } else if (type_name == "guint32") {
2678 type_name = "uint32";
2679 } else if (type_name == "gint64") {
2680 type_name = "int64";
2681 } else if (type_name == "guint64") {
2682 type_name = "uint64";
2683 } else if (type_name == "gfloat") {
2684 type_name = "float";
2685 } else if (type_name == "gdouble") {
2686 type_name = "double";
2687 } else if (type_name == "filename") {
2688 type_name = "string";
2689 } else if (type_name == "GLib.offset") {
2690 type_name = "int64";
2691 } else if (type_name == "gsize") {
2692 type_name = "size_t";
2693 } else if (type_name == "gssize") {
2694 type_name = "ssize_t";
2695 } else if (type_name == "guintptr") {
2696 type_name = "uintptr";
2697 } else if (type_name == "gintptr") {
2698 type_name = "intptr";
2699 } else if (type_name == "GType") {
2700 type_name = "GLib.Type";
2701 } else if (type_name == "GLib.String") {
2702 type_name = "GLib.StringBuilder";
2703 } else if (type_name == "GObject.Class") {
2704 type_name = "GLib.ObjectClass";
2705 } else if (type_name == "gunichar") {
2706 type_name = "unichar";
2707 } else if (type_name == "GLib.Data") {
2708 type_name = "GLib.Datalist";
2709 } else if (type_name == "Atk.ImplementorIface") {
2710 type_name = "Atk.Implementor";
2711 } else {
2712 known_type = false;
2715 if (type == null) {
2716 var sym = parse_symbol_from_string (type_name, get_current_src ());
2717 type = new UnresolvedType.from_symbol (sym, get_current_src ());
2718 if (!known_type) {
2719 unresolved_gir_symbols.add (sym);
2724 return type;
2727 void parse_record () {
2728 start_element ("record");
2729 push_node (element_get_name (), true);
2731 Struct st;
2732 if (current.new_symbol) {
2733 st = new Struct (element_get_name (), current.source_reference);
2734 current.symbol = st;
2735 } else {
2736 st = (Struct) current.symbol;
2739 set_type_id_ccode (st);
2741 st.external = true;
2742 st.access = SymbolAccessibility.PUBLIC;
2744 var gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
2745 if (gtype_struct_for != null) {
2746 current.gtype_struct_for = parse_symbol_from_string (gtype_struct_for, current.source_reference);
2747 unresolved_gir_symbols.add (current.gtype_struct_for);
2750 bool first_field = true;
2751 next ();
2753 st.comment = parse_symbol_doc ();
2755 while (current_token == MarkupTokenType.START_ELEMENT) {
2756 if (!push_metadata ()) {
2757 if (first_field && reader.name == "field") {
2758 first_field = false;
2760 skip_element ();
2761 continue;
2764 if (reader.name == "field") {
2765 if (reader.get_attribute ("name") != "priv" && !(first_field && gtype_struct_for != null)) {
2766 parse_field ();
2767 } else {
2768 skip_element ();
2770 first_field = false;
2771 } else if (reader.name == "constructor") {
2772 parse_constructor ();
2773 } else if (reader.name == "method") {
2774 parse_method ("method");
2775 } else if (reader.name == "function") {
2776 skip_element ();
2777 } else if (reader.name == "union") {
2778 parse_union ();
2779 } else {
2780 // error
2781 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
2782 skip_element ();
2785 pop_metadata ();
2788 pop_node ();
2789 end_element ("record");
2792 void parse_class () {
2793 start_element ("class");
2794 push_node (element_get_name (), true);
2796 Class cl;
2797 var parent = reader.get_attribute ("parent");
2798 if (current.new_symbol) {
2799 cl = new Class (current.name, current.source_reference);
2800 cl.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT, reader.get_attribute ("abstract") == "1");
2802 if (parent != null) {
2803 cl.add_base_type (parse_type_from_gir_name (parent));
2805 current.symbol = cl;
2806 } else {
2807 cl = (Class) current.symbol;
2810 set_type_id_ccode (cl);
2812 cl.access = SymbolAccessibility.PUBLIC;
2813 cl.external = true;
2815 next ();
2817 cl.comment = parse_symbol_doc ();
2819 var first_field = true;
2820 while (current_token == MarkupTokenType.START_ELEMENT) {
2821 if (!push_metadata ()) {
2822 if (first_field && reader.name == "field") {
2823 first_field = false;
2825 skip_element ();
2826 continue;
2829 if (reader.name == "implements") {
2830 start_element ("implements");
2831 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
2832 next ();
2833 end_element ("implements");
2834 } else if (reader.name == "constant") {
2835 parse_constant ();
2836 } else if (reader.name == "field") {
2837 if (first_field && parent != null) {
2838 // first field is guaranteed to be the parent instance
2839 skip_element ();
2840 } else {
2841 if (reader.get_attribute ("name") != "priv") {
2842 parse_field ();
2843 } else {
2844 skip_element ();
2847 first_field = false;
2848 } else if (reader.name == "property") {
2849 parse_property ();
2850 } else if (reader.name == "constructor") {
2851 parse_constructor ();
2852 } else if (reader.name == "function") {
2853 parse_method ("function");
2854 } else if (reader.name == "method") {
2855 parse_method ("method");
2856 } else if (reader.name == "virtual-method") {
2857 parse_method ("virtual-method");
2858 } else if (reader.name == "union") {
2859 parse_union ();
2860 } else if (reader.name == "glib:signal") {
2861 parse_signal ();
2862 } else {
2863 // error
2864 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2865 skip_element ();
2868 pop_metadata ();
2871 pop_node ();
2872 end_element ("class");
2875 void parse_interface () {
2876 start_element ("interface");
2877 push_node (element_get_name (), true);
2879 Interface iface;
2880 if (current.new_symbol) {
2881 iface = new Interface (current.name, current.source_reference);
2882 current.symbol = iface;
2883 } else {
2884 iface = (Interface) current.symbol;
2887 set_type_id_ccode (iface);
2889 iface.access = SymbolAccessibility.PUBLIC;
2890 iface.external = true;
2893 next ();
2895 iface.comment = parse_symbol_doc ();
2897 while (current_token == MarkupTokenType.START_ELEMENT) {
2898 if (!push_metadata ()) {
2899 skip_element ();
2900 continue;
2903 if (reader.name == "prerequisite") {
2904 start_element ("prerequisite");
2905 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2906 next ();
2907 end_element ("prerequisite");
2908 } else if (reader.name == "field") {
2909 parse_field ();
2910 } else if (reader.name == "property") {
2911 parse_property ();
2912 } else if (reader.name == "virtual-method") {
2913 parse_method ("virtual-method");
2914 } else if (reader.name == "function") {
2915 parse_method ("function");
2916 } else if (reader.name == "method") {
2917 parse_method ("method");
2918 } else if (reader.name == "glib:signal") {
2919 parse_signal ();
2920 } else {
2921 // error
2922 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2923 skip_element ();
2926 pop_metadata ();
2929 pop_node ();
2930 end_element ("interface");
2933 void parse_field () {
2934 start_element ("field");
2935 push_node (element_get_name (), false);
2937 string nullable = reader.get_attribute ("nullable");
2938 string allow_none = reader.get_attribute ("allow-none");
2939 next ();
2941 var comment = parse_symbol_doc ();
2943 bool no_array_length;
2944 bool array_null_terminated;
2945 int array_length_idx;
2946 var type = parse_type (null, out array_length_idx, true, out no_array_length, out array_null_terminated);
2947 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2949 string name = current.name;
2950 string cname = current.girdata["name"];
2952 var field = new Field (name, type, null, current.source_reference);
2953 field.access = SymbolAccessibility.PUBLIC;
2954 field.comment = comment;
2955 if (name != cname) {
2956 field.set_attribute_string ("CCode", "cname", cname);
2958 if (type is ArrayType) {
2959 if (!no_array_length && array_length_idx > -1) {
2960 current.array_length_idx = array_length_idx;
2962 if (no_array_length || array_null_terminated) {
2963 field.set_attribute_bool ("CCode", "array_length", !no_array_length);
2965 if (array_null_terminated) {
2966 field.set_attribute_bool ("CCode", "array_null_terminated", true);
2969 if (nullable == "1" || allow_none == "1") {
2970 type.nullable = true;
2972 current.symbol = field;
2974 pop_node ();
2975 end_element ("field");
2978 Property parse_property () {
2979 start_element ("property");
2980 push_node (element_get_name().replace ("-", "_"), false);
2981 bool is_abstract = metadata.get_bool (ArgumentType.ABSTRACT, current.parent.symbol is Interface);
2982 string transfer = reader.get_attribute ("transfer-ownership");
2984 next ();
2986 var comment = parse_symbol_doc ();
2988 bool no_array_length;
2989 bool array_null_terminated;
2990 var type = parse_type (null, null, transfer != "container", out no_array_length, out array_null_terminated);
2991 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2992 var prop = new Property (current.name, type, null, null, current.source_reference);
2993 prop.comment = comment;
2994 prop.access = SymbolAccessibility.PUBLIC;
2995 prop.external = true;
2996 prop.is_abstract = is_abstract;
2997 if (no_array_length || array_null_terminated) {
2998 prop.set_attribute_bool ("CCode", "array_length", !no_array_length);
3000 if (array_null_terminated) {
3001 prop.set_attribute_bool ("CCode", "array_null_terminated", true);
3003 current.symbol = prop;
3005 pop_node ();
3006 end_element ("property");
3007 return prop;
3010 void parse_callback () {
3011 parse_function ("callback");
3014 void parse_constructor () {
3015 parse_function ("constructor");
3018 class ParameterInfo {
3019 public ParameterInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx, bool is_async = false) {
3020 this.param = param;
3021 this.array_length_idx = array_length_idx;
3022 this.closure_idx = closure_idx;
3023 this.destroy_idx = destroy_idx;
3024 this.vala_idx = 0.0F;
3025 this.keep = true;
3026 this.is_async = is_async;
3029 public Parameter param;
3030 public float vala_idx;
3031 public int array_length_idx;
3032 public int closure_idx;
3033 public int destroy_idx;
3034 public bool keep;
3035 public bool is_async;
3038 void parse_function (string element_name) {
3039 start_element (element_name);
3040 push_node (element_get_name (reader.get_attribute ("invoker")).replace ("-", "_"), false);
3042 string symbol_type;
3043 if (metadata.has_argument (ArgumentType.SYMBOL_TYPE)) {
3044 symbol_type = metadata.get_string (ArgumentType.SYMBOL_TYPE);
3045 } else {
3046 symbol_type = element_name;
3049 string name = current.name;
3050 string throws_string = reader.get_attribute ("throws");
3051 string invoker = reader.get_attribute ("invoker");
3053 next ();
3055 var comment = parse_symbol_doc ();
3057 DataType return_type;
3058 string return_ctype = null;
3059 int return_array_length_idx = -1;
3060 bool return_no_array_length = false;
3061 bool return_array_null_terminated = false;
3062 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
3063 Comment? return_comment;
3064 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);
3065 if (return_comment != null) {
3066 if (comment == null) {
3067 comment = new GirComment (null, current.source_reference);
3069 comment.return_content = return_comment;
3071 } else {
3072 return_type = new VoidType ();
3075 Symbol s;
3077 if (symbol_type == "callback") {
3078 s = new Delegate (name, return_type, current.source_reference);
3079 ((Delegate) s).has_target = false;
3080 } else if (symbol_type == "constructor") {
3081 if (name == "new") {
3082 name = null;
3083 } else if (name.has_prefix ("new_")) {
3084 name = name.substring ("new_".length);
3086 var m = new CreationMethod (null, name, current.source_reference);
3087 m.has_construct_function = false;
3089 if (name != null && !current.name.has_prefix ("new_")) {
3090 m.set_attribute_string ("CCode", "cname", current.girdata["c:identifier"]);
3093 string parent_ctype = null;
3094 if (current.parent.symbol is Class) {
3095 parent_ctype = current.parent.get_cname ();
3097 if (return_ctype != null && (parent_ctype == null || return_ctype != parent_ctype + "*")) {
3098 m.set_attribute_string ("CCode", "type", return_ctype);
3100 s = m;
3101 } else if (symbol_type == "glib:signal") {
3102 s = new Signal (name, return_type, current.source_reference);
3103 } else {
3104 s = new Method (name, return_type, current.source_reference);
3107 s.access = SymbolAccessibility.PUBLIC;
3108 s.comment = comment;
3109 s.external = true;
3111 if (s is Signal) {
3112 if (current.girdata["name"] != name.replace ("_", "-")) {
3113 s.set_attribute_string ("CCode", "cname", current.girdata["name"]);
3117 if (s is Method) {
3118 var m = (Method) s;
3119 if (symbol_type == "virtual-method" || symbol_type == "callback") {
3120 if (current.parent.symbol is Interface) {
3121 m.is_abstract = true;
3122 } else {
3123 m.is_virtual = true;
3125 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
3126 s.set_attribute ("NoWrapper", true, s.source_reference);
3127 } if (current.girdata["name"] != name) {
3128 m.set_attribute_string ("CCode", "vfunc_name", current.girdata["name"]);
3130 } else if (symbol_type == "function") {
3131 m.binding = MemberBinding.STATIC;
3133 if (metadata.has_argument (ArgumentType.FLOATING)) {
3134 m.returns_floating_reference = metadata.get_bool (ArgumentType.FLOATING);
3135 m.return_type.value_owned = true;
3139 if (s is Method && !(s is CreationMethod)) {
3140 var method = (Method) s;
3141 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
3142 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
3143 method.is_abstract = false;
3144 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
3145 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
3146 method.is_virtual = false;
3148 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
3149 method.set_attribute_string ("CCode", "vfunc_name", metadata.get_string (ArgumentType.VFUNC_NAME));
3150 method.is_virtual = true;
3152 if (metadata.has_argument (ArgumentType.FINISH_VFUNC_NAME)) {
3153 method.set_attribute_string ("CCode", "finish_vfunc_name", metadata.get_string (ArgumentType.FINISH_VFUNC_NAME));
3154 method.is_virtual = true;
3158 if (!(metadata.get_expression (ArgumentType.THROWS) is NullLiteral)) {
3159 if (metadata.has_argument (ArgumentType.THROWS)) {
3160 var error_types = metadata.get_string(ArgumentType.THROWS).split(",");
3161 foreach (var error_type in error_types) {
3162 s.add_error_type (parse_type_from_string (error_type, true, metadata.get_source_reference (ArgumentType.THROWS)));
3164 } else if (throws_string == "1") {
3165 s.add_error_type (new ErrorType (null, null));
3169 if (s is Method) {
3170 var m = (Method) s;
3171 m.set_attribute ("PrintfFormat", metadata.get_bool (ArgumentType.PRINTF_FORMAT));
3172 if (metadata.has_argument (ArgumentType.SENTINEL)) {
3173 m.set_attribute_string ("CCode", "sentinel", metadata.get_string (ArgumentType.SENTINEL));
3177 if (return_type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
3178 return_array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
3179 } else {
3180 if (return_no_array_length || return_array_null_terminated) {
3181 s.set_attribute_bool ("CCode", "array_length", !return_no_array_length);
3183 if (return_array_null_terminated) {
3184 s.set_attribute_bool ("CCode", "array_null_terminated", true);
3187 current.return_array_length_idx = return_array_length_idx;
3189 current.symbol = s;
3191 if (metadata.has_argument (ArgumentType.FINISH_NAME)) {
3192 s.set_attribute_string ("CCode", "finish_name", metadata.get_string (ArgumentType.FINISH_NAME));
3194 if (metadata.has_argument (ArgumentType.FINISH_INSTANCE)) {
3195 s.set_attribute_bool ("CCode", "finish_instance", metadata.get_bool (ArgumentType.FINISH_INSTANCE));
3198 int instance_idx = -2;
3199 if (element_name == "function" && symbol_type == "method") {
3200 if (metadata.has_argument (ArgumentType.INSTANCE_IDX)) {
3201 instance_idx = metadata.get_integer (ArgumentType.INSTANCE_IDX);
3202 if (instance_idx != 0) {
3203 s.set_attribute_double ("CCode", "instance_pos", instance_idx + 0.5);
3205 } else {
3206 Report.error (get_current_src (), "instance_idx required when converting function to method");
3210 var parameters = new ArrayList<ParameterInfo> ();
3211 current.array_length_parameters = new ArrayList<int> ();
3212 current.closure_parameters = new ArrayList<int> ();
3213 current.destroy_parameters = new ArrayList<int> ();
3214 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
3215 start_element ("parameters");
3216 next ();
3218 var current_parameter_idx = -1;
3219 while (current_token == MarkupTokenType.START_ELEMENT) {
3220 current_parameter_idx++;
3222 if (reader.name == "instance-parameter" &&
3223 !(symbol_type == "function" || symbol_type == "constructor")) {
3224 skip_element ();
3225 continue;
3228 if (instance_idx > -2 && instance_idx == current_parameter_idx) {
3229 skip_element ();
3230 continue;
3233 if (!push_metadata ()) {
3234 skip_element ();
3235 continue;
3238 int array_length_idx, closure_idx, destroy_idx;
3239 string scope;
3240 string default_param_name = null;
3241 Comment? param_comment;
3242 default_param_name = "arg%d".printf (parameters.size);
3243 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, out param_comment, default_param_name);
3244 if (array_length_idx != -1) {
3245 if (instance_idx > -2 && instance_idx < array_length_idx) {
3246 array_length_idx--;
3248 current.array_length_parameters.add (array_length_idx);
3250 if (closure_idx != -1) {
3251 if (instance_idx > -2 && instance_idx < closure_idx) {
3252 closure_idx--;
3254 if (current.closure_parameters.index_of (current_parameter_idx) < 0) {
3255 current.closure_parameters.add (closure_idx);
3258 if (destroy_idx != -1) {
3259 if (instance_idx > -2 && instance_idx < destroy_idx) {
3260 destroy_idx--;
3262 if (current.destroy_parameters.index_of (current_parameter_idx) < 0) {
3263 current.destroy_parameters.add (destroy_idx);
3266 if (param_comment != null) {
3267 if (comment == null) {
3268 comment = new GirComment (null, s.source_reference);
3269 s.comment = comment;
3272 comment.add_content_for_parameter ((param.ellipsis)? "..." : param.name, param_comment);
3275 var info = new ParameterInfo (param, array_length_idx, closure_idx, destroy_idx, scope == "async");
3277 if (s is Method && scope == "async") {
3278 var unresolved_type = param.variable_type as UnresolvedType;
3279 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
3280 // GAsync-style method
3281 ((Method) s).coroutine = true;
3282 info.keep = false;
3286 parameters.add (info);
3287 pop_metadata ();
3289 end_element ("parameters");
3291 current.parameters = parameters;
3293 for (int param_n = parameters.size - 1 ; param_n >= 0 ; param_n--) {
3294 ParameterInfo pi = parameters[param_n];
3295 if (!pi.param.ellipsis && pi.param.initializer == null) {
3296 string type_string = pi.param.variable_type.to_string ();
3297 if (type_string == "Gio.Cancellable?") {
3298 pi.param.initializer = new Vala.NullLiteral ();
3299 } else {
3300 break;
3305 pop_node ();
3306 end_element (element_name);
3309 void parse_method (string element_name) {
3310 parse_function (element_name);
3313 void parse_signal () {
3314 parse_function ("glib:signal");
3317 void parse_boxed (string element_name) {
3318 start_element (element_name);
3319 string name = reader.get_attribute ("name");
3320 if (name == null) {
3321 name = reader.get_attribute ("glib:name");
3323 push_node (element_get_name (name), true);
3325 Class cl;
3326 bool require_copy_free = false;
3327 if (current.new_symbol) {
3328 cl = new Class (current.name, current.source_reference);
3329 cl.is_compact = true;
3330 current.symbol = cl;
3331 } else {
3332 cl = (Class) current.symbol;
3335 set_type_id_ccode (cl);
3336 require_copy_free = cl.has_attribute_argument ("CCode", "type_id");
3338 cl.access = SymbolAccessibility.PUBLIC;
3339 cl.external = true;
3341 if (metadata.has_argument (ArgumentType.BASE_TYPE)) {
3342 cl.add_base_type (parse_type_from_string (metadata.get_string (ArgumentType.BASE_TYPE), true, metadata.get_source_reference (ArgumentType.BASE_TYPE)));
3345 next ();
3347 cl.comment = parse_symbol_doc ();
3349 Node? ref_method = null;
3350 Node? unref_method = null;
3352 while (current_token == MarkupTokenType.START_ELEMENT) {
3353 if (!push_metadata ()) {
3354 skip_element ();
3355 continue;
3358 if (reader.name == "field") {
3359 parse_field ();
3360 } else if (reader.name == "constructor") {
3361 parse_constructor ();
3362 } else if (reader.name == "method") {
3363 parse_method ("method");
3364 var cname = old_current.get_cname ();
3365 if (cname.has_suffix ("_ref") && (ref_method == null || old_current.name == "ref")) {
3366 ref_method = old_current;
3367 } else if (cname.has_suffix ("_unref") && (unref_method == null || old_current.name == "unref")) {
3368 unref_method = old_current;
3370 } else if (reader.name == "function") {
3371 skip_element ();
3372 } else if (reader.name == "union") {
3373 parse_union ();
3374 } else {
3375 // error
3376 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
3377 skip_element ();
3380 pop_metadata ();
3383 // Add ccode-attributes for ref/unref methodes if available
3384 // otherwise fallback to default g_boxed_copy/free
3385 if (cl.has_attribute_argument ("CCode", "ref_function") || cl.has_attribute_argument ("CCode", "unref_function")
3386 || cl.has_attribute_argument ("CCode", "copy_function") || cl.has_attribute_argument ("CCode", "free_function")) {
3387 //do nothing
3388 } else if (ref_method != null && unref_method != null) {
3389 cl.set_attribute_string ("CCode", "ref_function", ref_method.get_cname ());
3390 cl.set_attribute_string ("CCode", "unref_function", unref_method.get_cname ());
3391 } else if (require_copy_free) {
3392 cl.set_attribute_string ("CCode", "copy_function", "g_boxed_copy");
3393 cl.set_attribute_string ("CCode", "free_function", "g_boxed_free");
3396 pop_node ();
3397 end_element (element_name);
3400 void parse_union () {
3401 start_element ("union");
3403 string? element_name = element_get_name ();
3404 if (element_name == null) {
3405 next ();
3407 while (current_token == MarkupTokenType.START_ELEMENT) {
3408 if (!push_metadata ()) {
3409 skip_element ();
3410 continue;
3413 if (reader.name == "field") {
3414 parse_field ();
3415 } else {
3416 // error
3417 Report.error (get_current_src (), "unknown child element `%s' in `transparent union'".printf (reader.name));
3418 skip_element ();
3421 pop_metadata ();
3424 end_element ("union");
3425 return;
3428 push_node (element_name, true);
3430 Struct st;
3431 if (current.new_symbol) {
3432 st = new Struct (reader.get_attribute ("name"), current.source_reference);
3433 current.symbol = st;
3434 } else {
3435 st = (Struct) current.symbol;
3437 st.access = SymbolAccessibility.PUBLIC;
3438 st.external = true;
3440 next ();
3442 st.comment = parse_symbol_doc ();
3444 while (current_token == MarkupTokenType.START_ELEMENT) {
3445 if (!push_metadata ()) {
3446 skip_element ();
3447 continue;
3450 if (reader.name == "field") {
3451 parse_field ();
3452 } else if (reader.name == "constructor") {
3453 parse_constructor ();
3454 } else if (reader.name == "method") {
3455 parse_method ("method");
3456 } else if (reader.name == "function") {
3457 skip_element ();
3458 } else if (reader.name == "record") {
3459 parse_record ();
3460 } else {
3461 // error
3462 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
3463 skip_element ();
3466 pop_metadata ();
3469 pop_node ();
3470 end_element ("union");
3473 void parse_constant () {
3474 start_element ("constant");
3475 push_node (element_get_name (), false);
3477 next ();
3479 var comment = parse_symbol_doc ();
3481 var type = parse_type ();
3482 var c = new Constant (current.name, type, null, current.source_reference);
3483 current.symbol = c;
3484 c.access = SymbolAccessibility.PUBLIC;
3485 c.comment = comment;
3486 c.external = true;
3488 pop_node ();
3489 end_element ("constant");
3492 /* Reporting */
3493 void report_unused_metadata (Metadata metadata) {
3494 if (metadata == Metadata.empty) {
3495 return;
3498 if (metadata.args.size == 0 && metadata.children.size == 0) {
3499 Report.warning (metadata.source_reference, "empty metadata");
3500 return;
3503 foreach (var arg_type in metadata.args.get_keys ()) {
3504 var arg = metadata.args[arg_type];
3505 if (!arg.used) {
3506 // if metadata is used and argument is not, then it's a unexpected argument
3507 Report.warning (arg.source_reference, "argument never used");
3511 foreach (var child in metadata.children) {
3512 if (!child.used) {
3513 Report.warning (child.source_reference, "metadata never used");
3514 } else {
3515 report_unused_metadata (child);
3520 /* Post-parsing */
3522 void resolve_gir_symbols () {
3523 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
3524 foreach (var map_from in unresolved_gir_symbols) {
3525 while (map_from != null) {
3526 var map_to = unresolved_symbols_map[map_from];
3527 if (map_to != null) {
3528 // remap the original symbol to match the target
3529 map_from.inner = null;
3530 map_from.name = map_to.name;
3531 if (map_to is UnresolvedSymbol) {
3532 var umap_to = (UnresolvedSymbol) map_to;
3533 while (umap_to.inner != null) {
3534 umap_to = umap_to.inner;
3535 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
3536 map_from = map_from.inner;
3538 } else {
3539 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
3540 map_to = map_to.parent_symbol;
3541 map_from.inner = new UnresolvedSymbol (null, map_to.name);
3542 map_from = map_from.inner;
3545 break;
3547 map_from = map_from.inner;
3552 void create_new_namespaces () {
3553 foreach (var node in Node.new_namespaces) {
3554 if (node.symbol == null) {
3555 node.symbol = new Namespace (node.name, node.source_reference);
3560 void resolve_type_arguments () {
3561 // box structs in type arguments
3562 foreach (var element_type in unresolved_type_arguments) {
3563 TypeSymbol sym = null;
3564 if (element_type is UnresolvedType) {
3565 sym = (TypeSymbol) resolve_symbol (root, ((UnresolvedType) element_type).unresolved_symbol);
3566 } else if (element_type.data_type != null) {
3567 sym = element_type.data_type;
3569 var st = sym as Struct;
3570 if (st != null && !st.is_integer_type () && !st.is_floating_type ()) {
3571 element_type.nullable = true;
3576 void process_interface (Node iface_node) {
3577 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
3578 ensure we have at least one instantiable prerequisite */
3579 Interface iface = (Interface) iface_node.symbol;
3580 bool has_instantiable_prereq = false;
3581 foreach (DataType prereq in iface.get_prerequisites ()) {
3582 Symbol sym = null;
3583 if (prereq is UnresolvedType) {
3584 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
3585 sym = resolve_symbol (iface_node.parent, unresolved_symbol);
3586 } else {
3587 sym = prereq.data_type;
3589 if (sym is Class) {
3590 has_instantiable_prereq = true;
3591 break;
3595 if (!has_instantiable_prereq) {
3596 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
3600 void process_alias (Node alias) {
3601 /* this is unfortunate because <alias> tag has no type information, thus we have
3602 to guess it from the base type */
3603 DataType base_type = null;
3604 Symbol type_sym = null;
3605 Node base_node = null;
3606 bool simple_type = false;
3607 if (alias.base_type is UnresolvedType) {
3608 base_type = alias.base_type;
3609 base_node = resolve_node (alias.parent, ((UnresolvedType) base_type).unresolved_symbol);
3610 if (base_node != null) {
3611 type_sym = base_node.symbol;
3613 } else if (alias.base_type is PointerType && ((PointerType) alias.base_type).base_type is VoidType) {
3614 // gpointer, if it's a struct make it a simpletype
3615 simple_type = true;
3616 } else {
3617 base_type = alias.base_type;
3618 type_sym = base_type.data_type;
3619 if (type_sym != null) {
3620 base_node = resolve_node (alias.parent, parse_symbol_from_string (type_sym.get_full_name (), alias.source_reference));
3624 if (type_sym is Struct && ((Struct) type_sym).is_simple_type ()) {
3625 simple_type = true;
3628 if (base_type == null || type_sym == null || type_sym is Struct) {
3629 var st = new Struct (alias.name, alias.source_reference);
3630 st.access = SymbolAccessibility.PUBLIC;
3631 if (base_type != null) {
3632 // threat target="none" as a new struct
3633 st.base_type = base_type;
3635 st.comment = alias.comment;
3636 st.external = true;
3637 st.set_simple_type (simple_type);
3638 alias.symbol = st;
3639 } else if (type_sym is Class) {
3640 var cl = new Class (alias.name, alias.source_reference);
3641 cl.access = SymbolAccessibility.PUBLIC;
3642 if (base_type != null) {
3643 cl.add_base_type (base_type);
3645 cl.comment = alias.comment;
3646 cl.external = true;
3647 cl.is_compact = ((Class) type_sym).is_compact;
3648 alias.symbol = cl;
3649 } else if (type_sym is Interface) {
3650 // this is not a correct alias, but what can we do otherwise?
3651 var iface = new Interface (alias.name, alias.source_reference);
3652 iface.access = SymbolAccessibility.PUBLIC;
3653 if (base_type != null) {
3654 iface.add_prerequisite (base_type);
3656 iface.comment = alias.comment;
3657 iface.external = true;
3658 alias.symbol = iface;
3659 } else if (type_sym is Delegate) {
3660 var orig = (Delegate) type_sym;
3661 if (base_node != null) {
3662 base_node.process (this);
3663 orig = (Delegate) base_node.symbol;
3666 var deleg = new Delegate (alias.name, orig.return_type.copy (), alias.source_reference);
3667 deleg.access = orig.access;
3669 foreach (var param in orig.get_parameters ()) {
3670 deleg.add_parameter (param.copy ());
3673 foreach (var error_type in orig.get_error_types ()) {
3674 deleg.add_error_type (error_type.copy ());
3677 foreach (var attribute in orig.attributes) {
3678 deleg.attributes.append (attribute);
3681 deleg.external = true;
3683 alias.symbol = deleg;
3686 // inherit atributes, like type_id
3687 if (type_sym is Class || (type_sym is Struct && !simple_type)) {
3688 if (type_sym.has_attribute_argument ("CCode", "has_type_id")) {
3689 alias.symbol.set_attribute_bool ("CCode", "has_type_id", type_sym.get_attribute_bool ("CCode", "has_type_id"));
3690 } else if (type_sym.has_attribute_argument ("CCode", "type_id")) {
3691 alias.symbol.set_attribute_string ("CCode", "type_id", type_sym.get_attribute_string ("CCode", "type_id"));
3696 void process_callable (Node node) {
3697 if (node.element_type == "alias" && node.symbol is Delegate) {
3698 // processed in parse_alias
3699 return;
3702 var s = node.symbol;
3703 List<ParameterInfo> parameters = node.parameters;
3705 DataType return_type = null;
3706 if (s is Callable) {
3707 return_type = ((Callable) s).return_type;
3710 if (return_type is ArrayType && node.return_array_length_idx >= 0) {
3711 if (node.return_array_length_idx >= parameters.size) {
3712 Report.error (return_type.source_reference, "invalid array length index");
3713 } else {
3714 parameters[node.return_array_length_idx].keep = false;
3715 node.array_length_parameters.add (node.return_array_length_idx);
3717 } else if (return_type is VoidType && parameters.size > 0) {
3718 int n_out_parameters = 0;
3719 foreach (var info in parameters) {
3720 if (info.param.direction == ParameterDirection.OUT) {
3721 n_out_parameters++;
3725 if (n_out_parameters == 1) {
3726 ParameterInfo last_param = parameters[parameters.size-1];
3727 if (last_param.param.direction == ParameterDirection.OUT) {
3728 // use last out real-non-null-struct parameter as return type
3729 if (last_param.param.variable_type is UnresolvedType) {
3730 var st = resolve_symbol (node.parent, ((UnresolvedType) last_param.param.variable_type).unresolved_symbol) as Struct;
3731 if (st != null && !st.is_simple_type () && !last_param.param.variable_type.nullable) {
3732 if (!node.metadata.get_bool (ArgumentType.RETURN_VOID, false)) {
3733 last_param.keep = false;
3734 return_type = last_param.param.variable_type.copy ();
3740 } else {
3741 if (return_type is UnresolvedType && !return_type.nullable) {
3742 var st = resolve_symbol (node.parent, ((UnresolvedType) return_type).unresolved_symbol) as Struct;
3743 if (st != null) {
3744 bool is_simple_type = false;
3745 Struct? base_st = st;
3747 while (base_st != null) {
3748 if (base_st.is_simple_type ()) {
3749 is_simple_type = true;
3750 break;
3753 if (base_st.base_type is UnresolvedType) {
3754 base_st = resolve_symbol (node.parent, ((UnresolvedType) base_st.base_type).unresolved_symbol) as Struct;
3755 } else {
3756 base_st = base_st.base_struct;
3760 if (!is_simple_type) {
3761 return_type.nullable = true;
3767 // Do not mark out-parameters as nullable if they are simple-types,
3768 // since it would result in a boxed-type in vala
3769 foreach (ParameterInfo info in parameters) {
3770 var type = info.param.variable_type;
3771 if (info.param.direction == ParameterDirection.OUT && type.nullable) {
3772 Struct? st = null;
3773 if (type is UnresolvedType) {
3774 st = resolve_symbol (node.parent, ((UnresolvedType) type).unresolved_symbol) as Struct;
3775 } else if (type is ValueType) {
3776 st = type.data_type as Struct;
3778 if (st != null && st.is_simple_type ()) {
3779 type.nullable = false;
3784 if (parameters.size > 1) {
3785 ParameterInfo last_param = parameters[parameters.size-1];
3786 if (last_param.param.ellipsis) {
3787 var first_vararg_param = parameters[parameters.size-2];
3788 if (first_vararg_param.param.name.has_prefix ("first_")) {
3789 first_vararg_param.keep = false;
3794 int i = 0, j=1;
3796 int last = -1;
3797 foreach (ParameterInfo info in parameters) {
3798 if (s is Delegate && info.closure_idx == i) {
3799 var d = (Delegate) s;
3800 d.has_target = true;
3801 d.set_attribute_double ("CCode", "instance_pos", j - 0.1);
3802 info.keep = false;
3803 } else if (info.keep
3804 && !node.array_length_parameters.contains (i)
3805 && !node.closure_parameters.contains (i)
3806 && !node.destroy_parameters.contains (i)) {
3807 info.vala_idx = (float) j;
3808 info.keep = true;
3810 /* interpolate for vala_idx between this and last*/
3811 float last_idx = 0.0F;
3812 if (last != -1) {
3813 last_idx = parameters[last].vala_idx;
3815 for (int k=last+1; k < i; k++) {
3816 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
3818 last = i;
3819 j++;
3820 } else {
3821 info.keep = false;
3822 // make sure that vala_idx is always set
3823 // the above if branch does not set vala_idx for
3824 // hidden parameters at the end of the parameter list
3825 info.vala_idx = (j - 1) + (i - last) * 0.1F;
3827 i++;
3830 foreach (ParameterInfo info in parameters) {
3831 if (!info.keep) {
3832 continue;
3835 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
3836 so do it first*/
3837 if (s is Callable) {
3838 ((Callable) s).add_parameter (info.param);
3841 if (info.array_length_idx != -1) {
3842 if ((info.array_length_idx) >= parameters.size) {
3843 Report.error (info.param.source_reference, "invalid array_length index");
3844 continue;
3846 set_array_ccode (info.param, parameters[info.array_length_idx]);
3849 if (info.closure_idx != -1) {
3850 if ((info.closure_idx) >= parameters.size) {
3851 Report.error (info.param.source_reference, "invalid closure index");
3852 continue;
3854 if ("%g".printf (parameters[info.closure_idx].vala_idx) != "%g".printf (info.vala_idx + 0.1)) {
3855 info.param.set_attribute_double ("CCode", "delegate_target_pos", parameters[info.closure_idx].vala_idx);
3858 if (info.destroy_idx != -1) {
3859 if (info.destroy_idx >= parameters.size) {
3860 Report.error (info.param.source_reference, "invalid destroy index");
3861 continue;
3863 if ("%g".printf (parameters[info.destroy_idx].vala_idx) != "%g".printf (info.vala_idx + 0.2)) {
3864 info.param.set_attribute_double ("CCode", "destroy_notify_pos", parameters[info.destroy_idx].vala_idx);
3868 if (info.is_async) {
3869 var resolved_type = info.param.variable_type;
3870 if (resolved_type is UnresolvedType) {
3871 var resolved_symbol = resolve_symbol (node.parent, ((UnresolvedType) resolved_type).unresolved_symbol);
3872 if (resolved_symbol is Delegate) {
3873 resolved_type = new DelegateType ((Delegate) resolved_symbol);
3877 if (resolved_type is DelegateType) {
3878 var d = ((DelegateType) resolved_type).delegate_symbol;
3879 if (!(d.name == "DestroyNotify" && d.parent_symbol.name == "GLib")) {
3880 info.param.set_attribute_string ("CCode", "scope", "async");
3881 info.param.variable_type.value_owned = (info.closure_idx != -1 && info.destroy_idx != -1);
3884 } else {
3885 var resolved_type = info.param.variable_type;
3886 if (resolved_type is DelegateType) {
3887 info.param.variable_type.value_owned = (info.closure_idx != -1 && info.destroy_idx != -1);
3892 if (return_type is ArrayType && node.return_array_length_idx >= 0) {
3893 set_array_ccode (s, parameters[node.return_array_length_idx]);
3896 if (s is Callable) {
3897 ((Callable) s).return_type = return_type;
3901 void find_parent (string cname, Node current, ref Node best, ref int match) {
3902 var old_best = best;
3903 if (current.symbol is Namespace) {
3904 foreach (var child in current.members) {
3905 // symbol is null only for aliases that aren't yet processed
3906 if ((child.symbol == null || is_container (child.symbol)) && cname.has_prefix (child.get_lower_case_cprefix ())) {
3907 find_parent (cname, child, ref best, ref match);
3911 if (best != old_best) {
3912 // child is better
3913 return;
3916 var current_match = current.get_lower_case_cprefix().length;
3917 if (current_match > match) {
3918 match = current_match;
3919 best = current;
3923 bool same_gir (Symbol gir_component, Symbol sym) {
3924 var gir_name = gir_component.source_reference.file.gir_namespace;
3925 var gir_version = gir_component.source_reference.file.gir_version;
3926 return "%s-%s".printf (gir_name, gir_version) in sym.source_reference.file.filename;
3929 void process_namespace_method (Node ns, Node node) {
3930 /* transform static methods into instance methods if possible.
3931 In most of cases this is a .gir fault we are going to fix */
3933 var ns_cprefix = ns.get_lower_case_cprefix ();
3934 var method = (Method) node.symbol;
3935 var cname = node.get_cname ();
3937 Parameter first_param = null;
3938 if (method.get_parameters ().size > 0) {
3939 first_param = method.get_parameters()[0];
3941 if (first_param != null && first_param.variable_type is UnresolvedType) {
3942 // check if it's a missed instance method (often happens for structs)
3943 var sym = ((UnresolvedType) first_param.variable_type).unresolved_symbol;
3944 var parent = resolve_node (ns, sym);
3945 if (parent != null && same_gir (method, parent.symbol) && parent.parent == ns && is_container (parent.symbol) && cname.has_prefix (parent.get_lower_case_cprefix ())) {
3946 // instance method
3947 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
3948 if (parent.lookup (new_name) == null) {
3949 ns.remove_member (node);
3950 node.name = new_name;
3951 method.name = new_name;
3952 method.get_parameters().remove_at (0);
3953 method.binding = MemberBinding.INSTANCE;
3954 parent.add_member (node);
3956 return;
3960 int match = 0;
3961 Node parent = ns;
3962 find_parent (cname, ns, ref parent, ref match);
3963 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
3964 if (same_gir (method, parent.symbol) && parent.lookup (new_name) == null) {
3965 ns.remove_member (node);
3966 node.name = new_name;
3967 method.name = new_name;
3968 parent.add_member (node);
3972 void process_virtual_method_field (Node node, Delegate d, UnresolvedSymbol gtype_struct_for) {
3973 var gtype_node = resolve_node (node.parent, gtype_struct_for);
3974 if (gtype_node == null || !(gtype_node.symbol is ObjectTypeSymbol)) {
3975 Report.error (gtype_struct_for.source_reference, "Unknown symbol `%s' for virtual method field `%s'".printf (gtype_struct_for.to_string (), node.to_string ()));
3977 var nodes = gtype_node.lookup_all (d.name);
3978 if (nodes == null) {
3979 return;
3981 foreach (var n in nodes) {
3982 if (node != n) {
3983 n.process (this);
3986 foreach (var n in nodes) {
3987 if (n.merged) {
3988 continue;
3990 var sym = n.symbol;
3991 if (sym is Signal) {
3992 var sig = (Signal) sym;
3993 sig.is_virtual = true;
3994 assume_parameter_names (sig, d, true);
3995 } else if (sym is Property) {
3996 var prop = (Property) sym;
3997 prop.is_virtual = true;
4002 void process_async_method (Node node) {
4003 var m = (Method) node.symbol;
4004 string finish_method_base;
4005 if (m.name == null) {
4006 assert (m is CreationMethod);
4007 finish_method_base = "new";
4008 } else if (m.name.has_suffix ("_async")) {
4009 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
4010 } else {
4011 finish_method_base = m.name;
4013 var finish_method_node = node.parent.lookup (finish_method_base + "_finish");
4015 // check if the method is using non-standard finish method name
4016 if (finish_method_node == null) {
4017 var method_cname = node.get_finish_cname ();
4018 foreach (var n in node.parent.members) {
4019 if (n.symbol is Method && n.get_cname () == method_cname) {
4020 finish_method_node = n;
4021 break;
4026 Method method = m;
4028 if (finish_method_node != null && finish_method_node.symbol is Method) {
4029 finish_method_node.process (this);
4030 var finish_method = (Method) finish_method_node.symbol;
4031 if (finish_method is CreationMethod) {
4032 method = new CreationMethod (((CreationMethod) finish_method).class_name, null, m.source_reference);
4033 method.access = m.access;
4034 method.binding = m.binding;
4035 method.external = true;
4036 method.coroutine = true;
4037 method.has_construct_function = finish_method.has_construct_function;
4039 // cannot use List.copy()
4040 // as it returns a list of unowned elements
4041 foreach (Attribute a in m.attributes) {
4042 method.attributes.append (a);
4045 method.set_attribute_string ("CCode", "cname", node.get_cname ());
4046 if (finish_method_base == "new") {
4047 method.name = null;
4048 } else if (finish_method_base.has_prefix ("new_")) {
4049 method.name = m.name.substring ("new_".length);
4051 foreach (var param in m.get_parameters ()) {
4052 method.add_parameter (param);
4054 node.symbol = method;
4055 } else {
4056 method.return_type = finish_method.return_type.copy ();
4057 var a = finish_method.get_attribute ("CCode");
4058 if (a != null && a.has_argument ("array_length")) {
4059 method.set_attribute_bool ("CCode", "array_length", a.get_bool ("array_length"));
4061 if (a != null && a.has_argument ("array_null_terminated")) {
4062 method.set_attribute_bool ("CCode", "array_null_terminated", a.get_bool ("array_null_terminated"));
4066 foreach (var param in finish_method.get_parameters ()) {
4067 if (param.direction == ParameterDirection.OUT) {
4068 var async_param = param.copy ();
4069 if (method.scope.lookup (param.name) != null) {
4070 // parameter name conflict
4071 async_param.name += "_out";
4073 method.add_parameter (async_param);
4077 foreach (DataType error_type in finish_method.get_error_types ()) {
4078 method.add_error_type (error_type.copy ());
4080 finish_method_node.processed = true;
4081 finish_method_node.merged = true;
4085 /* Hash and equal functions */
4087 static uint unresolved_symbol_hash (UnresolvedSymbol? sym) {
4088 var builder = new StringBuilder ();
4089 while (sym != null) {
4090 builder.append (sym.name);
4091 sym = sym.inner;
4093 return builder.str.hash ();
4096 static bool unresolved_symbol_equal (UnresolvedSymbol? sym1, UnresolvedSymbol? sym2) {
4097 while (sym1 != sym2) {
4098 if (sym1 == null || sym2 == null) {
4099 return false;
4101 if (sym1.name != sym2.name) {
4102 return false;
4104 sym1 = sym1.inner;
4105 sym2 = sym2.inner;
4107 return true;
4110 /* Helper methods */
4112 Node? base_interface_property (Node prop_node) {
4113 var cl = prop_node.parent.symbol as Class;
4114 if (cl == null) {
4115 return null;
4118 foreach (DataType type in cl.get_base_types ()) {
4119 if (!(type is UnresolvedType)) {
4120 continue;
4123 var base_node = resolve_node (prop_node.parent, ((UnresolvedType) type).unresolved_symbol);
4124 if (base_node != null && base_node.symbol is Interface) {
4125 var base_prop_node = base_node.lookup (prop_node.name);
4126 if (base_prop_node != null && base_prop_node.symbol is Property) {
4127 var base_property = (Property) base_prop_node.symbol;
4128 if (base_property.is_abstract || base_property.is_virtual) {
4129 // found
4130 return base_prop_node;
4136 return null;