glib-2.0: Add float.parse/try_parse()
[vala-gnome.git] / vala / valagirparser.vala
blob8d266917aab5caeb9f2829f1b772255326725e42
1 /* valagirparser.vala
3 * Copyright (C) 2008-2012 Jürg Billeter
4 * Copyright (C) 2011-2014 Luca Bruno
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 * Author:
21 * Jürg Billeter <j@bitron.ch>
22 * Luca Bruno <lucabru@src.gnome.org>
25 using GLib;
27 /**
28 * Code visitor parsing all GIR source files.
30 * Pipeline:
31 * 1) Parse metadata
32 * 2) Parse GIR with metadata, track unresolved GIR symbols, create Vala symbols
33 * 3) Reconciliate the tree by mapping tracked symbols
34 * 4) Process the tree
36 public class Vala.GirParser : CodeVisitor {
38 * Metadata parser
41 enum ArgumentType {
42 SKIP,
43 HIDDEN,
44 TYPE,
45 TYPE_ARGUMENTS,
46 CHEADER_FILENAME,
47 NAME,
48 OWNED,
49 UNOWNED,
50 PARENT,
51 NULLABLE,
52 DEPRECATED,
53 REPLACEMENT,
54 DEPRECATED_SINCE,
55 SINCE,
56 ARRAY,
57 ARRAY_LENGTH_IDX,
58 ARRAY_NULL_TERMINATED,
59 DEFAULT,
60 OUT,
61 REF,
62 VFUNC_NAME,
63 VIRTUAL,
64 ABSTRACT,
65 SCOPE,
66 STRUCT,
67 THROWS,
68 PRINTF_FORMAT,
69 ARRAY_LENGTH_FIELD,
70 SENTINEL,
71 CLOSURE,
72 DESTROY,
73 CPREFIX,
74 LOWER_CASE_CPREFIX,
75 ERRORDOMAIN,
76 DESTROYS_INSTANCE,
77 BASE_TYPE,
78 FINISH_NAME,
79 FINISH_INSTANCE,
80 SYMBOL_TYPE,
81 INSTANCE_IDX,
82 EXPERIMENTAL,
83 FLOATING,
84 TYPE_ID,
85 RETURN_VOID,
86 RETURNS_MODIFIED_POINTER,
87 DELEGATE_TARGET_CNAME,
88 FINISH_VFUNC_NAME,
89 NO_ACCESSOR_METHOD,
90 CNAME,
91 DELEGATE_TARGET;
93 public static ArgumentType? from_string (string name) {
94 var enum_class = (EnumClass) typeof(ArgumentType).class_ref ();
95 var nick = name.replace ("_", "-");
96 unowned GLib.EnumValue? enum_value = enum_class.get_value_by_nick (nick);
97 if (enum_value != null) {
98 ArgumentType value = (ArgumentType) enum_value.value;
99 return value;
101 return null;
105 class Argument {
106 public Expression expression;
107 public SourceReference source_reference;
109 public bool used = false;
111 public Argument (Expression expression, SourceReference? source_reference = null) {
112 this.expression = expression;
113 this.source_reference = source_reference;
117 class MetadataSet : Metadata {
118 public MetadataSet (string? selector = null) {
119 base ("", selector);
122 public void add_sibling (Metadata metadata) {
123 foreach (var child in metadata.children) {
124 add_child (child);
126 // merge arguments and take precedence
127 foreach (var key in metadata.args.get_keys ()) {
128 args[key] = metadata.args[key];
133 class Metadata {
134 private static Metadata _empty = null;
135 public static Metadata empty {
136 get {
137 if (_empty == null) {
138 _empty = new Metadata ("");
140 return _empty;
144 public PatternSpec pattern_spec;
145 public string? selector;
146 public SourceReference source_reference;
148 public bool used = false;
149 public Vala.Map<ArgumentType,Argument> args = new HashMap<ArgumentType,Argument> ();
150 public ArrayList<Metadata> children = new ArrayList<Metadata> ();
152 public Metadata (string pattern, string? selector = null, SourceReference? source_reference = null) {
153 this.pattern_spec = new PatternSpec (pattern);
154 this.selector = selector;
155 this.source_reference = source_reference;
158 public void add_child (Metadata metadata) {
159 children.add (metadata);
162 public Metadata match_child (string name, string? selector = null) {
163 var result = Metadata.empty;
164 foreach (var metadata in children) {
165 if ((selector == null || metadata.selector == null || metadata.selector == selector) && metadata.pattern_spec.match_string (name)) {
166 metadata.used = true;
167 if (result == Metadata.empty) {
168 // first match
169 result = metadata;
170 } else {
171 var ms = result as MetadataSet;
172 if (ms == null) {
173 // second match
174 ms = new MetadataSet (selector);
175 ms.add_sibling (result);
177 ms.add_sibling (metadata);
178 result = ms;
182 return result;
185 public void add_argument (ArgumentType key, Argument value) {
186 args.set (key, value);
189 public bool has_argument (ArgumentType key) {
190 return args.contains (key);
193 public Expression? get_expression (ArgumentType arg) {
194 var val = args.get (arg);
195 if (val != null) {
196 val.used = true;
197 return val.expression;
199 return null;
202 public string? get_string (ArgumentType arg) {
203 var lit = get_expression (arg) as StringLiteral;
204 if (lit != null) {
205 return lit.eval ();
207 return null;
210 public int get_integer (ArgumentType arg) {
211 var unary = get_expression (arg) as UnaryExpression;
212 if (unary != null && unary.operator == UnaryOperator.MINUS) {
213 var lit = unary.inner as IntegerLiteral;
214 if (lit != null) {
215 return -int.parse (lit.value);
217 } else {
218 var lit = get_expression (arg) as IntegerLiteral;
219 if (lit != null) {
220 return int.parse (lit.value);
224 return 0;
227 public bool get_bool (ArgumentType arg, bool default_value = false) {
228 var lit = get_expression (arg) as BooleanLiteral;
229 if (lit != null) {
230 return lit.value;
232 return default_value;
235 public SourceReference? get_source_reference (ArgumentType arg) {
236 var val = args.get (arg);
237 if (val != null) {
238 return val.source_reference;
240 return null;
244 class MetadataParser {
246 * Grammar:
247 * metadata ::= [ rule [ '\n' relativerule ]* ]
248 * rule ::= pattern ' ' [ args ]
249 * relativerule ::= '.' rule
250 * pattern ::= glob [ '#' selector ] [ '.' pattern ]
252 private Metadata tree = new Metadata ("");
253 private Scanner scanner;
254 private SourceLocation begin;
255 private SourceLocation end;
256 private SourceLocation old_end;
257 private TokenType current;
258 private Metadata parent_metadata;
260 public MetadataParser () {
261 tree.used = true;
264 SourceReference get_current_src () {
265 return new SourceReference (scanner.source_file, begin, end);
268 SourceReference get_src (SourceLocation begin, SourceLocation? end = null) {
269 var e = this.end;
270 if (end != null) {
271 e = end;
273 return new SourceReference (scanner.source_file, begin, e);
276 public Metadata parse_metadata (SourceFile metadata_file) {
277 scanner = new Scanner (metadata_file);
278 next ();
279 while (current != TokenType.EOF) {
280 if (!parse_rule ()) {
281 return Metadata.empty;
284 return tree;
287 TokenType next () {
288 old_end = end;
289 current = scanner.read_token (out begin, out end);
290 return current;
293 bool has_space () {
294 return old_end.pos != begin.pos;
297 bool has_newline () {
298 return old_end.line != begin.line;
301 string get_string (SourceLocation? begin = null, SourceLocation? end = null) {
302 var b = this.begin;
303 var e = this.end;
304 if (begin != null) {
305 b = begin;
307 if (end != null) {
308 e = end;
310 return ((string) b.pos).substring (0, (int) (e.pos - b.pos));
313 string? parse_identifier (bool is_glob) {
314 var begin = this.begin;
316 if (current == TokenType.DOT || current == TokenType.HASH) {
317 if (is_glob) {
318 Report.error (get_src (begin), "expected glob-style pattern");
319 } else {
320 Report.error (get_src (begin), "expected identifier");
322 return null;
325 if (is_glob) {
326 while (current != TokenType.EOF && current != TokenType.DOT && current != TokenType.HASH) {
327 next ();
328 if (has_space ()) {
329 break;
332 } else {
333 next ();
336 return get_string (begin, old_end);
339 string? parse_selector () {
340 if (current != TokenType.HASH || has_space ()) {
341 return null;
343 next ();
345 return parse_identifier (false);
348 Metadata? parse_pattern () {
349 Metadata metadata;
350 bool is_relative = false;
351 if (current == TokenType.IDENTIFIER || current == TokenType.STAR) {
352 // absolute pattern
353 parent_metadata = tree;
354 } else {
355 // relative pattern
356 if (current != TokenType.DOT) {
357 Report.error (get_current_src (), "expected pattern or `.', got %s".printf (current.to_string ()));
358 return null;
360 next ();
361 is_relative = true;
364 if (parent_metadata == null) {
365 Report.error (get_current_src (), "cannot determinate parent metadata");
366 return null;
369 SourceLocation begin = this.begin;
370 var pattern = parse_identifier (true);
371 if (pattern == null) {
372 return null;
374 metadata = new Metadata (pattern, parse_selector (), get_src (begin));
375 parent_metadata.add_child (metadata);
377 while (current != TokenType.EOF && !has_space ()) {
378 if (current != TokenType.DOT) {
379 Report.error (get_current_src (), "expected `.' got %s".printf (current.to_string ()));
380 break;
382 next ();
384 begin = this.begin;
385 pattern = parse_identifier (true);
386 if (pattern == null) {
387 return null;
389 var child = new Metadata (pattern, parse_selector (), get_src (begin, old_end));
390 metadata.add_child (child);
391 metadata = child;
393 if (!is_relative) {
394 parent_metadata = metadata;
397 return metadata;
400 Expression? parse_expression () {
401 var begin = this.begin;
402 var src = get_current_src ();
403 Expression expr = null;
404 switch (current) {
405 case TokenType.NULL:
406 expr = new NullLiteral (src);
407 break;
408 case TokenType.TRUE:
409 expr = new BooleanLiteral (true, src);
410 break;
411 case TokenType.FALSE:
412 expr = new BooleanLiteral (false, src);
413 break;
414 case TokenType.MINUS:
415 next ();
416 var inner = parse_expression ();
417 if (inner == null) {
418 Report.error (src, "expected expression after `-', got %s".printf (current.to_string ()));
419 } else {
420 expr = new UnaryExpression (UnaryOperator.MINUS, inner, get_src (begin));
422 return expr;
423 case TokenType.INTEGER_LITERAL:
424 expr = new IntegerLiteral (get_string (), src);
425 break;
426 case TokenType.REAL_LITERAL:
427 expr = new RealLiteral (get_string (), src);
428 break;
429 case TokenType.STRING_LITERAL:
430 expr = new StringLiteral (get_string (), src);
431 break;
432 case TokenType.IDENTIFIER:
433 expr = new MemberAccess (null, get_string (), src);
434 while (next () == TokenType.DOT) {
435 if (next () != TokenType.IDENTIFIER) {
436 Report.error (get_current_src (), "expected identifier got %s".printf (current.to_string ()));
437 break;
439 expr = new MemberAccess (expr, get_string (), get_current_src ());
441 return expr;
442 case TokenType.OPEN_PARENS:
443 // empty tuple => no expression
444 if (next () != TokenType.CLOSE_PARENS) {
445 Report.error (get_current_src (), "expected `)', got %s".printf (current.to_string ()));
446 break;
448 expr = new Tuple (src);
449 break;
450 default:
451 Report.error (src, "expected literal or symbol got %s".printf (current.to_string ()));
452 break;
454 next ();
455 return expr;
458 bool parse_args (Metadata metadata) {
459 while (current != TokenType.EOF && has_space () && !has_newline ()) {
460 SourceLocation begin = this.begin;
461 var id = parse_identifier (false);
462 if (id == null) {
463 return false;
465 var arg_type = ArgumentType.from_string (id);
466 if (arg_type == null) {
467 Report.warning (get_src (begin, old_end), "unknown argument `%s'".printf (id));
468 continue;
471 if (current != TokenType.ASSIGN) {
472 // threat as `true'
473 metadata.add_argument (arg_type, new Argument (new BooleanLiteral (true, get_src (begin)), get_src (begin)));
474 continue;
476 next ();
478 Expression expr = parse_expression ();
479 if (expr == null) {
480 return false;
482 metadata.add_argument (arg_type, new Argument (expr, get_src (begin)));
485 return true;
488 bool parse_rule () {
489 var old_end = end;
490 var metadata = parse_pattern ();
491 if (metadata == null) {
492 return false;
495 if (current == TokenType.EOF || old_end.line != end.line) {
496 // eof or new rule
497 return true;
499 return parse_args (metadata);
504 * GIR parser
507 class Node {
508 public static ArrayList<Node> new_namespaces = new ArrayList<Node> ();
510 public weak Node parent;
511 public string element_type;
512 public string name;
513 public Map<string,string> girdata = null;
514 public Metadata metadata = Metadata.empty;
515 public SourceReference source_reference = null;
516 public ArrayList<Node> members = new ArrayList<Node> (); // guarantees fields order
517 public HashMap<string, ArrayList<Node>> scope = new HashMap<string, ArrayList<Node>> (str_hash, str_equal);
519 public GirComment comment;
520 public Symbol symbol;
521 public bool new_symbol;
522 public bool merged;
523 public bool processed;
525 // function-specific
526 public int return_array_length_idx = -1;
527 public List<ParameterInfo> parameters;
528 public ArrayList<int> array_length_parameters;
529 public ArrayList<int> closure_parameters;
530 public ArrayList<int> destroy_parameters;
531 // record-specific
532 public UnresolvedSymbol gtype_struct_for;
533 // alias-specific
534 public DataType base_type;
535 // struct-specific
536 public int array_length_idx = -1;
538 public bool deprecated = false;
539 public uint64 deprecated_version = 0;
540 public string? deprecated_since = null;
541 public string? deprecated_replacement = null;
543 public Node (string? name) {
544 this.name = name;
547 public void add_member (Node node) {
548 var nodes = scope[node.name];
549 if (nodes == null) {
550 nodes = new ArrayList<Node> ();
551 scope[node.name] = nodes;
553 nodes.add (node);
554 members.add (node);
555 node.parent = this;
558 public void remove_member (Node node) {
559 var nodes = scope[node.name];
560 nodes.remove (node);
561 if (nodes.size == 0) {
562 scope.remove (node.name);
564 members.remove (node);
565 node.parent = null;
568 public Node? lookup (string name, bool create_namespace = false, SourceReference? source_reference = null) {
569 var nodes = scope[name];
570 Node node = null;
571 if (nodes != null) {
572 node = nodes[0];
574 if (node == null) {
575 Symbol sym = null;
576 if (symbol != null) {
577 sym = symbol.scope.lookup (name);
579 if (sym != null || create_namespace) {
580 node = new Node (name);
581 node.symbol = sym;
582 node.new_symbol = node.symbol == null;
583 node.source_reference = source_reference;
584 add_member (node);
586 if (sym == null) {
587 new_namespaces.add (node);
591 return node;
594 public ArrayList<Node>? lookup_all (string name) {
595 return scope[name];
598 public UnresolvedSymbol get_unresolved_symbol () {
599 if (parent.name == null) {
600 return new UnresolvedSymbol (null, name);
601 } else {
602 return new UnresolvedSymbol (parent.get_unresolved_symbol (), name);
606 public string get_full_name () {
607 if (parent == null) {
608 return name;
611 if (name == null) {
612 return parent.get_full_name ();
615 if (parent.get_full_name () == null) {
616 return name;
619 return "%s.%s".printf (parent.get_full_name (), name);
622 public string get_default_gir_name () {
623 GLib.StringBuilder default_name = new GLib.StringBuilder ();
625 for (unowned Node? node = this ; node != null ; node = node.parent) {
626 if (node.symbol is Namespace) {
627 if (node.symbol.get_attribute_string ("CCode", "gir_namespace") != null) {
628 break;
632 default_name.prepend (node.name);
635 return default_name.str;
638 public string get_gir_name () {
639 var gir_name = girdata["name"];
640 if (gir_name == null) {
641 gir_name = girdata["glib:name"];
643 return gir_name;
646 public string get_lower_case_cprefix () {
647 if (name == null) {
648 return "";
651 var prefix = symbol.get_attribute_string ("CCode", "lower_case_cprefix");
652 if (prefix == null && (symbol is ObjectTypeSymbol || symbol is Struct)) {
653 if (metadata.has_argument (ArgumentType.LOWER_CASE_CPREFIX)) {
654 prefix = metadata.get_string (ArgumentType.LOWER_CASE_CPREFIX);
655 } else if (metadata.has_argument (ArgumentType.CPREFIX)) {
656 prefix = metadata.get_string (ArgumentType.CPREFIX);
657 } else {
658 prefix = symbol.get_attribute_string ("CCode", "cprefix");
662 if (prefix == null && girdata != null && (girdata.contains ("c:symbol-prefix") || girdata.contains("c:symbol-prefixes"))) {
663 /* Use the prefix in the gir. We look up prefixes up to the root.
664 If some node does not have girdata, we ignore it as i might be
665 a namespace created due to reparenting. */
666 unowned Node cur = this;
667 do {
668 if (cur.girdata != null) {
669 var p = cur.girdata["c:symbol-prefix"];
670 if (p == null) {
671 p = cur.girdata["c:symbol-prefixes"];
672 if (p != null) {
673 var idx = p.index_of (",");
674 if (idx >= 0) {
675 p = p.substring (0, idx);
680 if (p != null) {
681 prefix = "%s_%s".printf (p, prefix ?? "");
685 cur = cur.parent;
686 } while (cur != null);
689 if (prefix == null) {
690 prefix = get_default_lower_case_cprefix ();
692 return prefix;
695 public string get_default_lower_case_cprefix () {
696 return "%s%s_".printf (parent.get_lower_case_cprefix (), get_lower_case_csuffix ());
699 public string get_lower_case_csuffix () {
700 var suffix = symbol.get_attribute_string ("CCode", "lower_case_csuffix");
702 // we can't rely on gir suffix if metadata changed the name
703 if (suffix == null && girdata != null && girdata["c:symbol-prefix"] != null && !metadata.has_argument (ArgumentType.NAME)) {
704 suffix = girdata["c:symbol-prefix"];
706 if (suffix == null) {
707 suffix = get_default_lower_case_csuffix ();
709 return suffix;
712 public string get_default_lower_case_csuffix () {
713 return Symbol.camel_case_to_lower_case (name);
716 public string get_cprefix () {
717 if (name == null) {
718 return "";
720 string prefix;
721 if (metadata.has_argument (ArgumentType.CPREFIX)) {
722 prefix = metadata.get_string (ArgumentType.CPREFIX);
723 } else {
724 prefix = symbol.get_attribute_string ("CCode", "cprefix");
726 if (prefix == null && girdata != null && girdata["c:identifier-prefixes"] != null) {
727 prefix = girdata["c:identifier-prefixes"];
728 int idx = prefix.index_of (",");
729 if (idx != -1) {
730 prefix = prefix.substring (0, idx);
733 if (prefix == null) {
734 if (symbol is Enum || symbol is ErrorDomain) {
735 prefix = "%s%s".printf (parent.get_lower_case_cprefix ().ascii_up (), name);
736 } else {
737 prefix = get_cname ();
740 return prefix;
743 public string get_cname () {
744 if (name == null) {
745 return "";
747 string cname;
748 if (metadata.has_argument (ArgumentType.CNAME)) {
749 cname = metadata.get_string (ArgumentType.CNAME);
750 } else {
751 cname = symbol.get_attribute_string ("CCode", "cname");
753 if (girdata != null) {
754 if (cname == null) {
755 cname = girdata["c:identifier"];
757 if (cname == null) {
758 cname = girdata["c:type"];
761 if (cname == null) {
762 cname = get_default_cname ();
764 return cname;
767 public string get_default_cname () {
768 if (name == null) {
769 return "";
771 if (symbol is Field) {
772 if (((Field) symbol).binding == MemberBinding.STATIC) {
773 return parent.get_lower_case_cprefix () + name;
774 } else {
775 return name;
777 } else if (symbol is Method) {
778 return "%s%s".printf (parent.get_lower_case_cprefix (), name);
779 } else {
780 return "%s%s".printf (parent.get_cprefix (), name);
784 public string get_finish_cname () {
785 var finish_cname = symbol.get_attribute_string ("CCode", "finish_name");
786 if (finish_cname == null) {
787 finish_cname = get_cname ();
788 if (finish_cname.has_suffix ("_async")) {
789 finish_cname = finish_cname.substring (0, finish_cname.length - "_async".length);
791 finish_cname = "%s_finish".printf (finish_cname);
793 return finish_cname;
796 public string get_cheader_filename () {
797 if (metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
798 return metadata.get_string (ArgumentType.CHEADER_FILENAME);
800 var cheader_filename = symbol.get_attribute_string ("CCode", "cheader_filename");
801 if (cheader_filename != null) {
802 return cheader_filename;
804 if (parent.name != null) {
805 return parent.get_cheader_filename ();
806 } else if (symbol.source_reference != null) {
807 return symbol.source_reference.file.get_cinclude_filename ();
809 return "";
812 private static uint64 parse_version_string (string version) {
813 int64 res = 0;
814 int shift = 16;
815 string[] tokens = version.split (".", 3);
817 foreach (unowned string token in tokens) {
818 int64 t;
820 if (!int64.try_parse (token, out t))
821 return 0;
822 if (t > 0xffff)
823 return 0;
825 res |= (t << shift);
826 shift -= 8;
829 return res;
832 public void process (GirParser parser) {
833 if (processed) {
834 return;
837 if (symbol is Namespace && parent == parser.root) {
838 // first process aliases since they have no assigned symbol
839 foreach (var node in members) {
840 if (node.element_type == "alias") {
841 parser.process_alias (node);
845 // auto reparent namespace methods, allowing node removals
846 for (int i=0; i < members.size; i++) {
847 var node = members[i];
848 if (node.symbol is Method && node.new_symbol) {
849 parser.process_namespace_method (this, node);
850 if (i < members.size && members[i] != node) {
851 // node removed in the middle
852 i--;
858 if (symbol is Class && girdata != null) {
859 var class_struct = girdata["glib:type-struct"];
860 if (class_struct != null) {
861 var klass = parser.resolve_node (parent, parser.parse_symbol_from_string (class_struct, source_reference));
862 if (klass != null) {
863 var i = 0;
864 while ( i < klass.members.size ) {
865 var node = klass.members[i];
866 if (node.symbol is Method) {
867 klass.remove_member (node);
868 this.add_member (node);
870 Method m = (Method) node.symbol;
871 m.binding = MemberBinding.CLASS;
872 } else {
873 i++;
880 // process children
881 foreach (var node in members) {
882 node.process (parser);
885 if (girdata != null) {
886 // GIR node processing
887 if (symbol is Method) {
888 var m = (Method) symbol;
889 parser.process_callable (this);
891 var colliding = parent.lookup_all (name);
892 foreach (var node in colliding) {
893 var sym = node.symbol;
894 if (sym is Field && !(m.return_type is VoidType) && m.get_parameters().size == 0) {
895 // assume method is getter
896 merged = true;
897 } else if (sym is Signal) {
898 node.process (parser);
899 var sig = (Signal) sym;
900 if (m.is_virtual || m.is_abstract) {
901 sig.is_virtual = true;
902 } else {
903 sig.set_attribute ("HasEmitter", true);
905 parser.assume_parameter_names (sig, m, false);
906 if (m.get_parameters().size != sig.get_parameters().size) {
907 Report.warning (symbol.source_reference, "Signal `%s' conflicts with method of the same name".printf (get_full_name ()));
909 merged = true;
910 } else if (sym is Method && !(sym is CreationMethod) && node != this) {
911 if (m.is_virtual || m.is_abstract) {
912 bool different_invoker = false;
913 var attr = m.get_attribute ("NoWrapper");
914 if (attr != null) {
915 /* no invoker but this method has the same name,
916 most probably the invoker has a different name
917 and g-ir-scanner missed it */
918 var invoker = parser.find_invoker (this);
919 if (invoker != null) {
920 m.set_attribute_string ("CCode", "vfunc_name", m.name);
921 m.name = invoker.symbol.name;
922 m.set_attribute ("NoWrapper", false);
923 invoker.merged = true;
924 different_invoker = true;
927 if (!different_invoker) {
928 if (attr != null) {
929 Report.warning (symbol.source_reference, "Virtual method `%s' conflicts with method of the same name".printf (get_full_name ()));
931 node.merged = true;
936 if (!(m is CreationMethod)) {
937 if (metadata.has_argument (ArgumentType.DESTROYS_INSTANCE)) {
938 m.set_attribute ("DestroysInstance", metadata.get_bool (ArgumentType.DESTROYS_INSTANCE));
940 if (metadata.has_argument (ArgumentType.RETURNS_MODIFIED_POINTER)) {
941 m.set_attribute ("ReturnsModifiedPointer", metadata.get_bool (ArgumentType.RETURNS_MODIFIED_POINTER));
943 // merge custom vfunc
944 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
945 var vfunc = parent.lookup (metadata.get_string (ArgumentType.VFUNC_NAME));
946 if (vfunc != null && vfunc != this) {
947 vfunc.processed = true;
948 vfunc.merged = true;
952 if (m.coroutine) {
953 parser.process_async_method (this);
955 } else if (symbol is Property) {
956 var colliding = parent.lookup_all (name);
957 foreach (var node in colliding) {
958 if (node.symbol is Signal) {
959 // properties take precedence
960 node.processed = true;
961 node.merged = true;
962 } else if (node.symbol is Method) {
963 // getter in C, but not in Vala
964 node.merged = true;
968 var prop = (Property) symbol;
970 // add accessors, can't do this before gir symbol resolution
971 var readable = girdata["readable"];
972 var writable = girdata["writable"];
973 var construct_ = girdata["construct"];
974 var construct_only = girdata["construct-only"];
975 if (readable != "0") {
976 prop.get_accessor = new PropertyAccessor (true, false, false, prop.property_type.copy (), null, null);
978 if (writable == "1" || construct_only == "1") {
979 prop.set_accessor = new PropertyAccessor (false, (construct_only != "1") && (writable == "1"), (construct_only == "1") || (construct_ == "1"), prop.property_type.copy (), null, null);
982 // find virtual/abstract accessors to handle abstract properties properly
984 Node getter = null;
985 var getters = parent.lookup_all ("get_%s".printf (name));
986 if (getters != null) {
987 foreach (var g in getters) {
988 if ((getter == null || !g.merged) && g.get_cname () == "%sget_%s".printf (parent.get_lower_case_cprefix (), name)) {
989 getter = g;
994 Node setter = null;
995 var setters = parent.lookup_all ("set_%s".printf (name));
996 if (setters != null) {
997 foreach (var s in setters) {
998 if ((setter == null || !s.merged) && s.get_cname () == "%sset_%s".printf (parent.get_lower_case_cprefix (), name)) {
999 setter = s;
1004 prop.set_attribute ("NoAccessorMethod", (readable == "0" && construct_only == "1"));
1005 if (prop.get_accessor != null) {
1006 var m = getter != null ? getter.symbol as Method : null;
1007 // ensure getter vfunc if the property is abstract
1008 if (m != null) {
1009 getter.process (parser);
1010 if (m.return_type is VoidType || m.get_parameters().size != 0 || m.get_error_types ().size > 0) {
1011 prop.set_attribute ("NoAccessorMethod", true);
1012 } else {
1013 if (getter.name == name) {
1014 foreach (var node in colliding) {
1015 if (node.symbol is Method) {
1016 node.merged = true;
1021 prop.get_accessor.value_type.value_owned = m.return_type.value_owned;
1023 if (!m.is_abstract && !m.is_virtual && prop.is_abstract) {
1024 prop.set_attribute ("ConcreteAccessor", true);
1027 } else {
1028 prop.set_attribute ("NoAccessorMethod", true);
1032 if (prop.get_attribute ("NoAccessorMethod") == null && prop.set_accessor != null && prop.set_accessor.writable) {
1033 var m = setter != null ? setter.symbol as Method : null;
1034 // ensure setter vfunc if the property is abstract
1035 if (m != null) {
1036 setter.process (parser);
1037 if (!(m.return_type is VoidType || m.return_type is BooleanType) || m.get_parameters ().size != 1 || m.get_error_types ().size > 0) {
1038 prop.set_attribute ("NoAccessorMethod", true);
1039 prop.set_attribute ("ConcreteAccessor", false);
1040 } else {
1041 prop.set_accessor.value_type.value_owned = m.get_parameters()[0].variable_type.value_owned;
1042 if (prop.get_attribute ("ConcreteAccessor") != null && !m.is_abstract && !m.is_virtual && prop.is_abstract) {
1043 prop.set_attribute ("ConcreteAccessor", true);
1044 prop.set_attribute ("NoAccessorMethod", false);
1047 } else {
1048 prop.set_attribute ("NoAccessorMethod", true);
1049 prop.set_attribute ("ConcreteAccessor", false);
1053 if (prop.get_attribute ("NoAccessorMethod") != null) {
1054 if (!prop.overrides && parent.symbol is Class) {
1055 // bug 742012
1056 // find base interface property with ConcreteAccessor and this overriding property with NoAccessorMethod
1057 var base_prop_node = parser.base_interface_property (this);
1058 if (base_prop_node != null) {
1059 base_prop_node.process (parser);
1061 var base_property = (Property) base_prop_node.symbol;
1062 if (base_property.get_attribute ("ConcreteAccessor") != null) {
1063 prop.set_attribute ("NoAccessorMethod", false);
1064 if (prop.get_accessor != null) {
1065 prop.get_accessor.value_type.value_owned = base_property.get_accessor.value_type.value_owned;
1067 if (prop.set_accessor != null) {
1068 prop.set_accessor.value_type.value_owned = base_property.set_accessor.value_type.value_owned;
1076 if (metadata.has_argument (ArgumentType.NO_ACCESSOR_METHOD)) {
1077 prop.set_attribute ("NoAccessorMethod", metadata.get_bool (ArgumentType.NO_ACCESSOR_METHOD));
1080 if (prop.get_attribute ("NoAccessorMethod") != null) {
1081 // gobject defaults
1082 if (prop.get_accessor != null) {
1083 prop.get_accessor.value_type.value_owned = true;
1085 if (prop.set_accessor != null) {
1086 prop.set_accessor.value_type.value_owned = false;
1089 } else if (symbol is Field) {
1090 var field = (Field) symbol;
1091 var colliding = parent.lookup_all (name);
1092 if (colliding.size > 1) {
1093 // whatelse has precedence over the field
1094 merged = true;
1097 if (metadata.has_argument (ArgumentType.DELEGATE_TARGET)) {
1098 field.set_attribute_bool ("CCode", "delegate_target", metadata.get_bool (ArgumentType.DELEGATE_TARGET));
1100 if (metadata.has_argument (ArgumentType.DELEGATE_TARGET_CNAME)) {
1101 field.set_attribute_string ("CCode", "delegate_target_cname", metadata.get_string (ArgumentType.DELEGATE_TARGET_CNAME));
1104 if (field.variable_type is DelegateType && parent.gtype_struct_for != null) {
1105 // virtual method field
1106 var d = ((DelegateType) field.variable_type).delegate_symbol;
1107 parser.process_virtual_method_field (this, d, parent.gtype_struct_for);
1108 merged = true;
1109 } else if (field.variable_type is ArrayType) {
1110 Node array_length;
1111 if (metadata.has_argument (ArgumentType.ARRAY_LENGTH_FIELD)) {
1112 array_length = parent.lookup (metadata.get_string (ArgumentType.ARRAY_LENGTH_FIELD));
1113 } else if (array_length_idx > -1 && parent.members.size > array_length_idx) {
1114 array_length = parent.members[array_length_idx];
1115 } else {
1116 array_length = parent.lookup ("n_%s".printf (field.name));
1117 if (array_length == null) {
1118 array_length = parent.lookup ("num_%s".printf (field.name));
1119 if (array_length == null) {
1120 array_length = parent.lookup ("%s_length".printf (field.name));
1124 if (array_length != null && array_length.symbol is Field) {
1125 var length_field = (Field) array_length.symbol;
1126 // array has length
1127 field.set_attribute_string ("CCode", "array_length_cname", length_field.name);
1128 var length_type = length_field.variable_type.to_qualified_string ();
1129 if (length_type != "int") {
1130 var st = parser.root.lookup (length_type);
1131 if (st != null) {
1132 field.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1135 field.remove_attribute_argument ("CCode", "array_length");
1136 field.remove_attribute_argument ("CCode", "array_null_terminated");
1139 } else if (symbol is Signal || symbol is Delegate) {
1140 parser.process_callable (this);
1141 } else if (symbol is Interface) {
1142 parser.process_interface (this);
1143 } else if (symbol is Struct) {
1144 if (parent.symbol is ObjectTypeSymbol || parent.symbol is Struct) {
1145 // nested struct
1146 foreach (var fn in members) {
1147 var f = fn.symbol as Field;
1148 if (f != null) {
1149 if (f.binding == MemberBinding.INSTANCE) {
1150 f.set_attribute_string ("CCode", "cname", "%s.%s".printf (name, fn.get_cname ()));
1152 f.name = "%s_%s".printf (symbol.name, f.name);
1153 fn.name = f.name;
1154 parent.add_member (fn);
1157 merged = true;
1158 } else {
1159 // record for a gtype
1160 var gtype_struct_for = girdata["glib:is-gtype-struct-for"];
1161 if (gtype_struct_for != null) {
1162 var iface = parser.resolve_node (parent, parser.parse_symbol_from_string (gtype_struct_for, source_reference));
1163 if (iface != null && iface.symbol is Interface && "%sIface".printf (iface.get_cname ()) != get_cname ()) {
1164 // set the interface struct name
1165 iface.symbol.set_attribute_string ("CCode", "type_cname", get_cname ());
1167 merged = true;
1172 // deprecated
1173 if (metadata.has_argument (ArgumentType.REPLACEMENT)) {
1174 deprecated = true;
1175 deprecated_replacement = metadata.get_string (ArgumentType.REPLACEMENT);
1177 if (metadata.has_argument (ArgumentType.DEPRECATED_SINCE)) {
1178 deprecated = true;
1179 deprecated_since = metadata.get_string (ArgumentType.DEPRECATED_SINCE);
1180 } else if (girdata["deprecated-version"] != null) {
1181 deprecated = true;
1182 deprecated_since = girdata.get ("deprecated-version");
1184 if (metadata.has_argument (ArgumentType.DEPRECATED)) {
1185 deprecated = metadata.get_bool (ArgumentType.DEPRECATED, true);
1186 if (!deprecated) {
1187 deprecated_since = null;
1188 deprecated_replacement = null;
1190 } else if (girdata["deprecated"] != null) {
1191 deprecated = true;
1193 if (deprecated_since != null) {
1194 deprecated_version = parse_version_string (deprecated_since);
1197 // experimental
1198 if (metadata.has_argument (ArgumentType.EXPERIMENTAL)) {
1199 symbol.set_attribute_bool ("Version", "experimental", metadata.get_bool (ArgumentType.EXPERIMENTAL));
1202 // since
1203 if (metadata.has_argument (ArgumentType.SINCE)) {
1204 symbol.version.since = metadata.get_string (ArgumentType.SINCE);
1205 } else if (symbol is Namespace == false && girdata["version"] != null) {
1206 symbol.version.since = girdata.get ("version");
1209 if (parent.symbol is Namespace) {
1210 // always write cheader filename for namespace children
1211 symbol.set_attribute_string ("CCode", "cheader_filename", get_cheader_filename ());
1212 } else if (metadata.has_argument (ArgumentType.CHEADER_FILENAME)) {
1213 symbol.set_attribute_string ("CCode", "cheader_filename", metadata.get_string (ArgumentType.CHEADER_FILENAME));
1215 if (get_cname () != get_default_cname ()) {
1216 symbol.set_attribute_string ("CCode", "cname", get_cname ());
1219 // lower_case_cprefix
1220 if (get_lower_case_cprefix () != get_default_lower_case_cprefix ()) {
1221 symbol.set_attribute_string ("CCode", "lower_case_cprefix", get_lower_case_cprefix ());
1223 // lower_case_csuffix
1224 if (get_lower_case_csuffix () != get_default_lower_case_csuffix ()) {
1225 symbol.set_attribute_string ("CCode", "lower_case_csuffix", get_lower_case_csuffix ());
1228 // set gir name if the symbol has been renamed
1229 string gir_name = get_gir_name ();
1230 string default_gir_name = get_default_gir_name ();
1231 if (is_container (symbol) && !(symbol is Namespace) && (name != gir_name || gir_name != default_gir_name)) {
1232 symbol.set_attribute_string ("GIR", "name", gir_name);
1236 if (!(new_symbol && merged) && is_container (symbol)) {
1237 foreach (var node in members) {
1238 if (this.deprecated_version > 0 && node.deprecated_version > 0) {
1239 if (this.deprecated_version <= node.deprecated_version) {
1240 node.deprecated = false;
1241 node.deprecated_since = null;
1242 node.deprecated_replacement = null;
1245 if (node.deprecated) {
1246 node.symbol.version.deprecated = true;
1248 if (node.deprecated_since != null) {
1249 node.symbol.version.deprecated_since = node.deprecated_since;
1251 if (node.deprecated_replacement != null) {
1252 node.symbol.version.replacement = node.deprecated_replacement;
1255 if (node.new_symbol && !node.merged && !metadata.get_bool (ArgumentType.HIDDEN)) {
1256 add_symbol_to_container (symbol, node.symbol);
1260 var cl = symbol as Class;
1261 if (cl != null && !cl.is_compact && cl.default_construction_method == null) {
1262 // always provide constructor in generated bindings
1263 // to indicate that implicit Object () chainup is allowed
1264 var cm = new CreationMethod (null, null, cl.source_reference);
1265 cm.has_construct_function = false;
1266 cm.access = SymbolAccessibility.PROTECTED;
1267 cl.add_method (cm);
1271 processed = true;
1274 public string to_string () {
1275 if (parent.name == null) {
1276 return name;
1277 } else {
1278 return "%s.%s".printf (parent.to_string (), name);
1283 static GLib.Regex type_from_string_regex;
1285 MarkupReader reader;
1287 CodeContext context;
1288 Namespace glib_ns;
1290 SourceFile current_source_file;
1291 Node root;
1292 ArrayList<Metadata> metadata_roots = new ArrayList<Metadata> ();
1294 SourceLocation begin;
1295 SourceLocation end;
1296 MarkupTokenType current_token;
1298 string[] cheader_filenames;
1300 ArrayList<Metadata> metadata_stack;
1301 Metadata metadata;
1302 ArrayList<Node> tree_stack;
1303 Node current;
1304 Node old_current;
1306 Set<string> provided_namespaces = new HashSet<string> (str_hash, str_equal);
1307 HashMap<UnresolvedSymbol,Symbol> unresolved_symbols_map = new HashMap<UnresolvedSymbol,Symbol> (unresolved_symbol_hash, unresolved_symbol_equal);
1308 ArrayList<UnresolvedSymbol> unresolved_gir_symbols = new ArrayList<UnresolvedSymbol> ();
1309 HashMap<UnresolvedType,Node> unresolved_type_arguments = new HashMap<UnresolvedType,Node> ();
1312 * Parses all .gir source files in the specified code
1313 * context and builds a code tree.
1315 * @param context a code context
1317 public void parse (CodeContext context) {
1318 this.context = context;
1319 glib_ns = context.root.scope.lookup ("GLib") as Namespace;
1321 root = new Node (null);
1322 root.symbol = context.root;
1323 tree_stack = new ArrayList<Node> ();
1324 current = root;
1326 map_vala_to_gir ();
1328 context.accept (this);
1330 resolve_gir_symbols ();
1331 create_new_namespaces ();
1332 resolve_type_arguments ();
1334 root.process (this);
1336 foreach (var metadata in metadata_roots) {
1337 report_unused_metadata (metadata);
1340 this.context = null;
1343 void map_vala_to_gir () {
1344 foreach (var source_file in context.get_source_files ()) {
1345 string gir_namespace = source_file.gir_namespace;
1346 string gir_version = source_file.gir_version;
1347 Namespace ns = null;
1348 if (gir_namespace == null) {
1349 foreach (var node in source_file.get_nodes ()) {
1350 if (node is Namespace) {
1351 ns = (Namespace) node;
1352 gir_namespace = ns.get_attribute_string ("CCode", "gir_namespace");
1353 if (gir_namespace != null) {
1354 gir_version = ns.get_attribute_string ("CCode", "gir_version");
1355 break;
1360 if (gir_namespace == null) {
1361 continue;
1364 provided_namespaces.add ("%s-%s".printf (gir_namespace, gir_version));
1366 var gir_symbol = new UnresolvedSymbol (null, gir_namespace);
1367 if (gir_namespace != ns.name) {
1368 set_symbol_mapping (gir_symbol, ns);
1371 foreach (var node in source_file.get_nodes ()) {
1372 if (node.has_attribute_argument ("GIR", "name")) {
1373 var map_from = new UnresolvedSymbol (gir_symbol, node.get_attribute_string ("GIR", "name"));
1374 set_symbol_mapping (map_from, (Symbol) node);
1380 public override void visit_source_file (SourceFile source_file) {
1381 if (source_file.filename.has_suffix (".gir")) {
1382 parse_file (source_file);
1386 public void parse_file (SourceFile source_file) {
1387 var has_global_context = (context != null);
1388 if (!has_global_context) {
1389 context = source_file.context;
1392 metadata_stack = new ArrayList<Metadata> ();
1393 metadata = Metadata.empty;
1394 cheader_filenames = null;
1396 this.current_source_file = source_file;
1397 reader = new MarkupReader (source_file.filename);
1399 // xml prolog
1400 next ();
1401 next ();
1403 next ();
1404 parse_repository ();
1406 reader = null;
1407 this.current_source_file = null;
1408 if (!has_global_context) {
1409 context = null;
1413 void next () {
1414 current_token = reader.read_token (out begin, out end);
1417 void start_element (string name) {
1418 if (current_token != MarkupTokenType.START_ELEMENT || reader.name != name) {
1419 // error
1420 Report.error (get_current_src (), "expected start element of `%s'".printf (name));
1424 void end_element (string name) {
1425 while (current_token != MarkupTokenType.END_ELEMENT || reader.name != name) {
1426 Report.warning (get_current_src (), "expected end element of `%s'".printf (name));
1427 skip_element ();
1429 next ();
1432 SourceReference get_current_src () {
1433 return new SourceReference (this.current_source_file, begin, end);
1436 const string GIR_VERSION = "1.2";
1438 static void add_symbol_to_container (Symbol container, Symbol sym) {
1439 if (container is Class) {
1440 unowned Class cl = (Class) container;
1442 if (sym is Class) {
1443 cl.add_class ((Class) sym);
1444 } else if (sym is Constant) {
1445 cl.add_constant ((Constant) sym);
1446 } else if (sym is Enum) {
1447 cl.add_enum ((Enum) sym);
1448 } else if (sym is Field) {
1449 cl.add_field ((Field) sym);
1450 } else if (sym is Method) {
1451 cl.add_method ((Method) sym);
1452 } else if (sym is Property) {
1453 cl.add_property ((Property) sym);
1454 } else if (sym is Signal) {
1455 cl.add_signal ((Signal) sym);
1456 } else if (sym is Struct) {
1457 cl.add_struct ((Struct) sym);
1459 } else if (container is Enum) {
1460 unowned Enum en = (Enum) container;
1462 if (sym is EnumValue) {
1463 en.add_value ((EnumValue) sym);
1464 } else if (sym is Constant) {
1465 en.add_constant ((Constant) sym);
1466 } else if (sym is Method) {
1467 en.add_method ((Method) sym);
1469 } else if (container is Interface) {
1470 unowned Interface iface = (Interface) container;
1472 if (sym is Class) {
1473 iface.add_class ((Class) sym);
1474 } else if (sym is Constant) {
1475 iface.add_constant ((Constant) sym);
1476 } else if (sym is Enum) {
1477 iface.add_enum ((Enum) sym);
1478 } else if (sym is Field) {
1479 iface.add_field ((Field) sym);
1480 } else if (sym is Method) {
1481 iface.add_method ((Method) sym);
1482 } else if (sym is Property) {
1483 iface.add_property ((Property) sym);
1484 } else if (sym is Signal) {
1485 iface.add_signal ((Signal) sym);
1486 } else if (sym is Struct) {
1487 iface.add_struct ((Struct) sym);
1489 } else if (container is Namespace) {
1490 unowned Namespace ns = (Namespace) container;
1492 if (sym is Namespace) {
1493 ns.add_namespace ((Namespace) sym);
1494 } else if (sym is Class) {
1495 ns.add_class ((Class) sym);
1496 } else if (sym is Constant) {
1497 ns.add_constant ((Constant) sym);
1498 } else if (sym is Delegate) {
1499 ns.add_delegate ((Delegate) sym);
1500 } else if (sym is Enum) {
1501 ns.add_enum ((Enum) sym);
1502 } else if (sym is ErrorDomain) {
1503 ns.add_error_domain ((ErrorDomain) sym);
1504 } else if (sym is Field) {
1505 ns.add_field ((Field) sym);
1506 } else if (sym is Interface) {
1507 ns.add_interface ((Interface) sym);
1508 } else if (sym is Method) {
1509 ns.add_method ((Method) sym);
1510 } else if (sym is Struct) {
1511 ns.add_struct ((Struct) sym);
1513 } else if (container is Struct) {
1514 unowned Struct st = (Struct) container;
1516 if (sym is Constant) {
1517 st.add_constant ((Constant) sym);
1518 } else if (sym is Field) {
1519 st.add_field ((Field) sym);
1520 } else if (sym is Method) {
1521 st.add_method ((Method) sym);
1522 } else if (sym is Property) {
1523 st.add_property ((Property) sym);
1525 } else if (container is ErrorDomain) {
1526 unowned ErrorDomain ed = (ErrorDomain) container;
1528 if (sym is ErrorCode) {
1529 ed.add_code ((ErrorCode) sym);
1530 } else if (sym is Method) {
1531 ed.add_method ((Method) sym);
1533 } else {
1534 Report.error (sym.source_reference, "impossible to add `%s' to container `%s'".printf (sym.name, container.name));
1538 static bool is_container (Symbol sym) {
1539 return sym is ObjectTypeSymbol || sym is Struct || sym is Namespace || sym is ErrorDomain || sym is Enum;
1542 UnresolvedSymbol? parse_symbol_from_string (string symbol_string, SourceReference? source_reference = null) {
1543 UnresolvedSymbol? sym = null;
1544 foreach (unowned string s in symbol_string.split (".")) {
1545 sym = new UnresolvedSymbol (sym, s, source_reference);
1547 if (sym == null) {
1548 Report.error (source_reference, "a symbol must be specified");
1550 return sym;
1553 void set_symbol_mapping (UnresolvedSymbol map_from, Symbol map_to) {
1554 // last mapping is the most up-to-date
1555 if (map_from is UnresolvedSymbol) {
1556 unresolved_symbols_map[(UnresolvedSymbol) map_from] = map_to;
1560 void assume_parameter_names (Signal sig, Symbol sym, bool skip_first) {
1561 var iter = ((Callable) sym).get_parameters ().iterator ();
1562 bool first = true;
1563 foreach (var param in sig.get_parameters ()) {
1564 if (!iter.next ()) {
1565 // unreachable for valid GIR
1566 break;
1568 if (skip_first && first) {
1569 if (!iter.next ()) {
1570 // unreachable for valid GIR
1571 break;
1573 first = false;
1575 param.name = iter.get ().name;
1579 Node? find_invoker (Node node) {
1580 /* most common use case is invoker has at least the given method prefix
1581 and the same parameter names */
1582 var m = (Method) node.symbol;
1583 var prefix = "%s_".printf (m.name);
1584 foreach (var n in node.parent.members) {
1585 if (!n.name.has_prefix (prefix)) {
1586 continue;
1588 Method? invoker = n.symbol as Method;
1589 if (invoker == null || (m.get_parameters().size != invoker.get_parameters().size)) {
1590 continue;
1592 var iter = invoker.get_parameters ().iterator ();
1593 foreach (var param in m.get_parameters ()) {
1594 assert (iter.next ());
1595 if (param.name != iter.get().name) {
1596 invoker = null;
1597 break;
1600 if (invoker != null) {
1601 return n;
1605 return null;
1608 Metadata get_current_metadata () {
1609 var selector = reader.name;
1610 var child_name = reader.get_attribute ("name");
1611 if (child_name == null) {
1612 child_name = reader.get_attribute ("glib:name");
1614 // Give a transparent union the generic name "union"
1615 if (selector == "union" && child_name == null) {
1616 child_name = "union";
1618 if (child_name == null) {
1619 return Metadata.empty;
1621 selector = selector.replace ("-", "_");
1622 child_name = child_name.replace ("-", "_");
1624 if (selector.has_prefix ("glib:")) {
1625 selector = selector.substring ("glib:".length);
1628 return metadata.match_child (child_name, selector);
1631 bool push_metadata () {
1632 var new_metadata = get_current_metadata ();
1633 // skip ?
1634 if (new_metadata.has_argument (ArgumentType.SKIP)) {
1635 if (new_metadata.get_bool (ArgumentType.SKIP)) {
1636 return false;
1638 } else if (reader.get_attribute ("introspectable") == "0" || reader.get_attribute ("private") == "1") {
1639 return false;
1642 metadata_stack.add (metadata);
1643 metadata = new_metadata;
1645 return true;
1648 void pop_metadata () {
1649 metadata = metadata_stack.remove_at (metadata_stack.size - 1);
1652 bool parse_type_arguments_from_string (DataType parent_type, string type_arguments, SourceReference? source_reference = null) {
1653 int type_arguments_length = (int) type_arguments.length;
1654 GLib.StringBuilder current = new GLib.StringBuilder.sized (type_arguments_length);
1656 int depth = 0;
1657 for (var c = 0 ; c < type_arguments_length ; c++) {
1658 if (type_arguments[c] == '<' || type_arguments[c] == '[') {
1659 depth++;
1660 current.append_unichar (type_arguments[c]);
1661 } else if (type_arguments[c] == '>' || type_arguments[c] == ']') {
1662 depth--;
1663 current.append_unichar (type_arguments[c]);
1664 } else if (type_arguments[c] == ',') {
1665 if (depth == 0) {
1666 var dt = parse_type_from_string (current.str, true, source_reference);
1667 if (dt == null) {
1668 return false;
1670 parent_type.add_type_argument (dt);
1671 current.truncate ();
1672 } else {
1673 current.append_unichar (type_arguments[c]);
1675 } else {
1676 current.append_unichar (type_arguments[c]);
1680 var dt = parse_type_from_string (current.str, true, source_reference);
1681 if (dt == null) {
1682 return false;
1684 parent_type.add_type_argument (dt);
1686 return true;
1689 DataType? parse_type_from_string (string type_string, bool owned_by_default, SourceReference? source_reference = null) {
1690 if (type_from_string_regex == null) {
1691 try {
1692 type_from_string_regex = new GLib.Regex ("^(?:(owned|unowned|weak) +)?([0-9a-zA-Z_\\.]+)(?:<(.+)>)?(\\*+)?(\\[,*\\])?(\\?)?$", GLib.RegexCompileFlags.ANCHORED | GLib.RegexCompileFlags.DOLLAR_ENDONLY | GLib.RegexCompileFlags.OPTIMIZE);
1693 } catch (GLib.RegexError e) {
1694 GLib.error ("Unable to compile regex: %s", e.message);
1698 GLib.MatchInfo match;
1699 if (!type_from_string_regex.match (type_string, 0, out match)) {
1700 Report.error (source_reference, "unable to parse type");
1701 return null;
1704 DataType? type = null;
1706 var ownership_data = match.fetch (1);
1707 var type_name = match.fetch (2);
1708 var type_arguments_data = match.fetch (3);
1709 var pointers_data = match.fetch (4);
1710 var array_data = match.fetch (5);
1711 var nullable_data = match.fetch (6);
1713 var nullable = nullable_data != null && nullable_data.length > 0;
1715 if (ownership_data == null && type_name == "void") {
1716 if (array_data == null && !nullable) {
1717 type = new VoidType (source_reference);
1718 if (pointers_data != null) {
1719 for (int i=0; i < pointers_data.length; i++) {
1720 type = new PointerType (type);
1723 return type;
1724 } else {
1725 Report.error (source_reference, "invalid void type");
1726 return null;
1730 bool value_owned = owned_by_default;
1732 if (ownership_data == "owned") {
1733 if (owned_by_default) {
1734 Report.error (source_reference, "unexpected `owned' keyword");
1735 } else {
1736 value_owned = true;
1738 } else if (ownership_data == "unowned") {
1739 if (owned_by_default) {
1740 value_owned = false;
1741 } else {
1742 Report.error (source_reference, "unexpected `unowned' keyword");
1743 return null;
1747 var sym = parse_symbol_from_string (type_name, source_reference);
1748 if (sym == null) {
1749 return null;
1751 type = new UnresolvedType.from_symbol (sym, source_reference);
1753 if (type_arguments_data != null && type_arguments_data.length > 0) {
1754 if (!parse_type_arguments_from_string (type, type_arguments_data, source_reference)) {
1755 return null;
1759 if (pointers_data != null) {
1760 for (int i=0; i < pointers_data.length; i++) {
1761 type = new PointerType (type);
1765 if (array_data != null && array_data.length != 0) {
1766 type.value_owned = true;
1767 type = new ArrayType (type, (int) array_data.length - 1, source_reference);
1770 type.nullable = nullable;
1771 type.value_owned = value_owned;
1772 return type;
1775 string? element_get_string (string attribute_name, ArgumentType arg_type) {
1776 if (metadata.has_argument (arg_type)) {
1777 return metadata.get_string (arg_type);
1778 } else {
1779 return reader.get_attribute (attribute_name);
1784 * The changed is a faster way to check whether the type has changed and it may affect the C declaration.
1786 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) {
1787 changed = false;
1788 var type = orig_type;
1790 if (metadata.has_argument (ArgumentType.TYPE)) {
1791 type = parse_type_from_string (metadata.get_string (ArgumentType.TYPE), owned_by_default, metadata.get_source_reference (ArgumentType.TYPE));
1792 changed = true;
1793 } else if (!(type is VoidType)) {
1794 if (metadata.has_argument (ArgumentType.TYPE_ARGUMENTS)) {
1795 type.remove_all_type_arguments ();
1796 parse_type_arguments_from_string (type, metadata.get_string (ArgumentType.TYPE_ARGUMENTS), metadata.get_source_reference (ArgumentType.TYPE_ARGUMENTS));
1799 if (!(type is ArrayType) && metadata.get_bool (ArgumentType.ARRAY)) {
1800 type.value_owned = true;
1801 type = new ArrayType (type, 1, type.source_reference);
1802 changed = true;
1805 if (owned_by_default) {
1806 type.value_owned = !metadata.get_bool (ArgumentType.UNOWNED, !type.value_owned);
1807 } else {
1808 type.value_owned = metadata.get_bool (ArgumentType.OWNED, type.value_owned);
1810 type.nullable = metadata.get_bool (ArgumentType.NULLABLE, type.nullable);
1813 if (type is ArrayType) {
1814 if (!(orig_type is ArrayType)) {
1815 no_array_length = true;
1817 array_null_terminated = metadata.get_bool (ArgumentType.ARRAY_NULL_TERMINATED, array_null_terminated);
1820 return type;
1823 string? element_get_name (string? gir_name = null) {
1824 var name = gir_name;
1825 if (name == null) {
1826 name = reader.get_attribute ("name");
1828 var pattern = metadata.get_string (ArgumentType.NAME);
1829 if (pattern != null) {
1830 if (pattern.index_of_char ('(') < 0) {
1831 // shortcut for "(.+)/replacement"
1832 name = pattern;
1833 } else {
1834 try {
1835 string replacement = "\\1"; // replace the whole name with the match by default
1836 var split = pattern.split ("/");
1837 if (split.length > 1) {
1838 pattern = split[0];
1839 replacement = split[1];
1841 var regex = new Regex (pattern, RegexCompileFlags.ANCHORED, RegexMatchFlags.ANCHORED);
1842 name = regex.replace (name, -1, 0, replacement);
1843 } catch (Error e) {
1844 name = pattern;
1847 } else {
1848 if (name != null && name.has_suffix ("Enum")) {
1849 name = name.substring (0, name.length - "Enum".length);
1853 return name;
1856 string? element_get_type_id () {
1857 var type_id = metadata.get_string (ArgumentType.TYPE_ID);
1858 if (type_id != null) {
1859 return type_id;
1862 type_id = reader.get_attribute ("glib:get-type");
1863 if (type_id != null) {
1864 type_id += " ()";
1866 return type_id;
1869 void set_array_ccode (Symbol sym, ParameterInfo info) {
1870 sym.set_attribute_double ("CCode", "array_length_pos", info.vala_idx);
1871 if (sym is Parameter) {
1872 sym.set_attribute_string ("CCode", "array_length_cname", info.param.name);
1874 var type_name = info.param.variable_type.to_qualified_string ();
1875 if (type_name != "int") {
1876 var st = root.lookup (type_name);
1877 if (st != null) {
1878 if (sym is Method) {
1879 var m = (Method) sym;
1880 m.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1881 } else {
1882 var param = (Parameter) sym;
1883 param.set_attribute_string ("CCode", "array_length_type", st.get_cname ());
1889 void set_type_id_ccode (Symbol sym) {
1890 if (sym.has_attribute_argument ("CCode", "has_type_id")
1891 || sym.has_attribute_argument ("CCode", "type_id"))
1892 return;
1894 var type_id = element_get_type_id ();
1895 if (type_id == null) {
1896 sym.set_attribute_bool ("CCode", "has_type_id", false);
1897 } else {
1898 sym.set_attribute_string ("CCode", "type_id", type_id);
1902 void parse_repository () {
1903 start_element ("repository");
1904 if (reader.get_attribute ("version") != GIR_VERSION) {
1905 Report.error (get_current_src (), "unsupported GIR version %s (supported: %s)".printf (reader.get_attribute ("version"), GIR_VERSION));
1906 return;
1908 next ();
1909 while (current_token == MarkupTokenType.START_ELEMENT) {
1910 if (reader.name == "namespace") {
1911 parse_namespace ();
1912 } else if (reader.name == "include") {
1913 parse_include ();
1914 } else if (reader.name == "package") {
1915 var pkg = parse_package ();
1916 this.current_source_file.package_name = pkg;
1917 if (context.has_package (pkg)) {
1918 // package already provided elsewhere, stop parsing this GIR
1919 // if it was not passed explicitly
1920 if (!this.current_source_file.explicit) {
1921 return;
1923 } else {
1924 context.add_package (pkg);
1926 } else if (reader.name == "c:include") {
1927 parse_c_include ();
1928 } else {
1929 // error
1930 Report.error (get_current_src (), "unknown child element `%s' in `repository'".printf (reader.name));
1931 skip_element ();
1934 end_element ("repository");
1937 void parse_include () {
1938 start_element ("include");
1939 var pkg = reader.get_attribute ("name");
1940 var version = reader.get_attribute ("version");
1941 if (version != null) {
1942 pkg = "%s-%s".printf (pkg, version);
1944 // add the package to the queue
1945 context.add_external_package (pkg);
1946 next ();
1947 end_element ("include");
1950 string parse_package () {
1951 start_element ("package");
1952 var pkg = reader.get_attribute ("name");
1953 next ();
1954 end_element ("package");
1955 return pkg;
1958 void parse_c_include () {
1959 start_element ("c:include");
1960 cheader_filenames += reader.get_attribute ("name");
1961 next ();
1962 end_element ("c:include");
1965 void skip_element () {
1966 next ();
1968 int level = 1;
1969 while (level > 0) {
1970 if (current_token == MarkupTokenType.START_ELEMENT) {
1971 level++;
1972 } else if (current_token == MarkupTokenType.END_ELEMENT) {
1973 level--;
1974 } else if (current_token == MarkupTokenType.EOF) {
1975 Report.error (get_current_src (), "unexpected end of file");
1976 break;
1978 next ();
1982 Node? resolve_node (Node parent_scope, UnresolvedSymbol unresolved_sym, bool create_namespace = false) {
1983 if (unresolved_sym.inner == null) {
1984 var scope = parent_scope;
1985 while (scope != null) {
1986 var node = scope.lookup (unresolved_sym.name, create_namespace, unresolved_sym.source_reference);
1987 if (node != null) {
1988 return node;
1990 scope = scope.parent;
1992 } else {
1993 var inner = resolve_node (parent_scope, unresolved_sym.inner, create_namespace);
1994 if (inner != null) {
1995 return inner.lookup (unresolved_sym.name, create_namespace, unresolved_sym.source_reference);
1998 return null;
2001 Symbol? resolve_symbol (Node parent_scope, UnresolvedSymbol unresolved_sym) {
2002 var node = resolve_node (parent_scope, unresolved_sym);
2003 if (node != null) {
2004 return node.symbol;
2006 return null;
2009 void push_node (string name, bool merge) {
2010 var parent = current;
2011 if (metadata.has_argument (ArgumentType.PARENT)) {
2012 var target = parse_symbol_from_string (metadata.get_string (ArgumentType.PARENT), metadata.get_source_reference (ArgumentType.PARENT));
2013 parent = resolve_node (root, target, true);
2016 var node = parent.lookup (name);
2017 if (node == null || (node.symbol != null && !merge)) {
2018 node = new Node (name);
2019 node.new_symbol = true;
2020 parent.add_member (node);
2021 } else {
2022 Node.new_namespaces.remove (node);
2024 node.element_type = reader.name;
2025 node.girdata = reader.get_attributes ();
2026 node.metadata = metadata;
2027 node.source_reference = get_current_src ();
2029 var gir_name = node.get_gir_name ();
2030 if (parent != current || gir_name != name) {
2031 set_symbol_mapping (new UnresolvedSymbol (null, gir_name), node.get_unresolved_symbol ());
2034 tree_stack.add (current);
2035 current = node;
2038 void pop_node () {
2039 old_current = current;
2040 current = tree_stack.remove_at (tree_stack.size - 1);
2043 void parse_namespace () {
2044 start_element ("namespace");
2046 string? cprefix = reader.get_attribute ("c:identifier-prefixes");
2047 if (cprefix != null) {
2048 int idx = cprefix.index_of (",");
2049 if (idx != -1) {
2050 cprefix = cprefix.substring (0, idx);
2054 string? lower_case_cprefix = reader.get_attribute ("c:symbol-prefixes");
2055 string vala_namespace = cprefix;
2056 string gir_namespace = reader.get_attribute ("name");
2057 string gir_version = reader.get_attribute ("version");
2059 if (lower_case_cprefix != null) {
2060 int idx = lower_case_cprefix.index_of (",");
2061 if (idx != -1) {
2062 lower_case_cprefix = lower_case_cprefix.substring (0, idx);
2066 if (provided_namespaces.contains ("%s-%s".printf (gir_namespace, gir_version))) {
2067 skip_element ();
2068 return;
2071 // load metadata, first look into metadata directories then in the same directory of the .gir.
2072 string? metadata_filename = context.get_metadata_path (current_source_file.filename);
2073 if (metadata_filename != null && FileUtils.test (metadata_filename, FileTest.EXISTS)) {
2074 var metadata_parser = new MetadataParser ();
2075 var metadata_file = new SourceFile (context, current_source_file.file_type, metadata_filename);
2076 context.add_source_file (metadata_file);
2077 metadata = metadata_parser.parse_metadata (metadata_file);
2078 metadata_roots.add (metadata);
2081 var ns_metadata = metadata.match_child (gir_namespace);
2082 if (ns_metadata.has_argument (ArgumentType.NAME)) {
2083 vala_namespace = ns_metadata.get_string (ArgumentType.NAME);
2085 if (vala_namespace == null) {
2086 vala_namespace = gir_namespace;
2089 current_source_file.gir_namespace = gir_namespace;
2090 current_source_file.gir_version = gir_version;
2092 Namespace ns;
2093 push_node (vala_namespace, true);
2094 if (current.new_symbol) {
2095 ns = new Namespace (vala_namespace, current.source_reference);
2096 current.symbol = ns;
2097 } else {
2098 ns = (Namespace) current.symbol;
2099 ns.attributes = null;
2100 ns.source_reference = current.source_reference;
2103 current.metadata = ns_metadata;
2105 if (ns_metadata.has_argument (ArgumentType.CPREFIX)) {
2106 cprefix = ns_metadata.get_string (ArgumentType.CPREFIX);
2109 if (ns_metadata.has_argument (ArgumentType.LOWER_CASE_CPREFIX)) {
2110 lower_case_cprefix = ns_metadata.get_string (ArgumentType.LOWER_CASE_CPREFIX);
2111 } else if (lower_case_cprefix != null) {
2112 lower_case_cprefix += "_";
2115 ns.set_attribute_string ("CCode", "gir_namespace", gir_namespace);
2116 ns.set_attribute_string ("CCode", "gir_version", gir_version);
2118 if (cprefix != null) {
2119 ns.set_attribute_string ("CCode", "cprefix", cprefix);
2120 if (lower_case_cprefix == null) {
2121 ns.set_attribute_string ("CCode", "lower_case_cprefix", Symbol.camel_case_to_lower_case (cprefix) + "_");
2125 if (lower_case_cprefix != null) {
2126 ns.set_attribute_string ("CCode", "lower_case_cprefix", lower_case_cprefix);
2129 if (cheader_filenames != null) {
2130 ns.set_attribute_string ("CCode", "cheader_filename", string.joinv (",", cheader_filenames));
2133 next ();
2134 while (current_token == MarkupTokenType.START_ELEMENT) {
2135 if (!push_metadata ()) {
2136 skip_element ();
2137 continue;
2140 if (reader.name == "alias") {
2141 parse_alias ();
2142 } else if (reader.name == "enumeration") {
2143 if (metadata.has_argument (ArgumentType.ERRORDOMAIN)) {
2144 if (metadata.get_bool (ArgumentType.ERRORDOMAIN)) {
2145 parse_error_domain ();
2146 } else {
2147 parse_enumeration ();
2149 } else {
2150 if ((reader.get_attribute ("glib:error-quark") != null) || (reader.get_attribute ("glib:error-domain") != null)) {
2151 parse_error_domain ();
2152 } else {
2153 parse_enumeration ();
2156 } else if (reader.name == "bitfield") {
2157 parse_bitfield ();
2158 } else if (reader.name == "function") {
2159 parse_method ("function");
2160 } else if (reader.name == "callback") {
2161 parse_callback ();
2162 } else if (reader.name == "record") {
2163 if (metadata.has_argument (ArgumentType.STRUCT)) {
2164 if (metadata.get_bool (ArgumentType.STRUCT)) {
2165 parse_record ();
2166 } else {
2167 parse_boxed ("record");
2169 } else if (element_get_type_id () != null) {
2170 parse_boxed ("record");
2171 } else if (!reader.get_attribute ("name").has_suffix ("Private")) {
2172 if (reader.get_attribute ("glib:is-gtype-struct-for") == null && reader.get_attribute ("disguised") == "1") {
2173 parse_boxed ("record");
2174 } else {
2175 parse_record ();
2177 } else {
2178 skip_element ();
2180 } else if (reader.name == "class") {
2181 parse_class ();
2182 } else if (reader.name == "interface") {
2183 parse_interface ();
2184 } else if (reader.name == "glib:boxed") {
2185 parse_boxed ("glib:boxed");
2186 } else if (reader.name == "union") {
2187 if (element_get_type_id () != null && !metadata.get_bool (ArgumentType.STRUCT)) {
2188 parse_boxed ("union");
2189 } else {
2190 parse_union ();
2192 } else if (reader.name == "constant") {
2193 parse_constant ();
2194 } else {
2195 // error
2196 Report.error (get_current_src (), "unknown child element `%s' in `namespace'".printf (reader.name));
2197 skip_element ();
2200 pop_metadata ();
2202 pop_node ();
2203 end_element ("namespace");
2206 void parse_alias () {
2207 start_element ("alias");
2208 push_node (element_get_name (), true);
2209 // not enough information, symbol will be created while processing the tree
2211 next ();
2213 if (current.comment == null) {
2214 current.comment = parse_symbol_doc ();
2215 } else {
2216 parse_symbol_doc ();
2219 bool no_array_length = false;
2220 bool array_null_terminated = false;
2221 current.base_type = element_get_type (parse_type (null, null, true), true, ref no_array_length, ref array_null_terminated);
2223 if (metadata.has_argument (ArgumentType.BASE_TYPE)) {
2224 current.base_type = parse_type_from_string (metadata.get_string (ArgumentType.BASE_TYPE), true, metadata.get_source_reference (ArgumentType.BASE_TYPE));
2227 pop_node ();
2228 end_element ("alias");
2231 private void calculate_common_prefix (ref string? common_prefix, string cname) {
2232 if (common_prefix == null) {
2233 common_prefix = cname;
2234 while (common_prefix.length > 0 && !common_prefix.has_suffix ("_")) {
2235 // FIXME: could easily be made faster
2236 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2238 } else {
2239 while (!cname.has_prefix (common_prefix)) {
2240 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2243 while (common_prefix.length > 0 && (!common_prefix.has_suffix ("_") ||
2244 (cname.get_char (common_prefix.length).isdigit ()) && (cname.length - common_prefix.length) <= 1)) {
2245 // enum values may not consist solely of digits
2246 common_prefix = common_prefix.substring (0, common_prefix.length - 1);
2250 GirComment? parse_symbol_doc () {
2251 GirComment? comment = null;
2253 while (current_token == MarkupTokenType.START_ELEMENT) {
2254 unowned string reader_name = reader.name;
2256 if (reader_name == "doc") {
2257 start_element ("doc");
2258 next ();
2261 if (current_token == MarkupTokenType.TEXT) {
2262 comment = new GirComment (reader.content, current.source_reference);
2263 next ();
2266 end_element ("doc");
2267 } else if (reader_name == "doc-version" || reader_name == "doc-deprecated" || reader_name == "doc-stability") {
2268 skip_element ();
2269 } else {
2270 break;
2274 return comment;
2277 Comment? parse_doc () {
2278 Comment? comment = null;
2280 while (current_token == MarkupTokenType.START_ELEMENT) {
2281 unowned string reader_name = reader.name;
2283 if (reader_name == "doc") {
2284 start_element ("doc");
2285 next ();
2288 if (current_token == MarkupTokenType.TEXT) {
2289 comment = new Comment (reader.content, current.source_reference);
2290 next ();
2293 end_element ("doc");
2294 } else if (reader_name == "doc-version" || reader_name == "doc-deprecated" || reader_name == "doc-stability") {
2295 skip_element ();
2296 } else {
2297 break;
2301 return comment;
2304 void parse_enumeration (string element_name = "enumeration", bool error_domain = false) {
2305 start_element (element_name);
2306 push_node (element_get_name (), true);
2308 Symbol sym;
2309 if (current.new_symbol) {
2310 if (error_domain) {
2311 sym = new ErrorDomain (current.name, current.source_reference);
2312 } else {
2313 var en = new Enum (current.name, current.source_reference);
2314 if (element_name == "bitfield") {
2315 en.set_attribute ("Flags", true);
2317 sym = en;
2319 current.symbol = sym;
2320 } else {
2321 sym = current.symbol;
2324 if (!error_domain)
2325 set_type_id_ccode (sym);
2327 sym.external = true;
2328 sym.access = SymbolAccessibility.PUBLIC;
2330 string common_prefix = null;
2331 bool has_member = false;
2333 next ();
2335 sym.comment = parse_symbol_doc ();
2337 while (current_token == MarkupTokenType.START_ELEMENT) {
2338 if (!push_metadata ()) {
2339 skip_element ();
2340 continue;
2343 if (reader.name == "member") {
2344 has_member = true;
2345 if (error_domain) {
2346 parse_error_member ();
2347 calculate_common_prefix (ref common_prefix, old_current.get_cname ());
2348 } else {
2349 parse_enumeration_member ();
2350 calculate_common_prefix (ref common_prefix, old_current.get_cname ());
2352 } else if (reader.name == "function") {
2353 skip_element ();
2354 } else {
2355 // error
2356 Report.error (get_current_src (), "unknown child element `%s' in `%s'".printf (reader.name, element_name));
2357 skip_element ();
2360 pop_metadata ();
2363 if (!has_member) {
2364 Report.error (get_current_src (), "%s `%s' has no members".printf (element_name, current.name));
2367 if (common_prefix != null) {
2368 sym.set_attribute_string ("CCode", "cprefix", common_prefix);
2371 pop_node ();
2372 end_element (element_name);
2375 void parse_error_domain () {
2376 parse_enumeration ("enumeration", true);
2379 void parse_bitfield () {
2380 parse_enumeration ("bitfield");
2383 void parse_enumeration_member () {
2384 start_element ("member");
2385 push_node (element_get_name().ascii_up().replace ("-", "_"), false);
2387 var ev = new EnumValue (current.name, metadata.get_expression (ArgumentType.DEFAULT), current.source_reference);
2388 current.symbol = ev;
2389 next ();
2391 ev.comment = parse_symbol_doc ();
2393 pop_node ();
2394 end_element ("member");
2397 void parse_error_member () {
2398 start_element ("member");
2399 push_node (element_get_name().ascii_up().replace ("-", "_"), false);
2401 ErrorCode ec;
2402 string value = reader.get_attribute ("value");
2403 if (value != null) {
2404 ec = new ErrorCode.with_value (current.name, new IntegerLiteral (value));
2405 } else {
2406 ec = new ErrorCode (current.name);
2408 current.symbol = ec;
2409 next ();
2411 ec.comment = parse_symbol_doc ();
2413 pop_node ();
2414 end_element ("member");
2417 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) {
2418 start_element ("return-value");
2420 string transfer = reader.get_attribute ("transfer-ownership");
2421 string nullable = reader.get_attribute ("nullable");
2422 string allow_none = reader.get_attribute ("allow-none");
2423 next ();
2425 comment = parse_doc ();
2427 var transfer_elements = transfer != "container";
2428 var type = parse_type (out ctype, out array_length_idx, transfer_elements, out no_array_length, out array_null_terminated);
2429 if (transfer == "full" || transfer == "container") {
2430 type.value_owned = true;
2432 if (nullable == "1" || allow_none == "1") {
2433 type.nullable = true;
2435 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2437 end_element ("return-value");
2438 return type;
2441 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) {
2442 Parameter param;
2444 array_length_idx = -1;
2445 closure_idx = -1;
2446 destroy_idx = -1;
2448 string element_type = reader.name;
2449 if (current_token != MarkupTokenType.START_ELEMENT || (element_type != "parameter" && element_type != "instance-parameter")) {
2450 Report.error (get_current_src (), "expected start element of `parameter' or `instance-parameter'");
2452 start_element (element_type);
2453 var name = metadata.get_string (ArgumentType.NAME);
2454 if (name == null) {
2455 name = reader.get_attribute ("name");
2457 if (name == null) {
2458 name = default_name;
2460 string direction = null;
2461 if (metadata.has_argument (ArgumentType.OUT)) {
2462 if (metadata.get_bool (ArgumentType.OUT)) {
2463 direction = "out";
2464 } // null otherwise
2465 } else if (metadata.has_argument (ArgumentType.REF)) {
2466 if (metadata.get_bool (ArgumentType.REF)) {
2467 direction = "inout";
2468 } // null otherwise
2469 } else {
2470 direction = reader.get_attribute ("direction");
2472 string transfer = reader.get_attribute ("transfer-ownership");
2473 string nullable = reader.get_attribute ("nullable");
2474 string allow_none = reader.get_attribute ("allow-none");
2476 scope = element_get_string ("scope", ArgumentType.SCOPE);
2478 string closure = reader.get_attribute ("closure");
2479 string destroy = reader.get_attribute ("destroy");
2480 if (closure != null && &closure_idx != null) {
2481 closure_idx = int.parse (closure);
2483 if (destroy != null && &destroy_idx != null) {
2484 destroy_idx = int.parse (destroy);
2486 if (metadata.has_argument (ArgumentType.CLOSURE)) {
2487 closure_idx = metadata.get_integer (ArgumentType.CLOSURE);
2489 if (metadata.has_argument (ArgumentType.DESTROY)) {
2490 destroy_idx = metadata.get_integer (ArgumentType.DESTROY);
2493 next ();
2495 comment = parse_doc ();
2497 if (reader.name == "varargs") {
2498 start_element ("varargs");
2499 next ();
2500 param = new Parameter.with_ellipsis (get_current_src ());
2501 end_element ("varargs");
2502 } else {
2503 string ctype;
2504 bool no_array_length;
2505 bool array_null_terminated;
2506 var type = parse_type (out ctype, out array_length_idx, transfer != "container", out no_array_length, out array_null_terminated);
2507 if (transfer == "full" || transfer == "container" || destroy != null) {
2508 type.value_owned = true;
2510 if (nullable == "1" || (allow_none == "1" && direction != "out")) {
2511 type.nullable = true;
2514 bool changed;
2515 type = element_get_type (type, direction == "out" || direction == "inout", ref no_array_length, ref array_null_terminated, out changed);
2516 if (!changed) {
2517 // discard ctype, duplicated information
2518 ctype = null;
2521 param = new Parameter (name, type, get_current_src ());
2522 if (ctype != null) {
2523 param.set_attribute_string ("CCode", "type", ctype);
2525 if (direction == "out") {
2526 param.direction = ParameterDirection.OUT;
2527 } else if (direction == "inout") {
2528 param.direction = ParameterDirection.REF;
2530 if (type is ArrayType) {
2531 if (metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
2532 array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
2533 } else {
2534 if (no_array_length || array_null_terminated) {
2535 param.set_attribute_bool ("CCode", "array_length", !no_array_length);
2537 if (array_null_terminated) {
2538 param.set_attribute_bool ("CCode", "array_null_terminated", array_null_terminated);
2542 param.initializer = metadata.get_expression (ArgumentType.DEFAULT);
2544 // empty tuple used for parameters without initializer
2545 if (param.initializer is Tuple) {
2546 param.initializer = null;
2549 end_element (element_type);
2550 return param;
2553 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) {
2554 bool is_array = false;
2555 string type_name = reader.get_attribute ("name");
2556 ctype = null;
2558 var fixed_length = -1;
2559 array_length_idx = -1;
2560 no_array_length = true;
2561 array_null_terminated = true;
2563 if (reader.name == "array") {
2564 is_array = true;
2565 start_element ("array");
2567 var src = get_current_src ();
2569 if (type_name == null) {
2570 if (reader.get_attribute ("length") != null) {
2571 array_length_idx = int.parse (reader.get_attribute ("length"));
2572 no_array_length = false;
2573 array_null_terminated = false;
2575 if (reader.get_attribute ("fixed-size") != null) {
2576 fixed_length = int.parse (reader.get_attribute ("fixed-size"));
2577 array_null_terminated = false;
2579 if (reader.get_attribute ("c:type") == "GStrv") {
2580 no_array_length = true;
2581 array_null_terminated = true;
2583 if (reader.get_attribute ("zero-terminated") != null) {
2584 array_null_terminated = int.parse (reader.get_attribute ("zero-terminated")) != 0;
2586 next ();
2587 var element_type = parse_type ();
2588 element_type.value_owned = transfer_elements;
2589 end_element ("array");
2591 var array_type = new ArrayType (element_type, 1, src);
2592 if (fixed_length > 0) {
2593 array_type.fixed_length = true;
2594 array_type.length = new IntegerLiteral (fixed_length.to_string ());
2596 return array_type;
2598 } else if (reader.name == "callback"){
2599 parse_callback ();
2600 return new DelegateType ((Delegate) old_current.symbol);
2601 } else {
2602 start_element ("type");
2605 ctype = reader.get_attribute("c:type");
2607 next ();
2609 if (type_name == "GLib.PtrArray"
2610 && current_token == MarkupTokenType.START_ELEMENT) {
2611 type_name = "GLib.GenericArray";
2614 if (type_name == null) {
2615 type_name = ctype;
2618 DataType type = parse_type_from_gir_name (type_name, out no_array_length, out array_null_terminated, ctype);
2620 // type arguments / element types
2621 while (current_token == MarkupTokenType.START_ELEMENT) {
2622 if (type_name == "GLib.ByteArray") {
2623 skip_element ();
2624 continue;
2626 var element_type = parse_type ();
2627 element_type.value_owned = transfer_elements;
2628 type.add_type_argument (element_type);
2630 if (element_type is UnresolvedType) {
2631 Node parent = current ?? root;
2632 while (parent != root && parent.parent != null && parent.parent != root) {
2633 parent = parent.parent;
2635 unresolved_type_arguments[(UnresolvedType) element_type] = parent;
2639 end_element (is_array ? "array" : "type");
2640 return type;
2643 DataType parse_type_from_gir_name (string type_name, out bool no_array_length = null, out bool array_null_terminated = null, string? ctype = null) {
2644 no_array_length = false;
2645 array_null_terminated = false;
2647 DataType? type = null;
2648 if (type_name == "none") {
2649 type = new VoidType (get_current_src ());
2650 } else if (type_name == "gpointer") {
2651 type = new PointerType (new VoidType (get_current_src ()), get_current_src ());
2652 } else if (type_name == "GObject.Strv") {
2653 var element_type = new UnresolvedType.from_symbol (new UnresolvedSymbol (null, "string"));
2654 element_type.value_owned = true;
2655 type = new ArrayType (element_type, 1, get_current_src ());
2656 no_array_length = true;
2657 array_null_terminated = true;
2658 } else {
2659 bool known_type = true;
2660 if (type_name == "utf8") {
2661 type_name = "string";
2662 } else if (type_name == "gboolean") {
2663 type = new BooleanType ((Struct) context.root.scope.lookup ("bool"));
2664 } else if (type_name == "gchar") {
2665 type_name = "char";
2666 } else if (type_name == "gshort") {
2667 type_name = "short";
2668 } else if (type_name == "gushort") {
2669 type_name = "ushort";
2670 } else if (type_name == "gint") {
2671 type_name = "int";
2672 } else if (type_name == "guint") {
2673 type_name = "uint";
2674 } else if (type_name == "glong") {
2675 if (ctype != null && ctype.has_prefix ("gssize")) {
2676 type_name = "ssize_t";
2677 } else if (ctype != null && ctype.has_prefix ("gintptr")) {
2678 type_name = "intptr";
2679 } else {
2680 type_name = "long";
2682 } else if (type_name == "gulong") {
2683 if (ctype != null && ctype.has_prefix ("gsize")) {
2684 type_name = "size_t";
2685 } else if (ctype != null && ctype.has_prefix ("guintptr")) {
2686 type_name = "uintptr";
2687 } else {
2688 type_name = "ulong";
2690 } else if (type_name == "gint8") {
2691 type_name = "int8";
2692 } else if (type_name == "guint8") {
2693 type_name = "uint8";
2694 } else if (type_name == "gint16") {
2695 type_name = "int16";
2696 } else if (type_name == "guint16") {
2697 type_name = "uint16";
2698 } else if (type_name == "gint32") {
2699 type_name = "int32";
2700 } else if (type_name == "guint32") {
2701 type_name = "uint32";
2702 } else if (type_name == "gint64") {
2703 type_name = "int64";
2704 } else if (type_name == "guint64") {
2705 type_name = "uint64";
2706 } else if (type_name == "gfloat") {
2707 type_name = "float";
2708 } else if (type_name == "gdouble") {
2709 type_name = "double";
2710 } else if (type_name == "filename") {
2711 type_name = "string";
2712 } else if (type_name == "GLib.offset") {
2713 type_name = "int64";
2714 } else if (type_name == "gsize") {
2715 type_name = "size_t";
2716 } else if (type_name == "gssize") {
2717 type_name = "ssize_t";
2718 } else if (type_name == "guintptr") {
2719 type_name = "uintptr";
2720 } else if (type_name == "gintptr") {
2721 type_name = "intptr";
2722 } else if (type_name == "GType") {
2723 type_name = "GLib.Type";
2724 } else if (type_name == "GLib.String") {
2725 type_name = "GLib.StringBuilder";
2726 } else if (type_name == "GObject.Class") {
2727 type_name = "GLib.ObjectClass";
2728 } else if (type_name == "gunichar") {
2729 type_name = "unichar";
2730 } else if (type_name == "GLib.Data") {
2731 type_name = "GLib.Datalist";
2732 } else if (type_name == "Atk.ImplementorIface") {
2733 type_name = "Atk.Implementor";
2734 } else {
2735 known_type = false;
2738 if (type == null) {
2739 var sym = parse_symbol_from_string (type_name, get_current_src ());
2740 type = new UnresolvedType.from_symbol (sym, get_current_src ());
2741 if (!known_type) {
2742 unresolved_gir_symbols.add (sym);
2747 return type;
2750 void parse_record () {
2751 start_element ("record");
2752 push_node (element_get_name (), true);
2754 Struct st;
2755 if (current.new_symbol) {
2756 st = new Struct (element_get_name (), current.source_reference);
2757 current.symbol = st;
2758 } else {
2759 st = (Struct) current.symbol;
2762 set_type_id_ccode (st);
2764 st.external = true;
2765 st.access = SymbolAccessibility.PUBLIC;
2767 var gtype_struct_for = reader.get_attribute ("glib:is-gtype-struct-for");
2768 if (gtype_struct_for != null) {
2769 current.gtype_struct_for = parse_symbol_from_string (gtype_struct_for, current.source_reference);
2770 unresolved_gir_symbols.add (current.gtype_struct_for);
2773 bool first_field = true;
2774 next ();
2776 st.comment = parse_symbol_doc ();
2778 while (current_token == MarkupTokenType.START_ELEMENT) {
2779 if (!push_metadata ()) {
2780 if (first_field && reader.name == "field") {
2781 first_field = false;
2783 skip_element ();
2784 continue;
2787 if (reader.name == "field") {
2788 if (reader.get_attribute ("name") != "priv" && !(first_field && gtype_struct_for != null)) {
2789 parse_field ();
2790 } else {
2791 skip_element ();
2793 first_field = false;
2794 } else if (reader.name == "constructor") {
2795 parse_constructor ();
2796 } else if (reader.name == "method") {
2797 parse_method ("method");
2798 } else if (reader.name == "function") {
2799 skip_element ();
2800 } else if (reader.name == "union") {
2801 parse_union ();
2802 } else {
2803 // error
2804 Report.error (get_current_src (), "unknown child element `%s' in `record'".printf (reader.name));
2805 skip_element ();
2808 pop_metadata ();
2811 pop_node ();
2812 end_element ("record");
2815 void parse_class () {
2816 start_element ("class");
2817 push_node (element_get_name (), true);
2819 Class cl;
2820 var parent = reader.get_attribute ("parent");
2821 if (current.new_symbol) {
2822 cl = new Class (current.name, current.source_reference);
2823 cl.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT, reader.get_attribute ("abstract") == "1");
2825 if (parent != null) {
2826 cl.add_base_type (parse_type_from_gir_name (parent));
2828 current.symbol = cl;
2829 } else {
2830 cl = (Class) current.symbol;
2833 set_type_id_ccode (cl);
2835 cl.access = SymbolAccessibility.PUBLIC;
2836 cl.external = true;
2838 next ();
2840 cl.comment = parse_symbol_doc ();
2842 var first_field = true;
2843 while (current_token == MarkupTokenType.START_ELEMENT) {
2844 if (!push_metadata ()) {
2845 if (first_field && reader.name == "field") {
2846 first_field = false;
2848 skip_element ();
2849 continue;
2852 if (reader.name == "implements") {
2853 start_element ("implements");
2854 cl.add_base_type (parse_type_from_gir_name (reader.get_attribute ("name")));
2855 next ();
2856 end_element ("implements");
2857 } else if (reader.name == "constant") {
2858 parse_constant ();
2859 } else if (reader.name == "field") {
2860 if (first_field && parent != null) {
2861 // first field is guaranteed to be the parent instance
2862 skip_element ();
2863 } else {
2864 if (reader.get_attribute ("name") != "priv") {
2865 parse_field ();
2866 } else {
2867 skip_element ();
2870 first_field = false;
2871 } else if (reader.name == "property") {
2872 parse_property ();
2873 } else if (reader.name == "constructor") {
2874 parse_constructor ();
2875 } else if (reader.name == "function") {
2876 parse_method ("function");
2877 } else if (reader.name == "method") {
2878 parse_method ("method");
2879 } else if (reader.name == "virtual-method") {
2880 parse_method ("virtual-method");
2881 } else if (reader.name == "union") {
2882 parse_union ();
2883 } else if (reader.name == "glib:signal") {
2884 parse_signal ();
2885 } else {
2886 // error
2887 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
2888 skip_element ();
2891 pop_metadata ();
2894 pop_node ();
2895 end_element ("class");
2898 void parse_interface () {
2899 start_element ("interface");
2900 push_node (element_get_name (), true);
2902 Interface iface;
2903 if (current.new_symbol) {
2904 iface = new Interface (current.name, current.source_reference);
2905 current.symbol = iface;
2906 } else {
2907 iface = (Interface) current.symbol;
2910 set_type_id_ccode (iface);
2912 iface.access = SymbolAccessibility.PUBLIC;
2913 iface.external = true;
2916 next ();
2918 iface.comment = parse_symbol_doc ();
2920 while (current_token == MarkupTokenType.START_ELEMENT) {
2921 if (!push_metadata ()) {
2922 skip_element ();
2923 continue;
2926 if (reader.name == "prerequisite") {
2927 start_element ("prerequisite");
2928 iface.add_prerequisite (parse_type_from_gir_name (reader.get_attribute ("name")));
2929 next ();
2930 end_element ("prerequisite");
2931 } else if (reader.name == "field") {
2932 parse_field ();
2933 } else if (reader.name == "property") {
2934 parse_property ();
2935 } else if (reader.name == "virtual-method") {
2936 parse_method ("virtual-method");
2937 } else if (reader.name == "function") {
2938 parse_method ("function");
2939 } else if (reader.name == "method") {
2940 parse_method ("method");
2941 } else if (reader.name == "glib:signal") {
2942 parse_signal ();
2943 } else {
2944 // error
2945 Report.error (get_current_src (), "unknown child element `%s' in `interface'".printf (reader.name));
2946 skip_element ();
2949 pop_metadata ();
2952 pop_node ();
2953 end_element ("interface");
2956 void parse_field () {
2957 start_element ("field");
2958 push_node (element_get_name (), false);
2960 string nullable = reader.get_attribute ("nullable");
2961 string allow_none = reader.get_attribute ("allow-none");
2962 next ();
2964 var comment = parse_symbol_doc ();
2966 bool no_array_length;
2967 bool array_null_terminated;
2968 int array_length_idx;
2969 var type = parse_type (null, out array_length_idx, true, out no_array_length, out array_null_terminated);
2970 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
2972 string name = current.name;
2973 string cname = current.girdata["name"];
2975 var field = new Field (name, type, null, current.source_reference);
2976 field.access = SymbolAccessibility.PUBLIC;
2977 field.comment = comment;
2978 if (name != cname) {
2979 field.set_attribute_string ("CCode", "cname", cname);
2981 if (type is ArrayType) {
2982 if (!no_array_length && array_length_idx > -1) {
2983 current.array_length_idx = array_length_idx;
2985 if (no_array_length || array_null_terminated) {
2986 field.set_attribute_bool ("CCode", "array_length", !no_array_length);
2988 if (array_null_terminated) {
2989 field.set_attribute_bool ("CCode", "array_null_terminated", true);
2992 if (nullable == "1" || allow_none == "1") {
2993 type.nullable = true;
2995 current.symbol = field;
2997 pop_node ();
2998 end_element ("field");
3001 Property parse_property () {
3002 start_element ("property");
3003 push_node (element_get_name().replace ("-", "_"), false);
3004 bool is_abstract = metadata.get_bool (ArgumentType.ABSTRACT, current.parent.symbol is Interface);
3005 string transfer = reader.get_attribute ("transfer-ownership");
3007 next ();
3009 var comment = parse_symbol_doc ();
3011 bool no_array_length;
3012 bool array_null_terminated;
3013 var type = parse_type (null, null, transfer != "container", out no_array_length, out array_null_terminated);
3014 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
3015 var prop = new Property (current.name, type, null, null, current.source_reference);
3016 prop.comment = comment;
3017 prop.access = SymbolAccessibility.PUBLIC;
3018 prop.external = true;
3019 prop.is_abstract = is_abstract;
3020 if (no_array_length || array_null_terminated) {
3021 prop.set_attribute_bool ("CCode", "array_length", !no_array_length);
3023 if (array_null_terminated) {
3024 prop.set_attribute_bool ("CCode", "array_null_terminated", true);
3026 current.symbol = prop;
3028 pop_node ();
3029 end_element ("property");
3030 return prop;
3033 void parse_callback () {
3034 parse_function ("callback");
3037 void parse_constructor () {
3038 parse_function ("constructor");
3041 class ParameterInfo {
3042 public ParameterInfo (Parameter param, int array_length_idx, int closure_idx, int destroy_idx, bool is_async = false) {
3043 this.param = param;
3044 this.array_length_idx = array_length_idx;
3045 this.closure_idx = closure_idx;
3046 this.destroy_idx = destroy_idx;
3047 this.vala_idx = 0.0F;
3048 this.keep = true;
3049 this.is_async = is_async;
3052 public Parameter param;
3053 public float vala_idx;
3054 public int array_length_idx;
3055 public int closure_idx;
3056 public int destroy_idx;
3057 public bool keep;
3058 public bool is_async;
3061 void parse_function (string element_name) {
3062 start_element (element_name);
3063 push_node (element_get_name (reader.get_attribute ("invoker")).replace ("-", "_"), false);
3065 string symbol_type;
3066 if (metadata.has_argument (ArgumentType.SYMBOL_TYPE)) {
3067 symbol_type = metadata.get_string (ArgumentType.SYMBOL_TYPE);
3068 } else {
3069 symbol_type = element_name;
3072 string name = current.name;
3073 string throws_string = reader.get_attribute ("throws");
3074 string invoker = reader.get_attribute ("invoker");
3076 next ();
3078 var comment = parse_symbol_doc ();
3080 DataType return_type;
3081 string return_ctype = null;
3082 int return_array_length_idx = -1;
3083 bool return_no_array_length = false;
3084 bool return_array_null_terminated = false;
3085 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "return-value") {
3086 Comment? return_comment;
3087 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);
3088 if (return_comment != null) {
3089 if (comment == null) {
3090 comment = new GirComment (null, current.source_reference);
3092 comment.return_content = return_comment;
3094 } else {
3095 return_type = new VoidType ();
3098 Symbol s;
3100 if (symbol_type == "callback") {
3101 s = new Delegate (name, return_type, current.source_reference);
3102 ((Delegate) s).has_target = false;
3103 } else if (symbol_type == "constructor") {
3104 if (name == "new") {
3105 name = null;
3106 } else if (name.has_prefix ("new_")) {
3107 name = name.substring ("new_".length);
3109 var m = new CreationMethod (null, name, current.source_reference);
3110 m.has_construct_function = false;
3112 if (name != null && !current.name.has_prefix ("new_")) {
3113 m.set_attribute_string ("CCode", "cname", current.girdata["c:identifier"]);
3116 string parent_ctype = null;
3117 if (current.parent.symbol is Class) {
3118 parent_ctype = current.parent.get_cname ();
3120 if (return_ctype != null && (parent_ctype == null || return_ctype != parent_ctype + "*")) {
3121 m.set_attribute_string ("CCode", "type", return_ctype);
3123 s = m;
3124 } else if (symbol_type == "glib:signal") {
3125 s = new Signal (name, return_type, current.source_reference);
3126 } else {
3127 s = new Method (name, return_type, current.source_reference);
3130 s.access = SymbolAccessibility.PUBLIC;
3131 s.comment = comment;
3132 s.external = true;
3134 // Transform fixed-array properties of return-type into ccode-attribute
3135 var array_type = return_type as ArrayType;
3136 if (array_type != null && array_type.fixed_length) {
3137 s.set_attribute_string ("CCode", "array_length_cexpr", ((IntegerLiteral) array_type.length).value);
3138 array_type.fixed_length = false;
3139 array_type.length = null;
3142 if (s is Signal) {
3143 if (current.girdata["name"] != name.replace ("_", "-")) {
3144 s.set_attribute_string ("CCode", "cname", current.girdata["name"]);
3148 if (s is Method) {
3149 var m = (Method) s;
3150 if (symbol_type == "virtual-method" || symbol_type == "callback") {
3151 if (current.parent.symbol is Interface) {
3152 m.is_abstract = true;
3153 } else {
3154 m.is_virtual = true;
3156 if (invoker == null && !metadata.has_argument (ArgumentType.VFUNC_NAME)) {
3157 s.set_attribute ("NoWrapper", true, s.source_reference);
3158 } if (current.girdata["name"] != name) {
3159 m.set_attribute_string ("CCode", "vfunc_name", current.girdata["name"]);
3161 } else if (symbol_type == "function") {
3162 m.binding = MemberBinding.STATIC;
3164 if (metadata.has_argument (ArgumentType.FLOATING)) {
3165 m.returns_floating_reference = metadata.get_bool (ArgumentType.FLOATING);
3166 m.return_type.value_owned = true;
3170 if (s is Method && !(s is CreationMethod)) {
3171 var method = (Method) s;
3172 if (metadata.has_argument (ArgumentType.VIRTUAL)) {
3173 method.is_virtual = metadata.get_bool (ArgumentType.VIRTUAL);
3174 method.is_abstract = false;
3175 } else if (metadata.has_argument (ArgumentType.ABSTRACT)) {
3176 method.is_abstract = metadata.get_bool (ArgumentType.ABSTRACT);
3177 method.is_virtual = false;
3179 if (metadata.has_argument (ArgumentType.VFUNC_NAME)) {
3180 method.set_attribute_string ("CCode", "vfunc_name", metadata.get_string (ArgumentType.VFUNC_NAME));
3181 method.is_virtual = true;
3183 if (metadata.has_argument (ArgumentType.FINISH_VFUNC_NAME)) {
3184 method.set_attribute_string ("CCode", "finish_vfunc_name", metadata.get_string (ArgumentType.FINISH_VFUNC_NAME));
3185 method.is_virtual = true;
3189 if (!(metadata.get_expression (ArgumentType.THROWS) is NullLiteral)) {
3190 if (metadata.has_argument (ArgumentType.THROWS)) {
3191 var error_types = metadata.get_string(ArgumentType.THROWS).split(",");
3192 foreach (var error_type in error_types) {
3193 s.add_error_type (parse_type_from_string (error_type, true, metadata.get_source_reference (ArgumentType.THROWS)));
3195 } else if (throws_string == "1") {
3196 s.add_error_type (new ErrorType (null, null));
3200 if (s is Method) {
3201 var m = (Method) s;
3202 m.set_attribute ("PrintfFormat", metadata.get_bool (ArgumentType.PRINTF_FORMAT));
3203 if (metadata.has_argument (ArgumentType.SENTINEL)) {
3204 m.set_attribute_string ("CCode", "sentinel", metadata.get_string (ArgumentType.SENTINEL));
3208 if (return_type is ArrayType && metadata.has_argument (ArgumentType.ARRAY_LENGTH_IDX)) {
3209 return_array_length_idx = metadata.get_integer (ArgumentType.ARRAY_LENGTH_IDX);
3210 } else {
3211 if (return_no_array_length || return_array_null_terminated) {
3212 s.set_attribute_bool ("CCode", "array_length", !return_no_array_length);
3214 if (return_array_null_terminated) {
3215 s.set_attribute_bool ("CCode", "array_null_terminated", true);
3218 current.return_array_length_idx = return_array_length_idx;
3220 current.symbol = s;
3222 if (metadata.has_argument (ArgumentType.FINISH_NAME)) {
3223 s.set_attribute_string ("CCode", "finish_name", metadata.get_string (ArgumentType.FINISH_NAME));
3225 if (metadata.has_argument (ArgumentType.FINISH_INSTANCE)) {
3226 s.set_attribute_bool ("CCode", "finish_instance", metadata.get_bool (ArgumentType.FINISH_INSTANCE));
3229 int instance_idx = -2;
3230 if (element_name == "function" && symbol_type == "method") {
3231 if (metadata.has_argument (ArgumentType.INSTANCE_IDX)) {
3232 instance_idx = metadata.get_integer (ArgumentType.INSTANCE_IDX);
3233 if (instance_idx != 0) {
3234 s.set_attribute_double ("CCode", "instance_pos", instance_idx + 0.5);
3236 } else {
3237 Report.error (get_current_src (), "instance_idx required when converting function to method");
3241 var parameters = new ArrayList<ParameterInfo> ();
3242 current.array_length_parameters = new ArrayList<int> ();
3243 current.closure_parameters = new ArrayList<int> ();
3244 current.destroy_parameters = new ArrayList<int> ();
3245 if (current_token == MarkupTokenType.START_ELEMENT && reader.name == "parameters") {
3246 start_element ("parameters");
3247 next ();
3249 var current_parameter_idx = -1;
3250 while (current_token == MarkupTokenType.START_ELEMENT) {
3251 current_parameter_idx++;
3253 if (reader.name == "instance-parameter" &&
3254 !(symbol_type == "function" || symbol_type == "constructor")) {
3255 skip_element ();
3256 continue;
3259 if (instance_idx > -2 && instance_idx == current_parameter_idx) {
3260 skip_element ();
3261 continue;
3264 if (!push_metadata ()) {
3265 skip_element ();
3266 continue;
3269 int array_length_idx, closure_idx, destroy_idx;
3270 string scope;
3271 string default_param_name = null;
3272 Comment? param_comment;
3273 default_param_name = "arg%d".printf (parameters.size);
3274 var param = parse_parameter (out array_length_idx, out closure_idx, out destroy_idx, out scope, out param_comment, default_param_name);
3275 if (array_length_idx != -1) {
3276 if (instance_idx > -2 && instance_idx < array_length_idx) {
3277 array_length_idx--;
3279 current.array_length_parameters.add (array_length_idx);
3281 if (closure_idx != -1) {
3282 if (instance_idx > -2 && instance_idx < closure_idx) {
3283 closure_idx--;
3285 if (current.closure_parameters.index_of (current_parameter_idx) < 0) {
3286 current.closure_parameters.add (closure_idx);
3289 if (destroy_idx != -1) {
3290 if (instance_idx > -2 && instance_idx < destroy_idx) {
3291 destroy_idx--;
3293 if (current.destroy_parameters.index_of (current_parameter_idx) < 0) {
3294 current.destroy_parameters.add (destroy_idx);
3297 if (param_comment != null) {
3298 if (comment == null) {
3299 comment = new GirComment (null, s.source_reference);
3300 s.comment = comment;
3303 comment.add_content_for_parameter ((param.ellipsis)? "..." : param.name, param_comment);
3306 var info = new ParameterInfo (param, array_length_idx, closure_idx, destroy_idx, scope == "async");
3308 if (s is Method && scope == "async") {
3309 var unresolved_type = param.variable_type as UnresolvedType;
3310 if (unresolved_type != null && unresolved_type.unresolved_symbol.name == "AsyncReadyCallback") {
3311 // GAsync-style method
3312 ((Method) s).coroutine = true;
3313 info.keep = false;
3317 parameters.add (info);
3318 pop_metadata ();
3320 end_element ("parameters");
3322 current.parameters = parameters;
3324 for (int param_n = parameters.size - 1 ; param_n >= 0 ; param_n--) {
3325 ParameterInfo pi = parameters[param_n];
3326 if (!pi.param.ellipsis && pi.param.initializer == null) {
3327 string type_string = pi.param.variable_type.to_string ();
3328 if (type_string == "Gio.Cancellable?") {
3329 pi.param.initializer = new Vala.NullLiteral ();
3330 } else {
3331 break;
3336 pop_node ();
3337 end_element (element_name);
3340 void parse_method (string element_name) {
3341 parse_function (element_name);
3344 void parse_signal () {
3345 parse_function ("glib:signal");
3348 void parse_boxed (string element_name) {
3349 start_element (element_name);
3350 string name = reader.get_attribute ("name");
3351 if (name == null) {
3352 name = reader.get_attribute ("glib:name");
3354 push_node (element_get_name (name), true);
3356 Class cl;
3357 bool require_copy_free = false;
3358 if (current.new_symbol) {
3359 cl = new Class (current.name, current.source_reference);
3360 cl.is_compact = true;
3361 current.symbol = cl;
3362 } else {
3363 cl = (Class) current.symbol;
3366 set_type_id_ccode (cl);
3367 require_copy_free = cl.has_attribute_argument ("CCode", "type_id");
3369 cl.access = SymbolAccessibility.PUBLIC;
3370 cl.external = true;
3372 if (metadata.has_argument (ArgumentType.BASE_TYPE)) {
3373 cl.add_base_type (parse_type_from_string (metadata.get_string (ArgumentType.BASE_TYPE), true, metadata.get_source_reference (ArgumentType.BASE_TYPE)));
3376 next ();
3378 cl.comment = parse_symbol_doc ();
3380 Node? ref_method = null;
3381 Node? unref_method = null;
3383 while (current_token == MarkupTokenType.START_ELEMENT) {
3384 if (!push_metadata ()) {
3385 skip_element ();
3386 continue;
3389 if (reader.name == "field") {
3390 parse_field ();
3391 } else if (reader.name == "constructor") {
3392 parse_constructor ();
3393 } else if (reader.name == "method") {
3394 parse_method ("method");
3395 var cname = old_current.get_cname ();
3396 if (cname.has_suffix ("_ref") && (ref_method == null || old_current.name == "ref")) {
3397 ref_method = old_current;
3398 } else if (cname.has_suffix ("_unref") && (unref_method == null || old_current.name == "unref")) {
3399 unref_method = old_current;
3401 } else if (reader.name == "function") {
3402 skip_element ();
3403 } else if (reader.name == "union") {
3404 parse_union ();
3405 } else {
3406 // error
3407 Report.error (get_current_src (), "unknown child element `%s' in `class'".printf (reader.name));
3408 skip_element ();
3411 pop_metadata ();
3414 // Add ccode-attributes for ref/unref methodes if available
3415 // otherwise fallback to default g_boxed_copy/free
3416 if (cl.has_attribute_argument ("CCode", "ref_function") || cl.has_attribute_argument ("CCode", "unref_function")
3417 || cl.has_attribute_argument ("CCode", "copy_function") || cl.has_attribute_argument ("CCode", "free_function")) {
3418 //do nothing
3419 } else if (ref_method != null && unref_method != null) {
3420 cl.set_attribute_string ("CCode", "ref_function", ref_method.get_cname ());
3421 cl.set_attribute_string ("CCode", "unref_function", unref_method.get_cname ());
3422 } else if (require_copy_free) {
3423 cl.set_attribute_string ("CCode", "copy_function", "g_boxed_copy");
3424 cl.set_attribute_string ("CCode", "free_function", "g_boxed_free");
3427 pop_node ();
3428 end_element (element_name);
3431 void parse_union () {
3432 start_element ("union");
3434 string? element_name = element_get_name ();
3435 if (element_name == null) {
3436 next ();
3438 while (current_token == MarkupTokenType.START_ELEMENT) {
3439 if (!push_metadata ()) {
3440 skip_element ();
3441 continue;
3444 if (reader.name == "field") {
3445 parse_field ();
3446 } else {
3447 // error
3448 Report.error (get_current_src (), "unknown child element `%s' in `transparent union'".printf (reader.name));
3449 skip_element ();
3452 pop_metadata ();
3455 end_element ("union");
3456 return;
3459 push_node (element_name, true);
3461 Struct st;
3462 if (current.new_symbol) {
3463 st = new Struct (reader.get_attribute ("name"), current.source_reference);
3464 current.symbol = st;
3465 } else {
3466 st = (Struct) current.symbol;
3468 st.access = SymbolAccessibility.PUBLIC;
3469 st.external = true;
3471 next ();
3473 st.comment = parse_symbol_doc ();
3475 while (current_token == MarkupTokenType.START_ELEMENT) {
3476 if (!push_metadata ()) {
3477 skip_element ();
3478 continue;
3481 if (reader.name == "field") {
3482 parse_field ();
3483 } else if (reader.name == "constructor") {
3484 parse_constructor ();
3485 } else if (reader.name == "method") {
3486 parse_method ("method");
3487 } else if (reader.name == "function") {
3488 skip_element ();
3489 } else if (reader.name == "record") {
3490 parse_record ();
3491 } else {
3492 // error
3493 Report.error (get_current_src (), "unknown child element `%s' in `union'".printf (reader.name));
3494 skip_element ();
3497 pop_metadata ();
3500 pop_node ();
3501 end_element ("union");
3504 void parse_constant () {
3505 start_element ("constant");
3506 push_node (element_get_name (), false);
3508 next ();
3510 var comment = parse_symbol_doc ();
3512 bool no_array_length;
3513 bool array_null_terminated;
3514 int array_length_idx;
3515 var type = parse_type (null, out array_length_idx, true, out no_array_length, out array_null_terminated);
3516 type = element_get_type (type, true, ref no_array_length, ref array_null_terminated);
3517 var c = new Constant (current.name, type, null, current.source_reference);
3518 current.symbol = c;
3519 c.access = SymbolAccessibility.PUBLIC;
3520 c.comment = comment;
3521 c.external = true;
3522 if (no_array_length || array_null_terminated) {
3523 c.set_attribute_bool ("CCode", "array_length", !no_array_length);
3525 if (array_null_terminated) {
3526 c.set_attribute_bool ("CCode", "array_null_terminated", true);
3529 pop_node ();
3530 end_element ("constant");
3533 /* Reporting */
3534 void report_unused_metadata (Metadata metadata) {
3535 if (metadata == Metadata.empty) {
3536 return;
3539 if (metadata.args.size == 0 && metadata.children.size == 0) {
3540 Report.warning (metadata.source_reference, "empty metadata");
3541 return;
3544 foreach (var arg_type in metadata.args.get_keys ()) {
3545 var arg = metadata.args[arg_type];
3546 if (!arg.used) {
3547 // if metadata is used and argument is not, then it's a unexpected argument
3548 Report.warning (arg.source_reference, "argument never used");
3552 foreach (var child in metadata.children) {
3553 if (!child.used) {
3554 Report.warning (child.source_reference, "metadata never used");
3555 } else {
3556 report_unused_metadata (child);
3561 /* Post-parsing */
3563 void resolve_gir_symbols () {
3564 // gir has simple namespaces, we won't get deeper than 2 levels here, except reparenting
3565 foreach (var map_from in unresolved_gir_symbols) {
3566 while (map_from != null) {
3567 var map_to = unresolved_symbols_map[map_from];
3568 if (map_to != null) {
3569 // remap the original symbol to match the target
3570 map_from.inner = null;
3571 map_from.name = map_to.name;
3572 if (map_to is UnresolvedSymbol) {
3573 var umap_to = (UnresolvedSymbol) map_to;
3574 while (umap_to.inner != null) {
3575 umap_to = umap_to.inner;
3576 map_from.inner = new UnresolvedSymbol (null, umap_to.name);
3577 map_from = map_from.inner;
3579 } else {
3580 while (map_to.parent_symbol != null && map_to.parent_symbol != context.root) {
3581 map_to = map_to.parent_symbol;
3582 map_from.inner = new UnresolvedSymbol (null, map_to.name);
3583 map_from = map_from.inner;
3586 break;
3588 map_from = map_from.inner;
3593 void create_new_namespaces () {
3594 foreach (var node in Node.new_namespaces) {
3595 if (node.symbol == null) {
3596 node.symbol = new Namespace (node.name, node.source_reference);
3601 void resolve_type_arguments () {
3602 var it = unresolved_type_arguments.map_iterator ();
3603 while (it.next ()) {
3604 var element_type = it.get_key ();
3605 var parent = it.get_value ();
3606 var sym = (TypeSymbol) resolve_symbol (parent, element_type.unresolved_symbol);
3608 // box structs in type arguments
3609 var st = sym as Struct;
3610 if (st != null && !st.is_integer_type () && !st.is_floating_type ()) {
3611 element_type.nullable = true;
3616 void process_interface (Node iface_node) {
3617 /* Temporarily workaround G-I bug not adding GLib.Object prerequisite:
3618 ensure we have at least one instantiable prerequisite */
3619 Interface iface = (Interface) iface_node.symbol;
3620 bool has_instantiable_prereq = false;
3621 foreach (DataType prereq in iface.get_prerequisites ()) {
3622 Symbol sym = null;
3623 if (prereq is UnresolvedType) {
3624 var unresolved_symbol = ((UnresolvedType) prereq).unresolved_symbol;
3625 sym = resolve_symbol (iface_node.parent, unresolved_symbol);
3626 } else {
3627 sym = prereq.data_type;
3629 if (sym is Class) {
3630 has_instantiable_prereq = true;
3631 break;
3635 if (!has_instantiable_prereq) {
3636 iface.add_prerequisite (new ObjectType ((ObjectTypeSymbol) glib_ns.scope.lookup ("Object")));
3640 void process_alias (Node alias) {
3641 /* this is unfortunate because <alias> tag has no type information, thus we have
3642 to guess it from the base type */
3643 DataType base_type = null;
3644 Symbol type_sym = null;
3645 Node base_node = null;
3646 bool simple_type = false;
3647 if (alias.base_type is UnresolvedType) {
3648 base_type = alias.base_type;
3649 base_node = resolve_node (alias.parent, ((UnresolvedType) base_type).unresolved_symbol);
3650 if (base_node != null) {
3651 type_sym = base_node.symbol;
3653 } else if (alias.base_type is PointerType && ((PointerType) alias.base_type).base_type is VoidType) {
3654 // gpointer, if it's a struct make it a simpletype
3655 simple_type = true;
3656 } else {
3657 base_type = alias.base_type;
3658 type_sym = base_type.data_type;
3659 if (type_sym != null) {
3660 base_node = resolve_node (alias.parent, parse_symbol_from_string (type_sym.get_full_name (), alias.source_reference));
3664 if (type_sym is Struct && ((Struct) type_sym).is_simple_type ()) {
3665 simple_type = true;
3668 if (base_type == null || type_sym == null || type_sym is Struct) {
3669 var st = new Struct (alias.name, alias.source_reference);
3670 st.access = SymbolAccessibility.PUBLIC;
3671 if (base_type != null) {
3672 // threat target="none" as a new struct
3673 st.base_type = base_type;
3675 st.comment = alias.comment;
3676 st.external = true;
3677 st.set_simple_type (simple_type);
3678 alias.symbol = st;
3679 } else if (type_sym is Class) {
3680 var cl = new Class (alias.name, alias.source_reference);
3681 cl.access = SymbolAccessibility.PUBLIC;
3682 if (base_type != null) {
3683 cl.add_base_type (base_type);
3685 cl.comment = alias.comment;
3686 cl.external = true;
3687 cl.is_compact = ((Class) type_sym).is_compact;
3688 alias.symbol = cl;
3689 } else if (type_sym is Interface) {
3690 // this is not a correct alias, but what can we do otherwise?
3691 var iface = new Interface (alias.name, alias.source_reference);
3692 iface.access = SymbolAccessibility.PUBLIC;
3693 if (base_type != null) {
3694 iface.add_prerequisite (base_type);
3696 iface.comment = alias.comment;
3697 iface.external = true;
3698 alias.symbol = iface;
3699 } else if (type_sym is Delegate) {
3700 var orig = (Delegate) type_sym;
3701 if (base_node != null) {
3702 base_node.process (this);
3703 orig = (Delegate) base_node.symbol;
3706 var deleg = new Delegate (alias.name, orig.return_type.copy (), alias.source_reference);
3707 deleg.access = orig.access;
3709 foreach (var param in orig.get_parameters ()) {
3710 deleg.add_parameter (param.copy ());
3713 foreach (var error_type in orig.get_error_types ()) {
3714 deleg.add_error_type (error_type.copy ());
3717 foreach (var attribute in orig.attributes) {
3718 deleg.attributes.append (attribute);
3721 deleg.external = true;
3723 alias.symbol = deleg;
3724 } else if (type_sym != null) {
3725 Report.warning (alias.source_reference, "alias `%s' for `%s' is not supported".printf (alias.get_full_name (), type_sym.get_full_name ()));
3726 alias.symbol = type_sym;
3727 alias.merged = true;
3730 // inherit atributes, like type_id
3731 if (type_sym is Class || (type_sym is Struct && !simple_type)) {
3732 if (type_sym.has_attribute_argument ("CCode", "has_type_id")) {
3733 alias.symbol.set_attribute_bool ("CCode", "has_type_id", type_sym.get_attribute_bool ("CCode", "has_type_id"));
3734 } else if (type_sym.has_attribute_argument ("CCode", "type_id")) {
3735 alias.symbol.set_attribute_string ("CCode", "type_id", type_sym.get_attribute_string ("CCode", "type_id"));
3740 void process_callable (Node node) {
3741 if (node.element_type == "alias" && node.symbol is Delegate) {
3742 // processed in parse_alias
3743 return;
3746 var s = node.symbol;
3747 List<ParameterInfo> parameters = node.parameters;
3749 DataType return_type = null;
3750 if (s is Callable) {
3751 return_type = ((Callable) s).return_type;
3754 if (return_type is ArrayType && node.return_array_length_idx >= 0) {
3755 if (node.return_array_length_idx >= parameters.size) {
3756 Report.error (return_type.source_reference, "invalid array length index");
3757 } else {
3758 parameters[node.return_array_length_idx].keep = false;
3759 node.array_length_parameters.add (node.return_array_length_idx);
3761 } else if (return_type is VoidType && parameters.size > 0) {
3762 int n_out_parameters = 0;
3763 foreach (var info in parameters) {
3764 if (info.param.direction == ParameterDirection.OUT) {
3765 n_out_parameters++;
3769 if (n_out_parameters == 1) {
3770 ParameterInfo last_param = parameters[parameters.size-1];
3771 if (last_param.param.direction == ParameterDirection.OUT) {
3772 // use last out real-non-null-struct parameter as return type
3773 if (last_param.param.variable_type is UnresolvedType) {
3774 var st = resolve_symbol (node.parent, ((UnresolvedType) last_param.param.variable_type).unresolved_symbol) as Struct;
3775 if (st != null && !st.is_simple_type () && !last_param.param.variable_type.nullable) {
3776 if (!node.metadata.get_bool (ArgumentType.RETURN_VOID, false)) {
3777 last_param.keep = false;
3778 return_type = last_param.param.variable_type.copy ();
3784 } else {
3785 if (return_type is UnresolvedType && !return_type.nullable) {
3786 var st = resolve_symbol (node.parent, ((UnresolvedType) return_type).unresolved_symbol) as Struct;
3787 if (st != null) {
3788 bool is_simple_type = false;
3789 Struct? base_st = st;
3791 while (base_st != null) {
3792 if (base_st.is_simple_type ()) {
3793 is_simple_type = true;
3794 break;
3797 if (base_st.base_type is UnresolvedType) {
3798 base_st = resolve_symbol (node.parent, ((UnresolvedType) base_st.base_type).unresolved_symbol) as Struct;
3799 } else {
3800 base_st = base_st.base_struct;
3804 if (!is_simple_type) {
3805 return_type.nullable = true;
3811 // Do not mark out-parameters as nullable if they are simple-types,
3812 // since it would result in a boxed-type in vala
3813 foreach (ParameterInfo info in parameters) {
3814 var type = info.param.variable_type;
3815 if (info.param.direction == ParameterDirection.OUT && type.nullable) {
3816 Struct? st = null;
3817 if (type is UnresolvedType) {
3818 st = resolve_symbol (node.parent, ((UnresolvedType) type).unresolved_symbol) as Struct;
3819 } else if (type is ValueType) {
3820 st = type.data_type as Struct;
3822 if (st != null && st.is_simple_type ()) {
3823 type.nullable = false;
3828 if (parameters.size > 1) {
3829 ParameterInfo last_param = parameters[parameters.size-1];
3830 if (last_param.param.ellipsis) {
3831 var first_vararg_param = parameters[parameters.size-2];
3832 if (first_vararg_param.param.name.has_prefix ("first_")) {
3833 first_vararg_param.keep = false;
3838 int i = 0, j=1;
3840 int last = -1;
3841 foreach (ParameterInfo info in parameters) {
3842 if (s is Delegate && info.closure_idx == i) {
3843 var d = (Delegate) s;
3844 d.has_target = true;
3845 d.set_attribute_double ("CCode", "instance_pos", j - 0.1);
3846 info.keep = false;
3847 } else if (info.keep
3848 && !node.array_length_parameters.contains (i)
3849 && !node.closure_parameters.contains (i)
3850 && !node.destroy_parameters.contains (i)) {
3851 info.vala_idx = (float) j;
3852 info.keep = true;
3854 /* interpolate for vala_idx between this and last*/
3855 float last_idx = 0.0F;
3856 if (last != -1) {
3857 last_idx = parameters[last].vala_idx;
3859 for (int k=last+1; k < i; k++) {
3860 parameters[k].vala_idx = last_idx + (((j - last_idx) / (i-last)) * (k-last));
3862 last = i;
3863 j++;
3864 } else {
3865 info.keep = false;
3866 // make sure that vala_idx is always set
3867 // the above if branch does not set vala_idx for
3868 // hidden parameters at the end of the parameter list
3869 info.vala_idx = (j - 1) + (i - last) * 0.1F;
3871 i++;
3874 foreach (ParameterInfo info in parameters) {
3875 if (!info.keep) {
3876 continue;
3879 /* add_parameter sets carray_length_parameter_position and cdelegate_target_parameter_position
3880 so do it first*/
3881 if (s is Callable) {
3882 ((Callable) s).add_parameter (info.param);
3885 if (info.array_length_idx != -1) {
3886 if ((info.array_length_idx) >= parameters.size) {
3887 Report.error (info.param.source_reference, "invalid array_length index");
3888 continue;
3890 set_array_ccode (info.param, parameters[info.array_length_idx]);
3893 if (info.closure_idx != -1) {
3894 if ((info.closure_idx) >= parameters.size) {
3895 Report.error (info.param.source_reference, "invalid closure index");
3896 continue;
3898 if ("%g".printf (parameters[info.closure_idx].vala_idx) != "%g".printf (info.vala_idx + 0.1)) {
3899 info.param.set_attribute_double ("CCode", "delegate_target_pos", parameters[info.closure_idx].vala_idx);
3902 if (info.destroy_idx != -1) {
3903 if (info.destroy_idx >= parameters.size) {
3904 Report.error (info.param.source_reference, "invalid destroy index");
3905 continue;
3907 if ("%g".printf (parameters[info.destroy_idx].vala_idx) != "%g".printf (info.vala_idx + 0.2)) {
3908 info.param.set_attribute_double ("CCode", "destroy_notify_pos", parameters[info.destroy_idx].vala_idx);
3912 if (info.is_async) {
3913 var resolved_type = info.param.variable_type;
3914 if (resolved_type is UnresolvedType) {
3915 var resolved_symbol = resolve_symbol (node.parent, ((UnresolvedType) resolved_type).unresolved_symbol);
3916 if (resolved_symbol is Delegate) {
3917 resolved_type = new DelegateType ((Delegate) resolved_symbol);
3921 if (resolved_type is DelegateType) {
3922 var d = ((DelegateType) resolved_type).delegate_symbol;
3923 if (!(d.name == "DestroyNotify" && d.parent_symbol.name == "GLib")) {
3924 info.param.set_attribute_string ("CCode", "scope", "async");
3925 info.param.variable_type.value_owned = (info.closure_idx != -1 && info.destroy_idx != -1);
3928 } else {
3929 var resolved_type = info.param.variable_type;
3930 if (resolved_type is DelegateType) {
3931 info.param.variable_type.value_owned = (info.closure_idx != -1 && info.destroy_idx != -1);
3936 if (return_type is ArrayType && node.return_array_length_idx >= 0) {
3937 set_array_ccode (s, parameters[node.return_array_length_idx]);
3940 if (s is Callable) {
3941 ((Callable) s).return_type = return_type;
3945 void find_parent (string cname, Node current, ref Node best, ref int match) {
3946 var old_best = best;
3947 if (current.symbol is Namespace) {
3948 foreach (var child in current.members) {
3949 // symbol is null only for aliases that aren't yet processed
3950 if ((child.symbol == null || is_container (child.symbol)) && cname.has_prefix (child.get_lower_case_cprefix ())) {
3951 find_parent (cname, child, ref best, ref match);
3955 if (best != old_best) {
3956 // child is better
3957 return;
3960 var current_match = current.get_lower_case_cprefix().length;
3961 if (current_match > match) {
3962 match = current_match;
3963 best = current;
3967 bool same_gir (Symbol gir_component, Symbol sym) {
3968 var gir_name = gir_component.source_reference.file.gir_namespace;
3969 var gir_version = gir_component.source_reference.file.gir_version;
3970 return "%s-%s".printf (gir_name, gir_version) in sym.source_reference.file.filename;
3973 void process_namespace_method (Node ns, Node node) {
3974 /* transform static methods into instance methods if possible.
3975 In most of cases this is a .gir fault we are going to fix */
3977 var ns_cprefix = ns.get_lower_case_cprefix ();
3978 var method = (Method) node.symbol;
3979 var cname = node.get_cname ();
3981 Parameter first_param = null;
3982 if (node.parameters.size > 0) {
3983 first_param = node.parameters[0].param;
3985 if (first_param != null && first_param.direction == ParameterDirection.IN && first_param.variable_type is UnresolvedType) {
3986 // check if it's a missed instance method (often happens for structs)
3987 var sym = ((UnresolvedType) first_param.variable_type).unresolved_symbol;
3988 var parent = resolve_node (ns, sym);
3989 if (parent != null && same_gir (method, parent.symbol) && parent.parent == ns && is_container (parent.symbol) && cname.has_prefix (parent.get_lower_case_cprefix ())) {
3990 // instance method
3991 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
3992 if (parent.lookup (new_name) == null) {
3993 ns.remove_member (node);
3994 node.name = new_name;
3995 node.parameters.remove_at (0);
3996 method.name = new_name;
3997 method.binding = MemberBinding.INSTANCE;
3998 parent.add_member (node);
4000 return;
4004 int match = 0;
4005 Node parent = ns;
4006 find_parent (cname, ns, ref parent, ref match);
4007 var new_name = method.name.substring (parent.get_lower_case_cprefix().length - ns_cprefix.length);
4008 if (same_gir (method, parent.symbol) && parent.lookup (new_name) == null) {
4009 ns.remove_member (node);
4010 node.name = new_name;
4011 method.name = new_name;
4012 parent.add_member (node);
4016 void process_virtual_method_field (Node node, Delegate d, UnresolvedSymbol gtype_struct_for) {
4017 var gtype_node = resolve_node (node.parent, gtype_struct_for);
4018 if (gtype_node == null || !(gtype_node.symbol is ObjectTypeSymbol)) {
4019 Report.error (gtype_struct_for.source_reference, "Unknown symbol `%s' for virtual method field `%s'".printf (gtype_struct_for.to_string (), node.to_string ()));
4021 var nodes = gtype_node.lookup_all (d.name);
4022 if (nodes == null) {
4023 return;
4025 foreach (var n in nodes) {
4026 if (node != n) {
4027 n.process (this);
4030 foreach (var n in nodes) {
4031 if (n.merged) {
4032 continue;
4034 var sym = n.symbol;
4035 if (sym is Signal) {
4036 var sig = (Signal) sym;
4037 sig.is_virtual = true;
4038 assume_parameter_names (sig, d, true);
4039 } else if (sym is Property) {
4040 var prop = (Property) sym;
4041 prop.is_virtual = true;
4046 void process_async_method (Node node) {
4047 var m = (Method) node.symbol;
4048 string finish_method_base;
4049 if (m.name == null) {
4050 assert (m is CreationMethod);
4051 finish_method_base = "new";
4052 } else if (m.name.has_suffix ("_async")) {
4053 finish_method_base = m.name.substring (0, m.name.length - "_async".length);
4054 } else {
4055 finish_method_base = m.name;
4057 var finish_method_node = node.parent.lookup (finish_method_base + "_finish");
4059 // check if the method is using non-standard finish method name
4060 if (finish_method_node == null) {
4061 var method_cname = node.get_finish_cname ();
4062 foreach (var n in node.parent.members) {
4063 if (n.symbol is Method && n.get_cname () == method_cname) {
4064 finish_method_node = n;
4065 break;
4070 Method method = m;
4072 if (finish_method_node != null && finish_method_node.symbol is Method) {
4073 finish_method_node.process (this);
4074 var finish_method = (Method) finish_method_node.symbol;
4075 if (finish_method is CreationMethod) {
4076 method = new CreationMethod (((CreationMethod) finish_method).class_name, null, m.source_reference);
4077 method.access = m.access;
4078 method.binding = m.binding;
4079 method.external = true;
4080 method.coroutine = true;
4081 method.has_construct_function = finish_method.has_construct_function;
4083 // cannot use List.copy()
4084 // as it returns a list of unowned elements
4085 foreach (Attribute a in m.attributes) {
4086 method.attributes.append (a);
4089 method.set_attribute_string ("CCode", "cname", node.get_cname ());
4090 if (finish_method_base == "new") {
4091 method.name = null;
4092 } else if (finish_method_base.has_prefix ("new_")) {
4093 method.name = m.name.substring ("new_".length);
4095 foreach (var param in m.get_parameters ()) {
4096 method.add_parameter (param);
4098 node.symbol = method;
4099 } else {
4100 method.return_type = finish_method.return_type.copy ();
4101 var a = finish_method.get_attribute ("CCode");
4102 if (a != null && a.has_argument ("array_length")) {
4103 method.set_attribute_bool ("CCode", "array_length", a.get_bool ("array_length"));
4105 if (a != null && a.has_argument ("array_null_terminated")) {
4106 method.set_attribute_bool ("CCode", "array_null_terminated", a.get_bool ("array_null_terminated"));
4110 foreach (var param in finish_method.get_parameters ()) {
4111 if (param.direction == ParameterDirection.OUT) {
4112 var async_param = param.copy ();
4113 if (method.scope.lookup (param.name) != null) {
4114 // parameter name conflict
4115 async_param.name += "_out";
4117 method.add_parameter (async_param);
4121 foreach (DataType error_type in finish_method.get_error_types ()) {
4122 method.add_error_type (error_type.copy ());
4124 finish_method_node.processed = true;
4125 finish_method_node.merged = true;
4129 /* Hash and equal functions */
4131 static uint unresolved_symbol_hash (UnresolvedSymbol? sym) {
4132 var builder = new StringBuilder ();
4133 while (sym != null) {
4134 builder.append (sym.name);
4135 sym = sym.inner;
4137 return builder.str.hash ();
4140 static bool unresolved_symbol_equal (UnresolvedSymbol? sym1, UnresolvedSymbol? sym2) {
4141 while (sym1 != sym2) {
4142 if (sym1 == null || sym2 == null) {
4143 return false;
4145 if (sym1.name != sym2.name) {
4146 return false;
4148 sym1 = sym1.inner;
4149 sym2 = sym2.inner;
4151 return true;
4154 /* Helper methods */
4156 Node? base_interface_property (Node prop_node) {
4157 var cl = prop_node.parent.symbol as Class;
4158 if (cl == null) {
4159 return null;
4162 foreach (DataType type in cl.get_base_types ()) {
4163 if (!(type is UnresolvedType)) {
4164 continue;
4167 var base_node = resolve_node (prop_node.parent, ((UnresolvedType) type).unresolved_symbol);
4168 if (base_node != null && base_node.symbol is Interface) {
4169 var base_prop_node = base_node.lookup (prop_node.name);
4170 if (base_prop_node != null && base_prop_node.symbol is Property) {
4171 var base_property = (Property) base_prop_node.symbol;
4172 if (base_property.is_abstract || base_property.is_virtual) {
4173 // found
4174 return base_prop_node;
4180 return null;