vala: Allow read-only properties
[vala-gnome.git] / vala / valacodewriter.vala
blob4535f88739bb2cec47cde1bce648d65d0d4ec379
1 /* valacodewriter.vala
3 * Copyright (C) 2006-2014 Jürg Billeter
4 * Copyright (C) 2006-2008 Raffaele Sandrini
5 * Copyright (C) 2014 Richard Wiedenhöft
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 * Author:
22 * Jürg Billeter <j@bitron.ch>
23 * Raffaele Sandrini <raffaele@sandrini.ch>
27 /**
28 * Code visitor generating Vala API file for the public interface.
30 public class Vala.CodeWriter : CodeVisitor {
31 static GLib.Regex fix_indent_regex;
33 private CodeContext context;
35 FileStream stream;
37 int indent;
38 /* at begin of line */
39 bool bol = true;
41 Scope current_scope;
43 CodeWriterType type;
45 string? override_header = null;
46 string? header_to_override = null;
48 public CodeWriter (CodeWriterType type = CodeWriterType.EXTERNAL) {
49 this.type = type;
52 /**
53 * Allows overriding of a specific cheader in the output
54 * @param original orignal cheader to override
55 * @param replacement cheader to replace original with
57 public void set_cheader_override (string original, string replacement)
59 header_to_override = original;
60 override_header = replacement;
63 /**
64 * Writes the public interface of the specified code context into the
65 * specified file.
67 * @param context a code context
68 * @param filename a relative or absolute filename
70 public void write_file (CodeContext context, string filename) {
71 var file_exists = FileUtils.test (filename, FileTest.EXISTS);
72 var temp_filename = "%s.valatmp".printf (filename);
73 this.context = context;
75 if (file_exists) {
76 stream = FileStream.open (temp_filename, "w");
77 } else {
78 stream = FileStream.open (filename, "w");
81 if (stream == null) {
82 Report.error (null, "unable to open `%s' for writing".printf (filename));
83 return;
86 var header = context.version_header ?
87 "/* %s generated by %s %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname (), Config.BUILD_VERSION) :
88 "/* %s generated by %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname ());
89 write_string (header);
90 write_newline ();
91 write_newline ();
93 current_scope = context.root.scope;
95 context.accept (this);
97 current_scope = null;
99 stream = null;
101 if (file_exists) {
102 var changed = true;
104 try {
105 var old_file = new MappedFile (filename, false);
106 var new_file = new MappedFile (temp_filename, false);
107 var len = old_file.get_length ();
108 if (len == new_file.get_length ()) {
109 if (Memory.cmp (old_file.get_contents (), new_file.get_contents (), len) == 0) {
110 changed = false;
113 old_file = null;
114 new_file = null;
115 } catch (FileError e) {
116 // assume changed if mmap comparison doesn't work
119 if (changed) {
120 FileUtils.rename (temp_filename, filename);
121 } else {
122 FileUtils.unlink (temp_filename);
128 public override void visit_using_directive (UsingDirective ns) {
129 if (type == CodeWriterType.FAST) {
130 write_string ("using ");
132 var symbols = new GLib.List<UnresolvedSymbol> ();
133 var sym = (UnresolvedSymbol) ns.namespace_symbol;
134 symbols.prepend (sym);
136 while ((sym = sym.inner) != null) {
137 symbols.prepend (sym);
140 write_string (symbols.nth_data (0).name);
142 for (int i = 1; i < symbols.length (); i++) {
143 write_string (".");
144 write_string (symbols.nth_data (i).name);
147 write_string (";\n");
151 public override void visit_namespace (Namespace ns) {
152 if (ns.external_package) {
153 return;
156 if (ns.name == null) {
157 ns.accept_children (this);
158 return;
161 var comments = ns.get_comments ();
162 if (context.vapi_comments && comments.size > 0) {
163 bool first = true;
164 SourceReference? first_reference = null;
165 foreach (Comment comment in comments) {
166 if (comment.source_reference.file.file_type == SourceFileType.SOURCE) {
167 if (first) {
168 write_comment (comment);
169 first = false;
170 first_reference = comment.source_reference;
171 } else {
172 Report.warning (comment.source_reference, "Comment describes namespace, that was already described by another comment.");
173 Report.notice (first_reference, "Previous comment was here.");
179 write_attributes (ns);
181 write_indent ();
182 write_string ("namespace ");
183 write_identifier (ns.name);
184 write_begin_block ();
186 current_scope = ns.scope;
188 visit_sorted (ns.get_namespaces ());
189 visit_sorted (ns.get_classes ());
190 visit_sorted (ns.get_interfaces ());
191 visit_sorted (ns.get_structs ());
192 visit_sorted (ns.get_enums ());
193 visit_sorted (ns.get_error_domains ());
194 visit_sorted (ns.get_delegates ());
195 visit_sorted (ns.get_fields ());
196 visit_sorted (ns.get_constants ());
197 visit_sorted (ns.get_methods ());
199 current_scope = current_scope.parent_scope;
201 write_end_block ();
202 write_newline ();
205 private string get_cheaders (Symbol sym) {
206 string cheaders = "";
207 if (type != CodeWriterType.FAST && !sym.external_package) {
208 cheaders = sym.get_attribute_string ("CCode", "cheader_filename") ?? "";
209 if (cheaders == "" && sym.parent_symbol != null && sym.parent_symbol != context.root) {
210 cheaders = get_cheaders (sym.parent_symbol);
212 if (cheaders == "" && sym.source_reference != null && !sym.external_package) {
213 cheaders = sym.source_reference.file.get_cinclude_filename ();
216 if (header_to_override != null) {
217 var cheaders_array = cheaders.split (",");
218 for (int i = 0; i < cheaders_array.length; i++) {
219 if (cheaders_array[i] == header_to_override) {
220 cheaders_array[i] = override_header;
223 cheaders = string.joinv (",", cheaders_array);
226 return cheaders;
229 public override void visit_class (Class cl) {
230 if (cl.external_package) {
231 return;
234 if (!check_accessibility (cl)) {
235 return;
238 if (context.vapi_comments && cl.comment != null) {
239 write_comment (cl.comment);
242 write_attributes (cl);
244 write_indent ();
245 write_accessibility (cl);
246 if (cl.is_abstract) {
247 write_string ("abstract ");
249 write_string ("class ");
250 write_identifier (cl.name);
252 write_type_parameters (cl.get_type_parameters ());
254 var base_types = cl.get_base_types ();
255 if (base_types.size > 0) {
256 write_string (" : ");
258 bool first = true;
259 foreach (DataType base_type in base_types) {
260 if (!first) {
261 write_string (", ");
262 } else {
263 first = false;
265 write_type (base_type);
268 write_begin_block ();
270 current_scope = cl.scope;
272 visit_sorted (cl.get_classes ());
273 visit_sorted (cl.get_structs ());
274 visit_sorted (cl.get_enums ());
275 visit_sorted (cl.get_delegates ());
276 visit_sorted (cl.get_fields ());
277 visit_sorted (cl.get_constants ());
278 visit_sorted (cl.get_methods ());
279 visit_sorted (cl.get_properties ());
280 visit_sorted (cl.get_signals ());
282 if (cl.constructor != null) {
283 cl.constructor.accept (this);
286 current_scope = current_scope.parent_scope;
288 write_end_block ();
289 write_newline ();
292 void visit_sorted (List<Symbol> symbols) {
293 if (type != CodeWriterType.EXTERNAL) {
294 // order of virtual methods matters for fast vapis
295 foreach (Symbol sym in symbols) {
296 sym.accept (this);
298 return;
301 var sorted_symbols = new ArrayList<Symbol> ();
302 foreach (Symbol sym in symbols) {
303 int left = 0;
304 int right = sorted_symbols.size - 1;
305 if (left > right || sym.name < sorted_symbols[left].name) {
306 sorted_symbols.insert (0, sym);
307 } else if (sym.name > sorted_symbols[right].name) {
308 sorted_symbols.add (sym);
309 } else {
310 while (right - left > 1) {
311 int i = (right + left) / 2;
312 if (sym.name > sorted_symbols[i].name) {
313 left = i;
314 } else {
315 right = i;
318 sorted_symbols.insert (left + 1, sym);
321 foreach (Symbol sym in sorted_symbols) {
322 sym.accept (this);
326 public override void visit_struct (Struct st) {
327 if (st.external_package) {
328 return;
331 if (!check_accessibility (st)) {
332 return;
335 if (context.vapi_comments && st.comment != null) {
336 write_comment (st.comment);
339 write_attributes (st);
341 write_indent ();
342 write_accessibility (st);
343 write_string ("struct ");
344 write_identifier (st.name);
346 write_type_parameters (st.get_type_parameters ());
348 if (st.base_type != null) {
349 write_string (" : ");
350 write_type (st.base_type);
353 write_begin_block ();
355 current_scope = st.scope;
357 foreach (Field field in st.get_fields ()) {
358 field.accept (this);
360 visit_sorted (st.get_constants ());
361 visit_sorted (st.get_methods ());
362 visit_sorted (st.get_properties ());
364 current_scope = current_scope.parent_scope;
366 write_end_block ();
367 write_newline ();
370 public override void visit_interface (Interface iface) {
371 if (iface.external_package) {
372 return;
375 if (!check_accessibility (iface)) {
376 return;
379 if (context.vapi_comments && iface.comment != null) {
380 write_comment (iface.comment);
383 write_attributes (iface);
385 write_indent ();
386 write_accessibility (iface);
387 write_string ("interface ");
388 write_identifier (iface.name);
390 write_type_parameters (iface.get_type_parameters ());
392 var prerequisites = iface.get_prerequisites ();
393 if (prerequisites.size > 0) {
394 write_string (" : ");
396 bool first = true;
397 foreach (DataType prerequisite in prerequisites) {
398 if (!first) {
399 write_string (", ");
400 } else {
401 first = false;
403 write_type (prerequisite);
406 write_begin_block ();
408 current_scope = iface.scope;
410 visit_sorted (iface.get_classes ());
411 visit_sorted (iface.get_structs ());
412 visit_sorted (iface.get_enums ());
413 visit_sorted (iface.get_delegates ());
414 visit_sorted (iface.get_fields ());
415 visit_sorted (iface.get_constants ());
416 visit_sorted (iface.get_methods ());
417 visit_sorted (iface.get_properties ());
418 visit_sorted (iface.get_signals ());
420 current_scope = current_scope.parent_scope;
422 write_end_block ();
423 write_newline ();
426 public override void visit_enum (Enum en) {
427 if (en.external_package) {
428 return;
431 if (!check_accessibility (en)) {
432 return;
435 if (context.vapi_comments && en.comment != null) {
436 write_comment (en.comment);
439 write_attributes (en);
441 write_indent ();
442 write_accessibility (en);
443 write_string ("enum ");
444 write_identifier (en.name);
445 write_begin_block ();
447 bool first = true;
448 foreach (EnumValue ev in en.get_values ()) {
449 if (first) {
450 first = false;
451 } else {
452 write_string (",");
453 write_newline ();
456 if (context.vapi_comments && ev.comment != null) {
457 write_comment (ev.comment);
460 write_attributes (ev);
462 write_indent ();
463 write_identifier (ev.name);
465 if (type == CodeWriterType.FAST && ev.value != null) {
466 write_string(" = ");
467 ev.value.accept (this);
471 if (!first) {
472 if (en.get_methods ().size > 0 || en.get_constants ().size > 0) {
473 write_string (";");
475 write_newline ();
478 current_scope = en.scope;
479 foreach (Method m in en.get_methods ()) {
480 m.accept (this);
482 foreach (Constant c in en.get_constants ()) {
483 c.accept (this);
485 current_scope = current_scope.parent_scope;
487 write_end_block ();
488 write_newline ();
491 public override void visit_error_domain (ErrorDomain edomain) {
492 if (edomain.external_package) {
493 return;
496 if (!check_accessibility (edomain)) {
497 return;
500 if (context.vapi_comments && edomain.comment != null) {
501 write_comment (edomain.comment);
504 write_attributes (edomain);
506 write_indent ();
507 write_accessibility (edomain);
508 write_string ("errordomain ");
509 write_identifier (edomain.name);
510 write_begin_block ();
512 bool first = true;
513 foreach (ErrorCode ecode in edomain.get_codes ()) {
514 if (first) {
515 first = false;
516 } else {
517 write_string (",");
518 write_newline ();
521 if (context.vapi_comments && ecode.comment != null) {
522 write_comment (ecode.comment);
525 write_attributes (ecode);
527 write_indent ();
528 write_identifier (ecode.name);
531 if (!first) {
532 if (edomain.get_methods ().size > 0) {
533 write_string (";");
535 write_newline ();
538 current_scope = edomain.scope;
539 foreach (Method m in edomain.get_methods ()) {
540 m.accept (this);
542 current_scope = current_scope.parent_scope;
544 write_end_block ();
545 write_newline ();
548 public override void visit_constant (Constant c) {
549 if (c.external_package) {
550 return;
553 if (!check_accessibility (c)) {
554 return;
557 if (context.vapi_comments && c.comment != null) {
558 write_comment (c.comment);
561 write_attributes (c);
563 write_indent ();
564 write_accessibility (c);
566 if (c.hides) {
567 write_string ("new ");
570 write_string ("const ");
572 write_type (c.type_reference);
574 write_string (" ");
575 write_identifier (c.name);
576 write_type_suffix (c.type_reference);
577 if (type == CodeWriterType.FAST && c.value != null) {
578 write_string(" = ");
579 c.value.accept (this);
581 write_string (";");
582 write_newline ();
585 public override void visit_field (Field f) {
586 if (f.external_package) {
587 return;
590 if (!check_accessibility (f)) {
591 return;
594 if (context.vapi_comments && f.comment != null) {
595 write_comment (f.comment);
598 write_attributes (f);
600 write_indent ();
601 write_accessibility (f);
603 if (f.hides) {
604 write_string ("new ");
607 if (f.binding == MemberBinding.STATIC) {
608 write_string ("static ");
609 } else if (f.binding == MemberBinding.CLASS) {
610 write_string ("class ");
613 if (f.variable_type.is_weak ()) {
614 write_string ("weak ");
617 write_type (f.variable_type);
619 write_string (" ");
620 write_identifier (f.name);
621 write_type_suffix (f.variable_type);
622 write_string (";");
623 write_newline ();
626 private void write_error_domains (List<DataType> error_domains) {
627 if (error_domains.size > 0) {
628 write_string (" throws ");
630 bool first = true;
631 foreach (DataType type in error_domains) {
632 if (!first) {
633 write_string (", ");
634 } else {
635 first = false;
638 write_type (type);
643 private void write_params (List<Parameter> params) {
644 write_string ("(");
646 int i = 1;
647 foreach (Parameter param in params) {
648 if (i > 1) {
649 write_string (", ");
652 if (param.ellipsis) {
653 write_string ("...");
654 continue;
657 write_attributes (param);
659 if (param.params_array) {
660 write_string ("params ");
663 if (param.direction == ParameterDirection.IN) {
664 if (param.variable_type.value_owned) {
665 write_string ("owned ");
667 } else {
668 if (param.direction == ParameterDirection.REF) {
669 write_string ("ref ");
670 } else if (param.direction == ParameterDirection.OUT) {
671 write_string ("out ");
673 if (param.variable_type.is_weak ()) {
674 write_string ("unowned ");
678 write_type (param.variable_type);
680 write_string (" ");
681 write_identifier (param.name);
682 write_type_suffix (param.variable_type);
684 if (param.initializer != null) {
685 write_string (" = ");
686 param.initializer.accept (this);
689 i++;
692 write_string (")");
695 public override void visit_delegate (Delegate cb) {
696 if (cb.external_package) {
697 return;
700 if (!check_accessibility (cb)) {
701 return;
704 if (context.vapi_comments && cb.comment != null) {
705 write_comment (cb.comment);
708 write_attributes (cb);
710 write_indent ();
712 write_accessibility (cb);
713 write_string ("delegate ");
715 write_return_type (cb.return_type);
717 write_string (" ");
718 write_identifier (cb.name);
720 write_type_parameters (cb.get_type_parameters ());
722 write_string (" ");
724 write_params (cb.get_parameters ());
726 write_error_domains (cb.get_error_types ());
728 write_string (";");
730 write_newline ();
733 public override void visit_constructor (Constructor c) {
734 if (type != CodeWriterType.DUMP) {
735 return;
738 if (context.vapi_comments && c.comment != null) {
739 write_comment (c.comment);
742 write_indent ();
743 write_string ("construct");
744 write_code_block (c.body);
745 write_newline ();
748 public override void visit_method (Method m) {
749 if (m.external_package) {
750 return;
753 // don't write interface implementation unless it's an abstract or virtual method
754 if (!check_accessibility (m) || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
755 if (type != CodeWriterType.DUMP) {
756 return;
760 if (context.vapi_comments && m.comment != null) {
761 write_comment (m.comment);
764 write_attributes (m);
766 write_indent ();
767 write_accessibility (m);
769 if (m is CreationMethod) {
770 if (m.coroutine) {
771 write_string ("async ");
774 var datatype = (TypeSymbol) m.parent_symbol;
775 write_identifier (datatype.name);
776 if (m.name != ".new") {
777 write_string (".");
778 write_identifier (m.name);
780 write_string (" ");
781 } else {
782 if (m.hides) {
783 write_string ("new ");
786 if (m.binding == MemberBinding.STATIC) {
787 write_string ("static ");
788 } else if (m.binding == MemberBinding.CLASS) {
789 write_string ("class ");
790 } else if (m.is_abstract) {
791 write_string ("abstract ");
792 } else if (m.is_virtual) {
793 write_string ("virtual ");
794 } else if (m.overrides) {
795 write_string ("override ");
798 if (m.coroutine) {
799 write_string ("async ");
802 write_return_type (m.return_type);
803 write_string (" ");
805 write_identifier (m.name);
807 write_type_parameters (m.get_type_parameters ());
809 write_string (" ");
812 write_params (m.get_parameters ());
814 write_error_domains (m.get_error_types ());
816 write_code_block (m.body);
818 write_newline ();
821 public override void visit_creation_method (CreationMethod m) {
822 visit_method (m);
825 public override void visit_property (Property prop) {
826 if (!check_accessibility (prop) || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
827 return;
830 if (context.vapi_comments && prop.comment != null) {
831 write_comment (prop.comment);
834 write_attributes (prop);
836 write_indent ();
837 write_accessibility (prop);
839 if (prop.hides) {
840 write_string ("new ");
843 if (prop.binding == MemberBinding.STATIC) {
844 write_string ("static ");
845 } else if (prop.is_abstract) {
846 write_string ("abstract ");
847 } else if (prop.is_virtual) {
848 write_string ("virtual ");
849 } else if (prop.overrides) {
850 write_string ("override ");
853 write_type (prop.property_type);
855 write_string (" ");
856 write_identifier (prop.name);
857 write_string (" {");
858 if (prop.get_accessor != null) {
859 write_attributes (prop.get_accessor);
861 write_property_accessor_accessibility (prop.get_accessor);
863 if (prop.get_accessor.value_type.is_disposable ()) {
864 write_string (" owned");
867 write_string (" get");
868 write_code_block (prop.get_accessor.body);
870 if (prop.set_accessor != null) {
871 write_attributes (prop.set_accessor);
873 write_property_accessor_accessibility (prop.set_accessor);
875 if (prop.set_accessor.value_type.value_owned) {
876 write_string (" owned");
879 if (prop.set_accessor.writable) {
880 write_string (" set");
882 if (prop.set_accessor.construction) {
883 write_string (" construct");
885 write_code_block (prop.set_accessor.body);
887 write_string (" }");
888 write_newline ();
891 public override void visit_signal (Signal sig) {
892 if (!check_accessibility (sig)) {
893 return;
896 if (context.vapi_comments && sig.comment != null) {
897 write_comment (sig.comment);
900 write_attributes (sig);
902 write_indent ();
903 write_accessibility (sig);
905 if (sig.hides) {
906 write_string ("new ");
909 if (sig.is_virtual) {
910 write_string ("virtual ");
913 write_string ("signal ");
915 write_return_type (sig.return_type);
917 write_string (" ");
918 write_identifier (sig.name);
920 write_string (" ");
922 write_params (sig.get_parameters ());
924 write_string (";");
926 write_newline ();
929 public override void visit_block (Block b) {
930 write_begin_block ();
932 foreach (Statement stmt in b.get_statements ()) {
933 stmt.accept (this);
936 write_end_block ();
939 public override void visit_empty_statement (EmptyStatement stmt) {
942 public override void visit_declaration_statement (DeclarationStatement stmt) {
943 write_indent ();
944 stmt.declaration.accept (this);
945 write_string (";");
946 write_newline ();
949 public override void visit_local_variable (LocalVariable local) {
950 if (local.variable_type.is_weak ()) {
951 write_string ("unowned ");
953 write_type (local.variable_type);
954 write_string (" ");
955 write_identifier (local.name);
956 write_type_suffix (local.variable_type);
957 if (local.initializer != null) {
958 write_string (" = ");
959 local.initializer.accept (this);
963 public override void visit_initializer_list (InitializerList list) {
964 write_string ("{");
966 bool first = true;
967 foreach (Expression initializer in list.get_initializers ()) {
968 if (!first) {
969 write_string (", ");
970 } else {
971 write_string (" ");
973 first = false;
974 initializer.accept (this);
976 write_string (" }");
979 public override void visit_expression_statement (ExpressionStatement stmt) {
980 write_indent ();
981 stmt.expression.accept (this);
982 write_string (";");
983 write_newline ();
986 public override void visit_if_statement (IfStatement stmt) {
987 write_indent ();
988 write_string ("if (");
989 stmt.condition.accept (this);
990 write_string (")");
991 stmt.true_statement.accept (this);
992 if (stmt.false_statement != null) {
993 write_string (" else");
994 stmt.false_statement.accept (this);
996 write_newline ();
999 public override void visit_switch_statement (SwitchStatement stmt) {
1000 write_indent ();
1001 write_string ("switch (");
1002 stmt.expression.accept (this);
1003 write_string (") {");
1004 write_newline ();
1006 foreach (SwitchSection section in stmt.get_sections ()) {
1007 section.accept (this);
1010 write_indent ();
1011 write_string ("}");
1012 write_newline ();
1015 public override void visit_switch_section (SwitchSection section) {
1016 foreach (SwitchLabel label in section.get_labels ()) {
1017 label.accept (this);
1020 visit_block (section);
1023 public override void visit_switch_label (SwitchLabel label) {
1024 if (label.expression != null) {
1025 write_indent ();
1026 write_string ("case ");
1027 label.expression.accept (this);
1028 write_string (":");
1029 write_newline ();
1030 } else {
1031 write_indent ();
1032 write_string ("default:");
1033 write_newline ();
1037 public override void visit_loop (Loop stmt) {
1038 write_indent ();
1039 write_string ("loop");
1040 stmt.body.accept (this);
1041 write_newline ();
1044 public override void visit_while_statement (WhileStatement stmt) {
1045 write_indent ();
1046 write_string ("while (");
1047 stmt.condition.accept (this);
1048 write_string (")");
1049 stmt.body.accept (this);
1050 write_newline ();
1053 public override void visit_do_statement (DoStatement stmt) {
1054 write_indent ();
1055 write_string ("do");
1056 stmt.body.accept (this);
1057 write_string ("while (");
1058 stmt.condition.accept (this);
1059 write_string (");");
1060 write_newline ();
1063 public override void visit_for_statement (ForStatement stmt) {
1064 write_indent ();
1065 write_string ("for (");
1067 bool first = true;
1068 foreach (Expression initializer in stmt.get_initializer ()) {
1069 if (!first) {
1070 write_string (", ");
1072 first = false;
1073 initializer.accept (this);
1075 write_string ("; ");
1077 stmt.condition.accept (this);
1078 write_string ("; ");
1080 first = true;
1081 foreach (Expression iterator in stmt.get_iterator ()) {
1082 if (!first) {
1083 write_string (", ");
1085 first = false;
1086 iterator.accept (this);
1089 write_string (")");
1090 stmt.body.accept (this);
1091 write_newline ();
1094 public override void visit_foreach_statement (ForeachStatement stmt) {
1097 public override void visit_break_statement (BreakStatement stmt) {
1098 write_indent ();
1099 write_string ("break;");
1100 write_newline ();
1103 public override void visit_continue_statement (ContinueStatement stmt) {
1104 write_indent ();
1105 write_string ("continue;");
1106 write_newline ();
1109 public override void visit_return_statement (ReturnStatement stmt) {
1110 write_indent ();
1111 write_string ("return");
1112 if (stmt.return_expression != null) {
1113 write_string (" ");
1114 stmt.return_expression.accept (this);
1116 write_string (";");
1117 write_newline ();
1120 public override void visit_yield_statement (YieldStatement y) {
1121 write_indent ();
1122 write_string ("yield");
1123 if (y.yield_expression != null) {
1124 write_string (" ");
1125 y.yield_expression.accept (this);
1127 write_string (";");
1128 write_newline ();
1131 public override void visit_throw_statement (ThrowStatement stmt) {
1132 write_indent ();
1133 write_string ("throw");
1134 if (stmt.error_expression != null) {
1135 write_string (" ");
1136 stmt.error_expression.accept (this);
1138 write_string (";");
1139 write_newline ();
1142 public override void visit_try_statement (TryStatement stmt) {
1143 write_indent ();
1144 write_string ("try");
1145 stmt.body.accept (this);
1146 foreach (var clause in stmt.get_catch_clauses ()) {
1147 clause.accept (this);
1149 if (stmt.finally_body != null) {
1150 write_string (" finally");
1151 stmt.finally_body.accept (this);
1153 write_newline ();
1156 public override void visit_catch_clause (CatchClause clause) {
1157 var type_name = clause.error_type == null ? "GLib.Error" : clause.error_type.to_string ();
1158 var var_name = clause.variable_name == null ? "_" : clause.variable_name;
1159 write_string (" catch (%s %s)".printf (type_name, var_name));
1160 clause.body.accept (this);
1163 public override void visit_lock_statement (LockStatement stmt) {
1164 write_indent ();
1165 write_string ("lock (");
1166 stmt.resource.accept (this);
1167 write_string (")");
1168 if (stmt.body == null) {
1169 write_string (";");
1170 } else {
1171 stmt.body.accept (this);
1173 write_newline ();
1176 public override void visit_delete_statement (DeleteStatement stmt) {
1177 write_indent ();
1178 write_string ("delete ");
1179 stmt.expression.accept (this);
1180 write_string (";");
1181 write_newline ();
1184 public override void visit_array_creation_expression (ArrayCreationExpression expr) {
1185 write_string ("new ");
1186 write_type (expr.element_type);
1187 write_string ("[");
1189 bool first = true;
1190 foreach (Expression size in expr.get_sizes ()) {
1191 if (!first) {
1192 write_string (", ");
1194 first = false;
1196 size.accept (this);
1199 write_string ("]");
1201 if (expr.initializer_list != null) {
1202 write_string (" ");
1203 expr.initializer_list.accept (this);
1207 public override void visit_boolean_literal (BooleanLiteral lit) {
1208 write_string (lit.value.to_string ());
1211 public override void visit_character_literal (CharacterLiteral lit) {
1212 write_string (lit.value);
1215 public override void visit_integer_literal (IntegerLiteral lit) {
1216 write_string (lit.value);
1219 public override void visit_real_literal (RealLiteral lit) {
1220 write_string (lit.value);
1223 public override void visit_string_literal (StringLiteral lit) {
1224 write_string (lit.value);
1227 public override void visit_null_literal (NullLiteral lit) {
1228 write_string ("null");
1231 public override void visit_member_access (MemberAccess expr) {
1232 if (expr.inner != null) {
1233 expr.inner.accept (this);
1234 write_string (".");
1236 write_identifier (expr.member_name);
1239 public override void visit_method_call (MethodCall expr) {
1240 expr.call.accept (this);
1241 write_string (" (");
1243 bool first = true;
1244 foreach (Expression arg in expr.get_argument_list ()) {
1245 if (!first) {
1246 write_string (", ");
1248 first = false;
1250 arg.accept (this);
1253 write_string (")");
1256 public override void visit_element_access (ElementAccess expr) {
1257 expr.container.accept (this);
1258 write_string ("[");
1260 bool first = true;
1261 foreach (Expression index in expr.get_indices ()) {
1262 if (!first) {
1263 write_string (", ");
1265 first = false;
1267 index.accept (this);
1270 write_string ("]");
1273 public override void visit_slice_expression (SliceExpression expr) {
1274 expr.container.accept (this);
1275 write_string ("[");
1276 expr.start.accept (this);
1277 write_string (":");
1278 expr.stop.accept (this);
1279 write_string ("]");
1282 public override void visit_base_access (BaseAccess expr) {
1283 write_string ("base");
1286 public override void visit_postfix_expression (PostfixExpression expr) {
1287 expr.inner.accept (this);
1288 if (expr.increment) {
1289 write_string ("++");
1290 } else {
1291 write_string ("--");
1295 public override void visit_object_creation_expression (ObjectCreationExpression expr) {
1296 if (!expr.struct_creation) {
1297 write_string ("new ");
1300 write_type (expr.type_reference);
1302 if (expr.symbol_reference.name != ".new") {
1303 write_string (".");
1304 write_string (expr.symbol_reference.name);
1307 write_string (" (");
1309 bool first = true;
1310 foreach (Expression arg in expr.get_argument_list ()) {
1311 if (!first) {
1312 write_string (", ");
1314 first = false;
1316 arg.accept (this);
1319 write_string (")");
1322 public override void visit_sizeof_expression (SizeofExpression expr) {
1323 write_string ("sizeof (");
1324 write_type (expr.type_reference);
1325 write_string (")");
1328 public override void visit_typeof_expression (TypeofExpression expr) {
1329 write_string ("typeof (");
1330 write_type (expr.type_reference);
1331 write_string (")");
1334 public override void visit_unary_expression (UnaryExpression expr) {
1335 switch (expr.operator) {
1336 case UnaryOperator.PLUS:
1337 write_string ("+");
1338 break;
1339 case UnaryOperator.MINUS:
1340 write_string ("-");
1341 break;
1342 case UnaryOperator.LOGICAL_NEGATION:
1343 write_string ("!");
1344 break;
1345 case UnaryOperator.BITWISE_COMPLEMENT:
1346 write_string ("~");
1347 break;
1348 case UnaryOperator.INCREMENT:
1349 write_string ("++");
1350 break;
1351 case UnaryOperator.DECREMENT:
1352 write_string ("--");
1353 break;
1354 case UnaryOperator.REF:
1355 write_string ("ref ");
1356 break;
1357 case UnaryOperator.OUT:
1358 write_string ("out ");
1359 break;
1360 default:
1361 assert_not_reached ();
1363 expr.inner.accept (this);
1366 public override void visit_cast_expression (CastExpression expr) {
1367 if (expr.is_non_null_cast) {
1368 write_string ("(!) ");
1369 expr.inner.accept (this);
1370 return;
1373 if (!expr.is_silent_cast) {
1374 write_string ("(");
1375 write_type (expr.type_reference);
1376 write_string (") ");
1379 expr.inner.accept (this);
1381 if (expr.is_silent_cast) {
1382 write_string (" as ");
1383 write_type (expr.type_reference);
1387 public override void visit_pointer_indirection (PointerIndirection expr) {
1388 write_string ("*");
1389 expr.inner.accept (this);
1392 public override void visit_addressof_expression (AddressofExpression expr) {
1393 write_string ("&");
1394 expr.inner.accept (this);
1397 public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
1398 write_string ("(owned) ");
1399 expr.inner.accept (this);
1402 public override void visit_binary_expression (BinaryExpression expr) {
1403 expr.left.accept (this);
1405 switch (expr.operator) {
1406 case BinaryOperator.PLUS:
1407 write_string (" + ");
1408 break;
1409 case BinaryOperator.MINUS:
1410 write_string (" - ");
1411 break;
1412 case BinaryOperator.MUL:
1413 write_string (" * ");
1414 break;
1415 case BinaryOperator.DIV:
1416 write_string (" / ");
1417 break;
1418 case BinaryOperator.MOD:
1419 write_string (" % ");
1420 break;
1421 case BinaryOperator.SHIFT_LEFT:
1422 write_string (" << ");
1423 break;
1424 case BinaryOperator.SHIFT_RIGHT:
1425 write_string (" >> ");
1426 break;
1427 case BinaryOperator.LESS_THAN:
1428 write_string (" < ");
1429 break;
1430 case BinaryOperator.GREATER_THAN:
1431 write_string (" > ");
1432 break;
1433 case BinaryOperator.LESS_THAN_OR_EQUAL:
1434 write_string (" <= ");
1435 break;
1436 case BinaryOperator.GREATER_THAN_OR_EQUAL:
1437 write_string (" >= ");
1438 break;
1439 case BinaryOperator.EQUALITY:
1440 write_string (" == ");
1441 break;
1442 case BinaryOperator.INEQUALITY:
1443 write_string (" != ");
1444 break;
1445 case BinaryOperator.BITWISE_AND:
1446 write_string (" & ");
1447 break;
1448 case BinaryOperator.BITWISE_OR:
1449 write_string (" | ");
1450 break;
1451 case BinaryOperator.BITWISE_XOR:
1452 write_string (" ^ ");
1453 break;
1454 case BinaryOperator.AND:
1455 write_string (" && ");
1456 break;
1457 case BinaryOperator.OR:
1458 write_string (" || ");
1459 break;
1460 case BinaryOperator.IN:
1461 write_string (" in ");
1462 break;
1463 case BinaryOperator.COALESCE:
1464 write_string (" ?? ");
1465 break;
1466 default:
1467 assert_not_reached ();
1470 expr.right.accept (this);
1473 public override void visit_type_check (TypeCheck expr) {
1474 expr.expression.accept (this);
1475 write_string (" is ");
1476 write_type (expr.type_reference);
1479 public override void visit_conditional_expression (ConditionalExpression expr) {
1480 expr.condition.accept (this);
1481 write_string ("?");
1482 expr.true_expression.accept (this);
1483 write_string (":");
1484 expr.false_expression.accept (this);
1487 public override void visit_lambda_expression (LambdaExpression expr) {
1488 write_string ("(");
1489 var params = expr.get_parameters ();
1490 int i = 1;
1491 foreach (var param in params) {
1492 if (i > 1) {
1493 write_string (", ");
1496 if (param.direction == ParameterDirection.REF) {
1497 write_string ("ref ");
1498 } else if (param.direction == ParameterDirection.OUT) {
1499 write_string ("out ");
1502 write_identifier (param.name);
1504 i++;
1506 write_string (") =>");
1507 if (expr.statement_body != null) {
1508 expr.statement_body.accept (this);
1509 } else if (expr.expression_body != null) {
1510 expr.expression_body.accept (this);
1514 public override void visit_assignment (Assignment a) {
1515 a.left.accept (this);
1516 write_string (" = ");
1517 a.right.accept (this);
1520 private void write_indent () {
1521 if (!bol) {
1522 stream.putc ('\n');
1525 stream.puts (string.nfill (indent, '\t'));
1526 bol = false;
1529 private void write_comment (Comment comment) {
1530 try {
1531 if (fix_indent_regex == null)
1532 fix_indent_regex = new Regex ("\\n[\\t ]*");
1533 } catch (Error e) {
1534 assert_not_reached ();
1537 string replacement = "\n%s ".printf (string.nfill (indent, '\t'));
1538 string fixed_content;
1539 try {
1540 fixed_content = fix_indent_regex.replace (comment.content, comment.content.length, 0, replacement);
1541 } catch (Error e) {
1542 assert_not_reached();
1545 write_indent ();
1546 write_string ("/*");
1547 write_string (fixed_content);
1548 write_string ("*/");
1551 private void write_identifier (string s) {
1552 char* id = (char*)s;
1553 int id_length = (int)s.length;
1554 if (Vala.Scanner.get_identifier_or_keyword (id, id_length) != Vala.TokenType.IDENTIFIER ||
1555 s.get_char ().isdigit ()) {
1556 stream.putc ('@');
1558 write_string (s);
1561 private void write_return_type (DataType type) {
1562 if (type.is_weak ()) {
1563 write_string ("unowned ");
1566 write_type (type);
1569 private void write_type (DataType type) {
1570 write_string (type.to_qualified_string (current_scope));
1573 private void write_type_suffix (DataType type) {
1574 var array_type = type as ArrayType;
1575 if (array_type != null && array_type.fixed_length) {
1576 write_string ("[");
1577 array_type.length.accept (this);
1578 write_string ("]");
1582 private void write_string (string s) {
1583 stream.puts (s);
1584 bol = false;
1587 private void write_newline () {
1588 stream.putc ('\n');
1589 bol = true;
1592 void write_code_block (Block? block) {
1593 if (block == null || type != CodeWriterType.DUMP) {
1594 write_string (";");
1595 return;
1598 block.accept (this);
1601 private void write_begin_block () {
1602 if (!bol) {
1603 stream.putc (' ');
1604 } else {
1605 write_indent ();
1607 stream.putc ('{');
1608 write_newline ();
1609 indent++;
1612 private void write_end_block () {
1613 indent--;
1614 write_indent ();
1615 stream.putc ('}');
1618 private bool check_accessibility (Symbol sym) {
1619 switch (type) {
1620 case CodeWriterType.EXTERNAL:
1621 return sym.access == SymbolAccessibility.PUBLIC ||
1622 sym.access == SymbolAccessibility.PROTECTED;
1624 case CodeWriterType.INTERNAL:
1625 case CodeWriterType.FAST:
1626 return sym.access == SymbolAccessibility.INTERNAL ||
1627 sym.access == SymbolAccessibility.PUBLIC ||
1628 sym.access == SymbolAccessibility.PROTECTED;
1630 case CodeWriterType.DUMP:
1631 return true;
1633 default:
1634 assert_not_reached ();
1638 private bool skip_since_tag_check (Symbol sym, string since_val) {
1639 Symbol parent_symbol = sym;
1641 while (parent_symbol.parent_symbol != null) {
1642 parent_symbol = parent_symbol.parent_symbol;
1643 if (parent_symbol.version.since == since_val) {
1644 return true;
1648 return false;
1651 private void write_attributes (CodeNode node) {
1652 var sym = node as Symbol;
1654 var need_cheaders = type != CodeWriterType.FAST && sym != null && !(sym is Namespace) && sym.parent_symbol is Namespace;
1656 var attributes = new GLib.Sequence<Attribute> ();
1657 foreach (var attr in node.attributes) {
1658 attributes.insert_sorted (attr, (a, b) => strcmp (a.name, b.name));
1660 if (need_cheaders && node.get_attribute ("CCode") == null) {
1661 attributes.insert_sorted (new Attribute ("CCode"), (a, b) => strcmp (a.name, b.name));
1664 var iter = attributes.get_begin_iter ();
1665 while (!iter.is_end ()) {
1666 unowned Attribute attr = iter.get ();
1667 iter = iter.next ();
1669 var keys = new GLib.Sequence<string> ();
1670 foreach (var key in attr.args.get_keys ()) {
1671 if (key == "cheader_filename" && sym is Namespace) {
1672 continue;
1674 keys.insert_sorted (key, (CompareDataFunc<string>) strcmp);
1676 if (need_cheaders && attr.name == "CCode" && !attr.has_argument ("cheader_filename")) {
1677 keys.insert_sorted ("cheader_filename", (CompareDataFunc<string>) strcmp);
1680 if (attr.name == "CCode" && keys.get_length () == 0) {
1681 // only cheader_filename on namespace
1682 continue;
1685 if (sym != null && attr.args.size == 1 && attr.name == "Version") {
1686 string since_val = attr.get_string ("since");
1687 if (since_val != null && skip_since_tag_check (sym, since_val)) {
1688 continue;
1692 if (!(node is Parameter) && !(node is PropertyAccessor)) {
1693 write_indent ();
1696 stream.printf ("[%s", attr.name);
1697 if (keys.get_length () > 0) {
1698 stream.puts (" (");
1700 unowned string separator = "";
1701 var arg_iter = keys.get_begin_iter ();
1702 while (!arg_iter.is_end ()) {
1703 unowned string arg_name = arg_iter.get ();
1704 arg_iter = arg_iter.next ();
1705 if (arg_name == "cheader_filename") {
1706 stream.printf ("%scheader_filename = \"%s\"", separator, get_cheaders (sym));
1707 } else {
1708 stream.printf ("%s%s = %s", separator, arg_name, attr.args.get (arg_name));
1710 separator = ", ";
1713 stream.puts (")");
1715 stream.puts ("]");
1716 if (node is Parameter || node is PropertyAccessor) {
1717 write_string (" ");
1718 } else {
1719 write_newline ();
1724 private void write_accessibility (Symbol sym) {
1725 if (sym.access == SymbolAccessibility.PUBLIC) {
1726 write_string ("public ");
1727 } else if (sym.access == SymbolAccessibility.PROTECTED) {
1728 write_string ("protected ");
1729 } else if (sym.access == SymbolAccessibility.INTERNAL) {
1730 write_string ("internal ");
1731 } else if (sym.access == SymbolAccessibility.PRIVATE) {
1732 write_string ("private ");
1735 if (type != CodeWriterType.EXTERNAL && sym.external && !sym.external_package) {
1736 write_string ("extern ");
1740 void write_property_accessor_accessibility (Symbol sym) {
1741 if (sym.access == SymbolAccessibility.PROTECTED) {
1742 write_string (" protected");
1743 } else if (sym.access == SymbolAccessibility.INTERNAL) {
1744 write_string (" internal");
1745 } else if (sym.access == SymbolAccessibility.PRIVATE) {
1746 write_string (" private");
1750 void write_type_parameters (List<TypeParameter> type_params) {
1751 if (type_params.size > 0) {
1752 write_string ("<");
1753 bool first = true;
1754 foreach (TypeParameter type_param in type_params) {
1755 if (first) {
1756 first = false;
1757 } else {
1758 write_string (",");
1760 write_identifier (type_param.name);
1762 write_string (">");
1767 public enum Vala.CodeWriterType {
1768 EXTERNAL,
1769 INTERNAL,
1770 FAST,
1771 DUMP