1/* valacodewriter.vala
2 *
3 * Copyright (C) 2006-2014  Jürg Billeter
4 * Copyright (C) 2006-2008  Raffaele Sandrini
5 * Copyright (C) 2014       Richard Wiedenhöft
6 *
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.
11
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.
16
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
20 *
21 * Author:
22 * 	Jürg Billeter <j@bitron.ch>
23 *	Raffaele Sandrini <raffaele@sandrini.ch>
24 */
25
26
27/**
28 * Code visitor generating Vala API file for the public interface.
29 */
30public class Vala.CodeWriter : CodeVisitor {
31	static GLib.Regex fix_indent_regex;
32
33	private CodeContext context;
34
35	FileStream stream;
36
37	int indent;
38	/* at begin of line */
39	bool bol = true;
40
41	Scope current_scope;
42
43	CodeWriterType type;
44
45	string? override_header = null;
46	string? header_to_override = null;
47
48	public CodeWriter (CodeWriterType type = CodeWriterType.EXTERNAL) {
49		this.type = type;
50	}
51
52	/**
53	 * Allows overriding of a specific cheader in the output
54	 * @param original original cheader to override
55	 * @param replacement cheader to replace original with
56	 */
57	public void set_cheader_override (string original, string replacement)
58	{
59		header_to_override = original;
60		override_header = replacement;
61	}
62
63	/**
64	 * Writes the public interface of the specified code context into the
65	 * specified file.
66	 *
67	 * @param context  a code context
68	 * @param filename a relative or absolute filename
69	 */
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;
74
75		if (file_exists) {
76			stream = FileStream.open (temp_filename, "w");
77		} else {
78			stream = FileStream.open (filename, "w");
79		}
80
81		if (stream == null) {
82			Report.error (null, "unable to open `%s' for writing".printf (filename));
83			return;
84		}
85
86		var header = context.version_header ?
87			"/* %s generated by %s %s, do not modify. */".printf (Path.get_basename (filename), Environment.get_prgname (), Vala.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 ();
92
93		current_scope = context.root.scope;
94
95		context.accept (this);
96
97		current_scope = null;
98
99		stream = null;
100
101		if (file_exists) {
102			var changed = true;
103
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;
111					}
112				}
113				old_file = null;
114				new_file = null;
115			} catch (FileError e) {
116				// assume changed if mmap comparison doesn't work
117			}
118
119			if (changed) {
120				FileUtils.rename (temp_filename, filename);
121			} else {
122				FileUtils.unlink (temp_filename);
123			}
124		}
125
126	}
127
128	public override void visit_using_directive (UsingDirective ns) {
129		if (type == CodeWriterType.FAST) {
130			write_string ("using ");
131
132			var symbols = new GLib.List<UnresolvedSymbol> ();
133			var sym = (UnresolvedSymbol) ns.namespace_symbol;
134			symbols.prepend (sym);
135
136			while ((sym = sym.inner) != null) {
137				symbols.prepend (sym);
138			}
139
140			write_string (symbols.nth_data (0).name);
141
142			for (int i = 1; i < symbols.length (); i++) {
143				write_string (".");
144				write_string (symbols.nth_data (i).name);
145			}
146
147			write_string (";\n");
148		}
149	}
150
151	public override void visit_namespace (Namespace ns) {
152		if (ns.external_package) {
153			return;
154		}
155
156		if (ns.name == null)  {
157			ns.accept_children (this);
158			return;
159		}
160
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.");
174					}
175				}
176			}
177		}
178
179		write_attributes (ns);
180
181		write_indent ();
182		write_string ("namespace ");
183		write_identifier (ns.name);
184		write_begin_block ();
185
186		current_scope = ns.scope;
187
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 ());
198
199		current_scope = current_scope.parent_scope;
200
201		write_end_block ();
202		write_newline ();
203	}
204
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);
211			}
212			if (cheaders == "" && sym.source_reference != null && !sym.external_package) {
213				cheaders = sym.source_reference.file.get_cinclude_filename ();
214			}
215
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;
221					}
222				}
223				cheaders = string.joinv (",", cheaders_array);
224			}
225		}
226		return cheaders;
227	}
228
229	public override void visit_class (Class cl) {
230		if (cl.external_package) {
231			return;
232		}
233
234		if (!check_accessibility (cl)) {
235			return;
236		}
237
238		if (context.vapi_comments && cl.comment != null) {
239			write_comment (cl.comment);
240		}
241
242		write_attributes (cl);
243
244		write_indent ();
245		write_accessibility (cl);
246		if (cl.is_abstract) {
247			write_string ("abstract ");
248		}
249		if (cl.is_sealed) {
250			write_string ("sealed ");
251		}
252		write_string ("class ");
253		write_identifier (cl.name);
254
255		write_type_parameters (cl.get_type_parameters ());
256
257		var base_types = cl.get_base_types ();
258		if (base_types.size > 0) {
259			write_string (" : ");
260
261			bool first = true;
262			foreach (DataType base_type in base_types) {
263				if (!first) {
264					write_string (", ");
265				} else {
266					first = false;
267				}
268				write_type (base_type);
269			}
270		}
271		write_begin_block ();
272
273		current_scope = cl.scope;
274
275		visit_sorted (cl.get_classes ());
276		visit_sorted (cl.get_interfaces ());
277		visit_sorted (cl.get_structs ());
278		visit_sorted (cl.get_enums ());
279		visit_sorted (cl.get_delegates ());
280		visit_sorted (cl.get_fields ());
281		visit_sorted (cl.get_constants ());
282		visit_sorted (cl.get_methods ());
283		visit_sorted (cl.get_properties ());
284		visit_sorted (cl.get_signals ());
285
286		if (cl.constructor != null) {
287			cl.constructor.accept (this);
288		}
289
290		if (cl.class_constructor != null) {
291			cl.class_constructor.accept (this);
292		}
293
294		if (cl.static_constructor != null) {
295			cl.static_constructor.accept (this);
296		}
297
298		if (cl.destructor != null) {
299			cl.destructor.accept (this);
300		}
301
302		if (cl.static_destructor != null) {
303			cl.static_destructor.accept (this);
304		}
305
306		if (cl.class_destructor != null) {
307			cl.class_destructor.accept (this);
308		}
309
310		current_scope = current_scope.parent_scope;
311
312		write_end_block ();
313		write_newline ();
314	}
315
316	void visit_sorted (List<Symbol> symbols) {
317		if (type != CodeWriterType.EXTERNAL && type != CodeWriterType.VAPIGEN) {
318			// order of virtual methods matters for fast vapis
319			foreach (Symbol sym in symbols) {
320				sym.accept (this);
321			}
322			return;
323		}
324
325		var sorted_symbols = new ArrayList<Symbol> ();
326		sorted_symbols.add_all (symbols);
327		sorted_symbols.sort ((a, b) => strcmp (a.name, b.name));
328		foreach (Symbol sym in sorted_symbols) {
329			sym.accept (this);
330		}
331	}
332
333	public override void visit_struct (Struct st) {
334		if (st.external_package) {
335			return;
336		}
337
338		if (!check_accessibility (st)) {
339			return;
340		}
341
342		if (context.vapi_comments && st.comment != null) {
343			write_comment (st.comment);
344		}
345
346		write_attributes (st);
347
348		write_indent ();
349		write_accessibility (st);
350		write_string ("struct ");
351		write_identifier (st.name);
352
353		write_type_parameters (st.get_type_parameters ());
354
355		if (st.base_type != null) {
356			write_string (" : ");
357			write_type (st.base_type);
358		}
359
360		write_begin_block ();
361
362		current_scope = st.scope;
363
364		foreach (Field field in st.get_fields ()) {
365			field.accept (this);
366		}
367		visit_sorted (st.get_constants ());
368		visit_sorted (st.get_methods ());
369		visit_sorted (st.get_properties ());
370
371		current_scope = current_scope.parent_scope;
372
373		write_end_block ();
374		write_newline ();
375	}
376
377	public override void visit_interface (Interface iface) {
378		if (iface.external_package) {
379			return;
380		}
381
382		if (!check_accessibility (iface)) {
383			return;
384		}
385
386		if (context.vapi_comments && iface.comment != null) {
387			write_comment (iface.comment);
388		}
389
390		write_attributes (iface);
391
392		write_indent ();
393		write_accessibility (iface);
394		write_string ("interface ");
395		write_identifier (iface.name);
396
397		write_type_parameters (iface.get_type_parameters ());
398
399		var prerequisites = iface.get_prerequisites ();
400		if (prerequisites.size > 0) {
401			write_string (" : ");
402
403			bool first = true;
404			foreach (DataType prerequisite in prerequisites) {
405				if (!first) {
406					write_string (", ");
407				} else {
408					first = false;
409				}
410				write_type (prerequisite);
411			}
412		}
413		write_begin_block ();
414
415		current_scope = iface.scope;
416
417		visit_sorted (iface.get_classes ());
418		visit_sorted (iface.get_interfaces ());
419		visit_sorted (iface.get_structs ());
420		visit_sorted (iface.get_enums ());
421		visit_sorted (iface.get_delegates ());
422		visit_sorted (iface.get_fields ());
423		visit_sorted (iface.get_constants ());
424		visit_sorted (iface.get_methods ());
425		visit_sorted (iface.get_properties ());
426		visit_sorted (iface.get_signals ());
427
428		current_scope = current_scope.parent_scope;
429
430		write_end_block ();
431		write_newline ();
432	}
433
434	public override void visit_enum (Enum en) {
435		if (en.external_package) {
436			return;
437		}
438
439		if (!check_accessibility (en)) {
440			return;
441		}
442
443		if (context.vapi_comments && en.comment != null) {
444			write_comment (en.comment);
445		}
446
447		write_attributes (en);
448
449		write_indent ();
450		write_accessibility (en);
451		write_string ("enum ");
452		write_identifier (en.name);
453		write_begin_block ();
454
455		bool first = true;
456		foreach (EnumValue ev in en.get_values ()) {
457			if (first) {
458				first = false;
459			} else {
460				write_string (",");
461				write_newline ();
462			}
463
464			if (context.vapi_comments && ev.comment != null) {
465				write_comment (ev.comment);
466			}
467
468			write_attributes (ev);
469
470			write_indent ();
471			write_identifier (ev.name);
472
473			if (type == CodeWriterType.FAST && ev.value != null && ev.value.is_constant ()) {
474				write_string(" = ");
475				ev.value.accept (this);
476			}
477		}
478
479		if (!first) {
480			if (en.get_methods ().size > 0 || en.get_constants ().size > 0) {
481				write_string (";");
482			}
483			write_newline ();
484		}
485
486		current_scope = en.scope;
487		foreach (Method m in en.get_methods ()) {
488			m.accept (this);
489		}
490		foreach (Constant c in en.get_constants ()) {
491			c.accept (this);
492		}
493		current_scope = current_scope.parent_scope;
494
495		write_end_block ();
496		write_newline ();
497	}
498
499	public override void visit_error_domain (ErrorDomain edomain) {
500		if (edomain.external_package) {
501			return;
502		}
503
504		if (!check_accessibility (edomain)) {
505			return;
506		}
507
508		if (context.vapi_comments && edomain.comment != null) {
509			write_comment (edomain.comment);
510		}
511
512		write_attributes (edomain);
513
514		write_indent ();
515		write_accessibility (edomain);
516		write_string ("errordomain ");
517		write_identifier (edomain.name);
518		write_begin_block ();
519
520		bool first = true;
521		foreach (ErrorCode ecode in edomain.get_codes ()) {
522			if (first) {
523				first = false;
524			} else {
525				write_string (",");
526				write_newline ();
527			}
528
529			if (context.vapi_comments && ecode.comment != null) {
530				write_comment (ecode.comment);
531			}
532
533			write_attributes (ecode);
534
535			write_indent ();
536			write_identifier (ecode.name);
537		}
538
539		if (!first) {
540			if (edomain.get_methods ().size > 0) {
541				write_string (";");
542			}
543			write_newline ();
544		}
545
546		current_scope = edomain.scope;
547		foreach (Method m in edomain.get_methods ()) {
548			m.accept (this);
549		}
550		current_scope = current_scope.parent_scope;
551
552		write_end_block ();
553		write_newline ();
554	}
555
556	public override void visit_constant (Constant c) {
557		if (c.external_package) {
558			return;
559		}
560
561		if (!check_accessibility (c)) {
562			return;
563		}
564
565		if (context.vapi_comments && c.comment != null) {
566			write_comment (c.comment);
567		}
568
569		write_attributes (c);
570
571		write_indent ();
572		write_accessibility (c);
573
574		if (c.hides) {
575			write_string ("new ");
576		}
577
578		write_string ("const ");
579
580		write_type (c.type_reference);
581
582		write_string (" ");
583		write_identifier (c.name);
584		write_type_suffix (c.type_reference);
585		if (type == CodeWriterType.FAST && c.value != null && c.value.is_constant ()) {
586			write_string(" = ");
587			c.value.accept (this);
588		}
589		write_string (";");
590		write_newline ();
591	}
592
593	public override void visit_field (Field f) {
594		if (f.external_package) {
595			return;
596		}
597
598		if (!check_accessibility (f)) {
599			return;
600		}
601
602		if (context.vapi_comments && f.comment != null) {
603			write_comment (f.comment);
604		}
605
606		write_attributes (f);
607
608		write_indent ();
609		write_accessibility (f);
610
611		if (f.hides) {
612			write_string ("new ");
613		}
614
615		if (f.binding == MemberBinding.STATIC) {
616			write_string ("static ");
617		} else if (f.binding == MemberBinding.CLASS) {
618			write_string ("class ");
619		}
620
621		if (f.variable_type.is_weak ()) {
622			write_string ("weak ");
623		}
624
625		write_type (f.variable_type);
626
627		write_string (" ");
628		write_identifier (f.name);
629		write_type_suffix (f.variable_type);
630		write_string (";");
631		write_newline ();
632	}
633
634	private void write_error_domains (List<DataType> error_domains) {
635		if (error_domains.size > 0) {
636			write_string (" throws ");
637
638			bool first = true;
639			foreach (DataType type in error_domains) {
640				if (!first) {
641					write_string (", ");
642				} else {
643					first = false;
644				}
645
646				write_type (type);
647			}
648		}
649	}
650
651	private void write_params (List<Parameter> params) {
652		write_string ("(");
653
654		int i = 1;
655		foreach (Parameter param in params) {
656			if (i > 1) {
657				write_string (", ");
658			}
659
660			if (param.ellipsis) {
661				write_string ("...");
662				continue;
663			}
664
665			write_attributes (param);
666
667			if (param.params_array) {
668				write_string ("params ");
669			}
670
671			if (param.direction == ParameterDirection.IN) {
672				if (param.variable_type.value_owned) {
673					write_string ("owned ");
674				}
675			} else {
676				if (param.direction == ParameterDirection.REF) {
677					write_string ("ref ");
678				} else if (param.direction == ParameterDirection.OUT) {
679					write_string ("out ");
680				}
681				if (param.variable_type.is_weak ()) {
682					write_string ("unowned ");
683				}
684			}
685
686			write_type (param.variable_type);
687
688			write_string (" ");
689			write_identifier (param.name);
690			write_type_suffix (param.variable_type);
691
692			if (param.initializer != null) {
693				write_string (" = ");
694				param.initializer.accept (this);
695			}
696
697			i++;
698		}
699
700		write_string (")");
701	}
702
703	public override void visit_delegate (Delegate cb) {
704		if (cb.external_package) {
705			return;
706		}
707
708		if (!check_accessibility (cb)) {
709			return;
710		}
711
712		if (context.vapi_comments && cb.comment != null) {
713			write_comment (cb.comment);
714		}
715
716		write_attributes (cb);
717
718		write_indent ();
719
720		write_accessibility (cb);
721		write_string ("delegate ");
722
723		write_return_type (cb.return_type);
724
725		write_string (" ");
726		write_identifier (cb.name);
727
728		write_type_parameters (cb.get_type_parameters ());
729
730		write_string (" ");
731
732		write_params (cb.get_parameters ());
733
734		var error_types = new ArrayList<DataType> ();
735		cb.get_error_types (error_types);
736		write_error_domains (error_types);
737
738		write_string (";");
739
740		write_newline ();
741	}
742
743	public override void visit_constructor (Constructor c) {
744		if (type != CodeWriterType.DUMP) {
745			return;
746		}
747
748		if (context.vapi_comments && c.comment != null) {
749			write_comment (c.comment);
750		}
751
752		write_indent ();
753		if (c.binding == MemberBinding.STATIC) {
754			write_string ("static ");
755		} else if (c.binding == MemberBinding.CLASS) {
756			write_string ("class ");
757		}
758		write_string ("construct");
759		write_code_block (c.body);
760		write_newline ();
761	}
762
763	public override void visit_destructor (Destructor d) {
764		if (type != CodeWriterType.DUMP) {
765			return;
766		}
767
768		if (context.vapi_comments && d.comment != null) {
769			write_comment (d.comment);
770		}
771
772		write_indent ();
773		if (d.binding == MemberBinding.STATIC) {
774			write_string ("static ");
775		} else if (d.binding == MemberBinding.CLASS) {
776			write_string ("class ");
777		}
778		write_string ("~");
779		var datatype = (TypeSymbol) d.parent_symbol;
780		write_identifier (datatype.name);
781		write_string (" () ");
782		write_code_block (d.body);
783		write_newline ();
784	}
785
786	public override void visit_method (Method m) {
787		if (m.external_package) {
788			return;
789		}
790
791		// don't write interface implementation unless it's an abstract or virtual method
792		if (!check_accessibility (m) || (m.base_interface_method != null && !m.is_abstract && !m.is_virtual)) {
793			if (type != CodeWriterType.DUMP) {
794				return;
795			}
796		}
797
798		if (context.vapi_comments && m.comment != null) {
799			write_comment (m.comment);
800		}
801
802		write_attributes (m);
803
804		write_indent ();
805		write_accessibility (m);
806
807		if (m is CreationMethod) {
808			if (m.coroutine) {
809				write_string ("async ");
810			}
811
812			var datatype = (TypeSymbol) m.parent_symbol;
813			write_identifier (datatype.name);
814			if (m.name != ".new") {
815				write_string (".");
816				write_identifier (m.name);
817			}
818			write_string (" ");
819		} else {
820			if (m.hides) {
821				write_string ("new ");
822			}
823
824			if (m.binding == MemberBinding.STATIC) {
825				write_string ("static ");
826			} else if (m.binding == MemberBinding.CLASS) {
827				write_string ("class ");
828			} else if (m.is_abstract) {
829				write_string ("abstract ");
830			} else if (m.is_virtual) {
831				write_string ("virtual ");
832			} else if (m.overrides) {
833				write_string ("override ");
834			}
835
836			if (m.coroutine) {
837				write_string ("async ");
838			}
839
840			write_return_type (m.return_type);
841			write_string (" ");
842
843			write_identifier (m.name);
844
845			write_type_parameters (m.get_type_parameters ());
846
847			write_string (" ");
848		}
849
850		write_params (m.get_parameters ());
851
852		var error_types = new ArrayList<DataType> ();
853		m.get_error_types (error_types);
854		write_error_domains (error_types);
855
856		write_code_block (m.body);
857
858		write_newline ();
859	}
860
861	public override void visit_creation_method (CreationMethod m) {
862		visit_method (m);
863	}
864
865	public override void visit_property (Property prop) {
866		if (!check_accessibility (prop) || (prop.base_interface_property != null && !prop.is_abstract && !prop.is_virtual)) {
867			return;
868		}
869
870		if (context.vapi_comments && prop.comment != null) {
871			write_comment (prop.comment);
872		}
873
874		write_attributes (prop);
875
876		write_indent ();
877		write_accessibility (prop);
878
879		if (prop.hides) {
880			write_string ("new ");
881		}
882
883		if (prop.binding == MemberBinding.STATIC) {
884			write_string ("static ");
885		} else  if (prop.is_abstract) {
886			write_string ("abstract ");
887		} else if (prop.is_virtual) {
888			write_string ("virtual ");
889		} else if (prop.overrides) {
890			write_string ("override ");
891		}
892
893		if (prop.property_type.is_weak ()) {
894			write_string ("weak ");
895		}
896
897		write_type (prop.property_type);
898
899		write_string (" ");
900		write_identifier (prop.name);
901		write_string (" {");
902		if (prop.get_accessor != null) {
903			write_attributes (prop.get_accessor);
904
905			write_property_accessor_accessibility (prop.get_accessor);
906
907			if (prop.get_accessor.value_type.value_owned) {
908				write_string (" owned");
909			}
910
911			write_string (" get");
912			write_code_block (prop.get_accessor.body);
913		}
914		if (prop.set_accessor != null) {
915			write_attributes (prop.set_accessor);
916
917			write_property_accessor_accessibility (prop.set_accessor);
918
919			if (prop.set_accessor.value_type.value_owned) {
920				write_string (" owned");
921			}
922
923			if (prop.set_accessor.writable) {
924				write_string (" set");
925			}
926			if (prop.set_accessor.construction) {
927				write_string (" construct");
928			}
929			write_code_block (prop.set_accessor.body);
930		}
931		write_string (" }");
932		write_newline ();
933	}
934
935	public override void visit_signal (Signal sig) {
936		if (!check_accessibility (sig)) {
937			return;
938		}
939
940		if (context.vapi_comments && sig.comment != null) {
941			write_comment (sig.comment);
942		}
943
944		write_attributes (sig);
945
946		write_indent ();
947		write_accessibility (sig);
948
949		if (sig.hides) {
950			write_string ("new ");
951		}
952
953		if (sig.is_virtual) {
954			write_string ("virtual ");
955		}
956
957		write_string ("signal ");
958
959		write_return_type (sig.return_type);
960
961		write_string (" ");
962		write_identifier (sig.name);
963
964		write_string (" ");
965
966		write_params (sig.get_parameters ());
967
968		write_string (";");
969
970		write_newline ();
971	}
972
973	public override void visit_block (Block b) {
974		write_begin_block ();
975
976		foreach (Statement stmt in b.get_statements ()) {
977			stmt.accept (this);
978		}
979
980		write_end_block ();
981		if (b.parent_node is Block) {
982			write_newline ();
983		}
984	}
985
986	public override void visit_empty_statement (EmptyStatement stmt) {
987	}
988
989	public override void visit_declaration_statement (DeclarationStatement stmt) {
990		write_indent ();
991		stmt.declaration.accept (this);
992		write_string (";");
993		write_newline ();
994	}
995
996	public override void visit_local_variable (LocalVariable local) {
997		if (local.variable_type.is_weak ()) {
998			write_string ("unowned ");
999		}
1000		write_type (local.variable_type);
1001		write_string (" ");
1002		write_identifier (local.name);
1003		write_type_suffix (local.variable_type);
1004		if (local.initializer != null) {
1005			write_string (" = ");
1006			local.initializer.accept (this);
1007		}
1008	}
1009
1010	public override void visit_initializer_list (InitializerList list) {
1011		write_string ("{");
1012
1013		bool first = true;
1014		foreach (Expression initializer in list.get_initializers ()) {
1015			if (!first) {
1016				write_string (", ");
1017			} else {
1018				write_string (" ");
1019			}
1020			first = false;
1021			initializer.accept (this);
1022		}
1023		write_string (" }");
1024	}
1025
1026	public override void visit_expression_statement (ExpressionStatement stmt) {
1027		write_indent ();
1028		stmt.expression.accept (this);
1029		write_string (";");
1030		write_newline ();
1031	}
1032
1033	public override void visit_if_statement (IfStatement stmt) {
1034		write_indent ();
1035		write_string ("if (");
1036		stmt.condition.accept (this);
1037		write_string (")");
1038		stmt.true_statement.accept (this);
1039		if (stmt.false_statement != null) {
1040			write_string (" else");
1041			stmt.false_statement.accept (this);
1042		}
1043		write_newline ();
1044	}
1045
1046	public override void visit_switch_statement (SwitchStatement stmt) {
1047		write_indent ();
1048		write_string ("switch (");
1049		stmt.expression.accept (this);
1050		write_string (") {");
1051		write_newline ();
1052
1053		foreach (SwitchSection section in stmt.get_sections ()) {
1054			section.accept (this);
1055		}
1056
1057		write_indent ();
1058		write_string ("}");
1059		write_newline ();
1060	}
1061
1062	public override void visit_switch_section (SwitchSection section) {
1063		foreach (SwitchLabel label in section.get_labels ()) {
1064			label.accept (this);
1065		}
1066
1067		visit_block (section);
1068	}
1069
1070	public override void visit_switch_label (SwitchLabel label) {
1071		if (label.expression != null) {
1072			write_indent ();
1073			write_string ("case ");
1074			label.expression.accept (this);
1075			write_string (":");
1076			write_newline ();
1077		} else {
1078			write_indent ();
1079			write_string ("default:");
1080			write_newline ();
1081		}
1082	}
1083
1084	public override void visit_loop (Loop stmt) {
1085		write_indent ();
1086		write_string ("loop");
1087		stmt.body.accept (this);
1088		write_newline ();
1089	}
1090
1091	public override void visit_while_statement (WhileStatement stmt) {
1092		write_indent ();
1093		write_string ("while (");
1094		stmt.condition.accept (this);
1095		write_string (")");
1096		stmt.body.accept (this);
1097		write_newline ();
1098	}
1099
1100	public override void visit_do_statement (DoStatement stmt) {
1101		write_indent ();
1102		write_string ("do");
1103		stmt.body.accept (this);
1104		write_string ("while (");
1105		stmt.condition.accept (this);
1106		write_string (");");
1107		write_newline ();
1108	}
1109
1110	public override void visit_for_statement (ForStatement stmt) {
1111		write_indent ();
1112		write_string ("for (");
1113
1114		bool first = true;
1115		foreach (Expression initializer in stmt.get_initializer ()) {
1116			if (!first) {
1117				write_string (", ");
1118			}
1119			first = false;
1120			initializer.accept (this);
1121		}
1122		write_string ("; ");
1123
1124		stmt.condition.accept (this);
1125		write_string ("; ");
1126
1127		first = true;
1128		foreach (Expression iterator in stmt.get_iterator ()) {
1129			if (!first) {
1130				write_string (", ");
1131			}
1132			first = false;
1133			iterator.accept (this);
1134		}
1135
1136		write_string (")");
1137		stmt.body.accept (this);
1138		write_newline ();
1139	}
1140
1141	public override void visit_foreach_statement (ForeachStatement stmt) {
1142	}
1143
1144	public override void visit_break_statement (BreakStatement stmt) {
1145		write_indent ();
1146		write_string ("break;");
1147		write_newline ();
1148	}
1149
1150	public override void visit_continue_statement (ContinueStatement stmt) {
1151		write_indent ();
1152		write_string ("continue;");
1153		write_newline ();
1154	}
1155
1156	public override void visit_return_statement (ReturnStatement stmt) {
1157		write_indent ();
1158		write_string ("return");
1159		if (stmt.return_expression != null) {
1160			write_string (" ");
1161			stmt.return_expression.accept (this);
1162		}
1163		write_string (";");
1164		write_newline ();
1165	}
1166
1167	public override void visit_yield_statement (YieldStatement y) {
1168		write_indent ();
1169		write_string ("yield");
1170		write_string (";");
1171		write_newline ();
1172	}
1173
1174	public override void visit_throw_statement (ThrowStatement stmt) {
1175		write_indent ();
1176		write_string ("throw");
1177		if (stmt.error_expression != null) {
1178			write_string (" ");
1179			stmt.error_expression.accept (this);
1180		}
1181		write_string (";");
1182		write_newline ();
1183	}
1184
1185	public override void visit_try_statement (TryStatement stmt) {
1186		write_indent ();
1187		write_string ("try");
1188		stmt.body.accept (this);
1189		foreach (var clause in stmt.get_catch_clauses ()) {
1190			clause.accept (this);
1191		}
1192		if (stmt.finally_body != null) {
1193			write_string (" finally");
1194			stmt.finally_body.accept (this);
1195		}
1196		write_newline ();
1197	}
1198
1199	public override void visit_catch_clause (CatchClause clause) {
1200		var type_name = clause.error_type == null ? "GLib.Error" : clause.error_type.to_string ();
1201		var var_name = clause.variable_name == null ? "_" : clause.variable_name;
1202		write_string (" catch (%s %s)".printf (type_name, var_name));
1203		clause.body.accept (this);
1204	}
1205
1206	public override void visit_lock_statement (LockStatement stmt) {
1207		write_indent ();
1208		write_string ("lock (");
1209		stmt.resource.accept (this);
1210		write_string (")");
1211		if (stmt.body == null) {
1212			write_string (";");
1213		} else {
1214			stmt.body.accept (this);
1215		}
1216		write_newline ();
1217	}
1218
1219	public override void visit_unlock_statement (UnlockStatement stmt) {
1220		write_indent ();
1221		write_string ("unlock (");
1222		stmt.resource.accept (this);
1223		write_string (");");
1224		write_newline ();
1225	}
1226
1227	public override void visit_delete_statement (DeleteStatement stmt) {
1228		write_indent ();
1229		write_string ("delete ");
1230		stmt.expression.accept (this);
1231		write_string (";");
1232		write_newline ();
1233	}
1234
1235	public override void visit_array_creation_expression (ArrayCreationExpression expr) {
1236		write_string ("new ");
1237		write_type (expr.element_type);
1238		write_string ("[");
1239
1240		bool first = true;
1241		foreach (Expression size in expr.get_sizes ()) {
1242			if (!first) {
1243				write_string (", ");
1244			}
1245			first = false;
1246
1247			size.accept (this);
1248		}
1249
1250		write_string ("]");
1251
1252		if (expr.initializer_list != null) {
1253			write_string (" ");
1254			expr.initializer_list.accept (this);
1255		}
1256	}
1257
1258	public override void visit_boolean_literal (BooleanLiteral lit) {
1259		write_string (lit.value.to_string ());
1260	}
1261
1262	public override void visit_character_literal (CharacterLiteral lit) {
1263		write_string (lit.value);
1264	}
1265
1266	public override void visit_integer_literal (IntegerLiteral lit) {
1267		write_string (lit.value);
1268	}
1269
1270	public override void visit_real_literal (RealLiteral lit) {
1271		write_string (lit.value);
1272	}
1273
1274	public override void visit_string_literal (StringLiteral lit) {
1275		write_string (lit.value);
1276	}
1277
1278	public override void visit_null_literal (NullLiteral lit) {
1279		write_string ("null");
1280	}
1281
1282	public override void visit_member_access (MemberAccess expr) {
1283		if (expr.inner != null) {
1284			expr.inner.accept (this);
1285			write_string (".");
1286		}
1287		write_identifier (expr.member_name);
1288	}
1289
1290	public override void visit_method_call (MethodCall expr) {
1291		if (expr.is_yield_expression) {
1292			write_string ("yield ");
1293		}
1294
1295		expr.call.accept (this);
1296		write_string (" (");
1297
1298		bool first = true;
1299		foreach (Expression arg in expr.get_argument_list ()) {
1300			if (!first) {
1301				write_string (", ");
1302			}
1303			first = false;
1304
1305			arg.accept (this);
1306		}
1307
1308		write_string (")");
1309	}
1310
1311	public override void visit_element_access (ElementAccess expr) {
1312		expr.container.accept (this);
1313		write_string ("[");
1314
1315		bool first = true;
1316		foreach (Expression index in expr.get_indices ()) {
1317			if (!first) {
1318				write_string (", ");
1319			}
1320			first = false;
1321
1322			index.accept (this);
1323		}
1324
1325		write_string ("]");
1326	}
1327
1328	public override void visit_slice_expression (SliceExpression expr) {
1329		expr.container.accept (this);
1330		write_string ("[");
1331		expr.start.accept (this);
1332		write_string (":");
1333		expr.stop.accept (this);
1334		write_string ("]");
1335	}
1336
1337	public override void visit_base_access (BaseAccess expr) {
1338		write_string ("base");
1339	}
1340
1341	public override void visit_postfix_expression (PostfixExpression expr) {
1342		expr.inner.accept (this);
1343		if (expr.increment) {
1344			write_string ("++");
1345		} else {
1346			write_string ("--");
1347		}
1348	}
1349
1350	public override void visit_object_creation_expression (ObjectCreationExpression expr) {
1351		if (expr.is_yield_expression) {
1352			write_string ("yield ");
1353		}
1354
1355		if (!expr.struct_creation) {
1356			write_string ("new ");
1357		}
1358
1359		write_type (expr.type_reference);
1360
1361		if (expr.symbol_reference.name != ".new") {
1362			write_string (".");
1363			write_string (expr.symbol_reference.name);
1364		}
1365
1366		write_string (" (");
1367
1368		bool first = true;
1369		foreach (Expression arg in expr.get_argument_list ()) {
1370			if (!first) {
1371				write_string (", ");
1372			}
1373			first = false;
1374
1375			arg.accept (this);
1376		}
1377
1378		write_string (")");
1379	}
1380
1381	public override void visit_sizeof_expression (SizeofExpression expr) {
1382		write_string ("sizeof (");
1383		write_type (expr.type_reference);
1384		write_string (")");
1385	}
1386
1387	public override void visit_typeof_expression (TypeofExpression expr) {
1388		write_string ("typeof (");
1389		write_type (expr.type_reference);
1390		write_string (")");
1391	}
1392
1393	public override void visit_unary_expression (UnaryExpression expr) {
1394		write_string (expr.operator.to_string ());
1395		expr.inner.accept (this);
1396	}
1397
1398	public override void visit_cast_expression (CastExpression expr) {
1399		if (expr.is_non_null_cast) {
1400			write_string ("(!) ");
1401			expr.inner.accept (this);
1402			return;
1403		}
1404
1405		if (!expr.is_silent_cast) {
1406			write_string ("(");
1407			write_type (expr.type_reference);
1408			write_string (") ");
1409		}
1410
1411		expr.inner.accept (this);
1412
1413		if (expr.is_silent_cast) {
1414			write_string (" as ");
1415			write_type (expr.type_reference);
1416		}
1417	}
1418
1419	public override void visit_pointer_indirection (PointerIndirection expr) {
1420		write_string ("*");
1421		expr.inner.accept (this);
1422	}
1423
1424	public override void visit_addressof_expression (AddressofExpression expr) {
1425		write_string ("&");
1426		expr.inner.accept (this);
1427	}
1428
1429	public override void visit_reference_transfer_expression (ReferenceTransferExpression expr) {
1430		write_string ("(owned) ");
1431		expr.inner.accept (this);
1432	}
1433
1434	public override void visit_binary_expression (BinaryExpression expr) {
1435		expr.left.accept (this);
1436		write_string (" ");
1437		write_string (expr.operator.to_string ());
1438		write_string (" ");
1439		expr.right.accept (this);
1440	}
1441
1442	public override void visit_type_check (TypeCheck expr) {
1443		expr.expression.accept (this);
1444		write_string (" is ");
1445		write_type (expr.type_reference);
1446	}
1447
1448	public override void visit_conditional_expression (ConditionalExpression expr) {
1449		expr.condition.accept (this);
1450		write_string ("?");
1451		expr.true_expression.accept (this);
1452		write_string (":");
1453		expr.false_expression.accept (this);
1454	}
1455
1456	public override void visit_lambda_expression (LambdaExpression expr) {
1457		write_string ("(");
1458		var params = expr.get_parameters ();
1459		int i = 1;
1460		foreach (var param in params) {
1461			if (i > 1) {
1462				write_string (", ");
1463			}
1464
1465			if (param.direction == ParameterDirection.REF) {
1466				write_string ("ref ");
1467			} else if (param.direction == ParameterDirection.OUT) {
1468				write_string ("out ");
1469			}
1470
1471			write_identifier (param.name);
1472
1473			i++;
1474		}
1475		write_string (") =>");
1476		if (expr.statement_body != null) {
1477			expr.statement_body.accept (this);
1478		} else if (expr.expression_body != null) {
1479			expr.expression_body.accept (this);
1480		}
1481	}
1482
1483	public override void visit_assignment (Assignment a) {
1484		a.left.accept (this);
1485		write_string (" = ");
1486		a.right.accept (this);
1487	}
1488
1489	private void write_indent () {
1490		if (!bol) {
1491			stream.putc ('\n');
1492		}
1493
1494		stream.puts (string.nfill (indent, '\t'));
1495		bol = false;
1496	}
1497
1498	private void write_comment (Comment comment) {
1499		try {
1500			if (fix_indent_regex == null)
1501				fix_indent_regex = new Regex ("\\n[\\t ]*");
1502		} catch (Error e) {
1503			assert_not_reached ();
1504		}
1505
1506		string replacement = "\n%s ".printf (string.nfill (indent, '\t'));
1507		string fixed_content;
1508		try {
1509			fixed_content = fix_indent_regex.replace (comment.content, comment.content.length, 0, replacement);
1510		} catch (Error e) {
1511			assert_not_reached();
1512		}
1513
1514		write_indent ();
1515		write_string ("/*");
1516		write_string (fixed_content);
1517		write_string ("*/");
1518	}
1519
1520	private void write_identifier (string s) {
1521		char* id = (char*)s;
1522		int id_length = (int)s.length;
1523		if (Vala.Scanner.get_identifier_or_keyword (id, id_length) != Vala.TokenType.IDENTIFIER ||
1524		    s.get_char ().isdigit ()) {
1525			stream.putc ('@');
1526		}
1527		write_string (s);
1528	}
1529
1530	private void write_return_type (DataType type) {
1531		if (type.is_weak ()) {
1532			write_string ("unowned ");
1533		}
1534
1535		write_type (type);
1536	}
1537
1538	private void write_type (DataType type) {
1539		write_string (type.to_qualified_string (current_scope));
1540	}
1541
1542	private void write_type_suffix (DataType type) {
1543		unowned ArrayType? array_type = type as ArrayType;
1544		if (array_type != null && array_type.fixed_length) {
1545			write_string ("[");
1546			array_type.length.accept (this);
1547			write_string ("]");
1548		}
1549	}
1550
1551	private void write_string (string s) {
1552		stream.puts (s);
1553		bol = false;
1554	}
1555
1556	private void write_newline () {
1557		stream.putc ('\n');
1558		bol = true;
1559	}
1560
1561	void write_code_block (Block? block) {
1562		if (block == null || (type != CodeWriterType.DUMP && type != CodeWriterType.VAPIGEN)) {
1563			write_string (";");
1564			return;
1565		}
1566
1567		block.accept (this);
1568	}
1569
1570	private void write_begin_block () {
1571		if (!bol) {
1572			stream.putc (' ');
1573		} else {
1574			write_indent ();
1575		}
1576		stream.putc ('{');
1577		write_newline ();
1578		indent++;
1579	}
1580
1581	private void write_end_block () {
1582		indent--;
1583		write_indent ();
1584		stream.putc ('}');
1585	}
1586
1587	private bool check_accessibility (Symbol sym) {
1588		switch (type) {
1589		case CodeWriterType.EXTERNAL:
1590		case CodeWriterType.VAPIGEN:
1591			return sym.access == SymbolAccessibility.PUBLIC ||
1592			       sym.access == SymbolAccessibility.PROTECTED;
1593
1594		case CodeWriterType.INTERNAL:
1595		case CodeWriterType.FAST:
1596			return sym.access == SymbolAccessibility.INTERNAL ||
1597			       sym.access == SymbolAccessibility.PUBLIC ||
1598			       sym.access == SymbolAccessibility.PROTECTED;
1599
1600		case CodeWriterType.DUMP:
1601			return true;
1602
1603		default:
1604			assert_not_reached ();
1605		}
1606	}
1607
1608	private bool skip_since_tag_check (Symbol sym, string since_val) {
1609		Symbol parent_symbol = sym;
1610
1611		while (parent_symbol.parent_symbol != null) {
1612			parent_symbol = parent_symbol.parent_symbol;
1613			if (parent_symbol.version.since == since_val) {
1614				return true;
1615			}
1616		}
1617
1618		return false;
1619	}
1620
1621	private void write_attributes (CodeNode node) {
1622		unowned Symbol? sym = node as Symbol;
1623
1624		var need_cheaders = type != CodeWriterType.FAST && sym != null && !(sym is Namespace) && sym.parent_symbol is Namespace;
1625
1626		var attributes = new GLib.Sequence<Attribute> ();
1627		foreach (var attr in node.attributes) {
1628			attributes.insert_sorted (attr, (a, b) => strcmp (a.name, b.name));
1629		}
1630		if (need_cheaders && node.get_attribute ("CCode") == null) {
1631			attributes.insert_sorted (new Attribute ("CCode"), (a, b) => strcmp (a.name, b.name));
1632		}
1633
1634		var iter = attributes.get_begin_iter ();
1635		while (!iter.is_end ()) {
1636			unowned Attribute attr = iter.get ();
1637			iter = iter.next ();
1638
1639			var keys = new GLib.Sequence<string> ();
1640			foreach (var key in attr.args.get_keys ()) {
1641				if (key == "cheader_filename" && sym is Namespace) {
1642					continue;
1643				}
1644				keys.insert_sorted (key, (CompareDataFunc<string>) strcmp);
1645			}
1646			if (need_cheaders && attr.name == "CCode" && !attr.has_argument ("cheader_filename")) {
1647				keys.insert_sorted ("cheader_filename", (CompareDataFunc<string>) strcmp);
1648			}
1649
1650			if (attr.name == "CCode" && keys.get_length () == 0) {
1651				// only cheader_filename on namespace
1652				continue;
1653			}
1654
1655			if (attr.name == "Source") {
1656				continue;
1657			}
1658
1659			if (sym != null && attr.args.size == 1 && attr.name == "Version") {
1660				string since_val = attr.get_string ("since");
1661				if (since_val != null && skip_since_tag_check (sym, since_val)) {
1662					continue;
1663				}
1664			}
1665
1666			if (!(node is Parameter) && !(node is PropertyAccessor)) {
1667				write_indent ();
1668			}
1669
1670			stream.printf ("[%s", attr.name);
1671			if (keys.get_length () > 0) {
1672				stream.puts (" (");
1673
1674				unowned string separator = "";
1675				var arg_iter = keys.get_begin_iter ();
1676				while (!arg_iter.is_end ()) {
1677					unowned string arg_name = arg_iter.get ();
1678					arg_iter = arg_iter.next ();
1679					if (arg_name == "cheader_filename") {
1680						stream.printf ("%scheader_filename = \"%s\"", separator, get_cheaders (sym));
1681					} else {
1682						stream.printf ("%s%s = %s", separator, arg_name, attr.args.get (arg_name));
1683					}
1684					separator = ", ";
1685				}
1686
1687				stream.puts (")");
1688			}
1689			stream.puts ("]");
1690			if (node is Parameter || node is PropertyAccessor) {
1691				write_string (" ");
1692			} else {
1693				write_newline ();
1694			}
1695		}
1696
1697		if (type == CodeWriterType.FAST && !(node is Parameter || node is PropertyAccessor)) {
1698			var source_reference = node.source_reference;
1699			if (source_reference != null) {
1700				write_indent ();
1701				string filename = source_reference.file.filename;
1702				if (filename.has_prefix (context.basedir)) {
1703					filename = filename.substring (context.basedir.length + 1);
1704				}
1705				stream.puts ("[Source (filename = \"%s\", line = %i, column = %i)]".printf (filename, source_reference.begin.line, source_reference.begin.column));
1706				write_newline ();
1707			}
1708		}
1709	}
1710
1711	private void write_accessibility (Symbol sym) {
1712		write_string (sym.access.to_string ());
1713		write_string (" ");
1714
1715		if (type != CodeWriterType.EXTERNAL && type != CodeWriterType.VAPIGEN && sym.external && !sym.external_package) {
1716			write_string ("extern ");
1717		}
1718	}
1719
1720	void write_property_accessor_accessibility (Symbol sym) {
1721		if (sym.access == SymbolAccessibility.PUBLIC) {
1722			return;
1723		}
1724
1725		write_string (" ");
1726		write_string (sym.access.to_string ());
1727	}
1728
1729	void write_type_parameters (List<TypeParameter> type_params) {
1730		if (type_params.size > 0) {
1731			write_string ("<");
1732			bool first = true;
1733			foreach (TypeParameter type_param in type_params) {
1734				if (first) {
1735					first = false;
1736				} else {
1737					write_string (",");
1738				}
1739				write_identifier (type_param.name);
1740			}
1741			write_string (">");
1742		}
1743	}
1744}
1745
1746public enum Vala.CodeWriterType {
1747	EXTERNAL,
1748	INTERNAL,
1749	FAST,
1750	DUMP,
1751	VAPIGEN
1752}
1753