valasourcefile.vala 12.8 KB
Newer Older
1 2
/* valasourcefile.vala
 *
3
 * Copyright (C) 2006-2008  Jürg Billeter
4 5 6 7
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
8
 * version 2.1 of the License, or (at your option) any later version.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23

 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 *
 * Author:
 * 	Jürg Billeter <j@bitron.ch>
 */

using GLib;
24
using Gee;
25

26 27 28
/**
 * Represents a Vala source or VAPI package file.
 */
29
public class Vala.SourceFile {
30 31 32
	/**
	 * The name of this source file.
	 */
33
	public string filename { get; set; }
34 35 36 37 38 39 40 41 42
	
	/**
	 * The header comment of this source file.
	 */
	public string comment { get; set; }
	
	/**
	 * Specifies whether this file is a VAPI package file.
	 */
43
	public bool external_package { get; set; }
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
	
	/**
	 * Specifies the dependency cycle this source file is member of. If this
	 * source file is in a cycle, all type definitions of that cycle will
	 * only be written to the C header file of the cycle head.
	 */
	public SourceFileCycle cycle { get; set; }
	
	/**
	 * Specifies whether this source file is the head of the cycle, if it is
	 * in a cycle at all.
	 */
	public bool is_cycle_head { get; set; }
	
	/**
	 * Mark used for cycle detection.
	 *
	 * 0: not yet visited
	 * 1: currently visiting
	 * 2: already visited
	 */
	public int mark { get; set; }
	
67 68 69 70
	/**
	 * The context this source file belongs to.
	 */
	public weak CodeContext context { get; set; }
71

72 73 74 75 76 77 78 79
	public string? content {
		get { return this._content; }
		set {
			this._content = value;
			this.source_array = null;
		}
	}

80
	private Gee.List<UsingDirective> using_directives = new ArrayList<UsingDirective> ();
81

82
	private Gee.List<CodeNode> nodes = new ArrayList<CodeNode> ();
83 84 85
	
	private string cheader_filename = null;
	private string csource_filename = null;
86
	private string cinclude_filename = null;
87
	
88 89 90 91
	private Gee.List<string> header_external_includes = new ArrayList<string> ();
	private Gee.List<string> header_internal_includes = new ArrayList<string> ();
	private Gee.List<string> source_external_includes = new ArrayList<string> ();
	private Gee.List<string> source_internal_includes = new ArrayList<string> ();
92
	
93 94
	private Gee.List<weak SourceFile> header_internal_full_dependencies = new ArrayList<weak SourceFile> ();
	private Gee.List<weak SourceFile> header_internal_dependencies = new ArrayList<weak SourceFile> ();
95

96 97
	private Gee.Set<Symbol> source_symbol_dependencies = new HashSet<Symbol> ();

98 99
	private Gee.ArrayList<string> source_array = null;

100 101
	private MappedFile mapped_file = null;

102 103
	private string? _content = null;

104 105 106 107 108 109 110
	/**
	 * Creates a new source file.
	 *
	 * @param filename source file name
	 * @param pkg      true if this is a VAPI package file
	 * @return         newly created source file
	 */
111
	public SourceFile (CodeContext context, string filename, bool pkg = false, string? content = null) {
112
		this.filename = filename;
113
		this.external_package = pkg;
114
		this.context = context;
115
		this.content = content;
116 117
	}
	
118 119 120 121 122
	/**
	 * Adds a new using directive with the specified namespace.
	 *
	 * @param ns reference to namespace
	 */
123 124 125
	public void add_using_directive (UsingDirective ns) {
		foreach (UsingDirective using_directive in using_directives) {
			if (same_symbol (using_directive.namespace_symbol, ns.namespace_symbol)) {
126 127 128 129
				// ignore duplicates
				return;
			}
		}
130
		using_directives.add (ns);
131
	}
132

133 134 135 136
	public void clear_using_directives () {
		using_directives.clear ();
	}

137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
	bool same_symbol (Symbol? sym1, Symbol? sym2) {
		if (sym1 == sym2) {
			return true;
		}

		var unresolved_symbol1 = sym1 as UnresolvedSymbol;
		var unresolved_symbol2 = sym2 as UnresolvedSymbol;
		if (unresolved_symbol1 != null && unresolved_symbol2 != null) {
			if (same_symbol (unresolved_symbol1.inner, unresolved_symbol2.inner)) {
				return (unresolved_symbol1.name == unresolved_symbol2.name);
			}
		}

		return false;
	}

153 154 155 156 157
	/**
	 * Returns a copy of the list of using directives.
	 *
	 * @return using directive list
	 */
158 159
	public Gee.List<UsingDirective> get_using_directives () {
		return new ReadOnlyList<UsingDirective> (using_directives);
160 161 162
	}
	
	/**
163
	 * Adds the specified code node to this source file.
164
	 *
165
	 * @param node a code node
166
	 */
167
	public void add_node (CodeNode node) {
168
		nodes.add (node);
169
	}
170

171
	public void remove_node (CodeNode node) {
172 173 174
		nodes.remove (node);
	}

175
	/**
176
	 * Returns a copy of the list of code nodes.
177
	 *
178
	 * @return code node list
179
	 */
180 181
	public Gee.List<CodeNode> get_nodes () {
		return new ReadOnlyList<CodeNode> (nodes);
182
	}
183

184
	public void accept (CodeVisitor visitor) {
185 186
		visitor.visit_source_file (this);
	}
187

188
	public void accept_children (CodeVisitor visitor) {
189
		foreach (UsingDirective ns_ref in using_directives) {
190
			ns_ref.accept (visitor);
191 192
		}
		
193 194
		foreach (CodeNode node in nodes) {
			node.accept (visitor);
195
		}
196
	}
197

198
	private string get_subdir () {
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
		if (context.basedir == null) {
			return "";
		}

		// filename and basedir are already canonicalized
		if (filename.has_prefix (context.basedir)) {
			var basename = Path.get_basename (filename);
			var subdir = filename.substring (context.basedir.len (), filename.len () - context.basedir.len () - basename.len ());
			while (subdir[0] == '/') {
				subdir = subdir.offset (1);
			}
			return subdir;
		}
		return "";
	}

215
	private string get_destination_directory () {
216 217 218 219 220 221
		if (context.directory == null) {
			return get_subdir ();
		}
		return "%s/%s".printf (context.directory, get_subdir ());
	}

222 223 224 225 226
	private string get_basename () {
		long dot = filename.pointer_to_offset (filename.rchr (-1, '.'));
		return Path.get_basename (filename.substring (0, dot));
	}

227 228 229 230
	public string get_relative_filename () {
		return get_subdir () + Path.get_basename (filename);
	}

231 232 233 234 235
	/**
	 * Returns the filename to use when generating C header files.
	 *
	 * @return generated C header filename
	 */
236
	public string get_cheader_filename () {
237
		if (cheader_filename == null) {
238
			cheader_filename = "%s%s.h".printf (get_destination_directory (), get_basename ());
239
		}
240 241 242 243 244 245 246 247
		return cheader_filename;
	}
	
	/**
	 * Returns the filename to use when generating C source files.
	 *
	 * @return generated C source filename
	 */
248
	public string get_csource_filename () {
249
		if (csource_filename == null) {
250
			csource_filename = "%s%s.c".printf (get_destination_directory (), get_basename ());
251 252 253
		}
		return csource_filename;
	}
254

255 256 257 258 259 260
	/**
	 * Returns the filename to use when including the generated C header
	 * file.
	 *
	 * @return C header filename to include
	 */
261
	public string get_cinclude_filename () {
262
		if (cinclude_filename == null) {
263
			cinclude_filename = "%s%s.h".printf (get_subdir (), get_basename ());
264 265 266 267
		}
		return cinclude_filename;
	}
	
268 269 270 271
	/**
	 * Adds the specified symbol to the list of symbols code in this source
	 * file depends on.
	 *
272 273 274
	 * TODO Move source and header file dependency analysis to
	 * code generator.
	 *
275 276 277
	 * @param sym      a symbol
	 * @param dep_type type of dependency
	 */
278
	public void add_symbol_dependency (Symbol? sym, SourceFileDependencyType dep_type) {
279
		if (external_package) {
280 281 282
			return;
		}

283
		Symbol s;
284
		
285 286 287
		if (sym is ErrorCode) {
			s = sym.parent_symbol;
		} else if (sym is TypeSymbol ||
288 289 290 291 292
		    sym is Method ||
		    sym is Field ||
		    sym is Property ||
		    sym is Constant) {
			s = sym;
293 294
		} else if (sym is FormalParameter) {
			var fp = (FormalParameter) sym;
295
			s = fp.parameter_type.data_type;
296
			if (s == null) {
297
				/* generic type parameter */
298 299
				return;
			}
300 301 302 303 304
		} else {
			return;
		}
		
		if (dep_type == SourceFileDependencyType.SOURCE) {
305
			source_symbol_dependencies.add (s);
306
			if (s.external_package) {
307
				foreach (string fn in s.get_cheader_filenames ()) {
308
					source_external_includes.add (fn);
309
				}
310
			} else {
311
				foreach (string fn in s.get_cheader_filenames ()) {
312
					source_internal_includes.add (fn);
313
				}
314
			}
315 316
			return;
		}
317

318
		if (s.external_package) {
319
			/* external package */
320
			foreach (string fn in s.get_cheader_filenames ()) {
321
				header_external_includes.add (fn);
322
			}
323 324 325
			return;
		}
		
326
		if (dep_type == SourceFileDependencyType.HEADER_FULL) {
327
			foreach (string fn in s.get_cheader_filenames ()) {
328
				header_internal_includes.add (fn);
329
			}
330
			header_internal_full_dependencies.add (s.source_reference.file);
331
		}
332

333
		header_internal_dependencies.add (s.source_reference.file);
334 335
	}

336 337 338 339
	/**
	 * Adds the symbols that define the specified type to the list of
	 * symbols code in this source file depends on.
	 *
340 341 342
	 * TODO Move source and header file dependency analysis to
	 * code generator.
	 *
343 344 345
	 * @param type     a data type
	 * @param dep_type type of dependency
	 */
346
	public void add_type_dependency (DataType type, SourceFileDependencyType dep_type) {
347 348 349 350 351
		foreach (Symbol type_symbol in type.get_symbols ()) {
			add_symbol_dependency (type_symbol, dep_type);
		}
	}

352
	/**
353
	 * Returns the list of external includes the generated C header file
354 355 356 357
	 * requires.
	 *
	 * @return external include list for C header file
	 */
358 359
	public Gee.List<string> get_header_external_includes () {
		return new ReadOnlyList<string> (header_external_includes);
360 361 362 363 364 365 366 367
	}
	
	/**
	 * Adds the specified filename to the list of package-internal includes
	 * the generated C header file requires.
	 *
	 * @param include internal include for C header file
	 */
368
	public void add_header_internal_include (string include) {
369 370
		/* skip includes to self */
		if (include != get_cinclude_filename ()) {
371
			header_internal_includes.add (include);
372
		}
373 374 375
	}
	
	/**
376 377
	 * Returns the list of package-internal includes the generated C header
	 * file requires.
378 379 380
	 *
	 * @return internal include list for C header file
	 */
381 382
	public Gee.List<string> get_header_internal_includes () {
		return new ReadOnlyList<string> (header_internal_includes);
383 384 385
	}
	
	/**
386 387 388 389 390
	 * Returns the list of external includes the generated C source file
	 * requires.
	 *
	 * @return include list for C source file
	 */
391 392
	public Gee.List<string> get_source_external_includes () {
		return new ReadOnlyList<string> (source_external_includes);
393 394 395 396 397
	}
	
	/**
	 * Returns the list of package-internal includes the generated C source
	 * file requires.
398 399 400
	 *
	 * @return include list for C source file
	 */
401 402
	public Gee.List<string> get_source_internal_includes () {
		return new ReadOnlyList<string> (source_internal_includes);
403
	}
404
	
405 406 407 408 409 410
	/**
	 * Returns the list of source files the generated C header file requires
	 * definitely.
	 *
	 * @return definite source file dependencies
	 */
411 412
	public Gee.List<weak SourceFile> get_header_internal_full_dependencies () {
		return new ReadOnlyList<weak SourceFile> (header_internal_full_dependencies);
413
	}
414 415 416 417 418 419 420
	
	/**
	 * Returns the list of source files the generated C header file loosely
	 * depends on.
	 *
	 * @return loose source file dependencies
	 */
421 422
	public Gee.List<weak SourceFile> get_header_internal_dependencies () {
		return new ReadOnlyList<weak SourceFile> (header_internal_dependencies);
423
	}
424

425 426 427 428
	public Gee.Set<Symbol> get_source_symbol_dependencies () {
		return new ReadOnlySet<Symbol> (source_symbol_dependencies);
	}

429 430 431 432 433 434
	/**
	 * Returns the requested line from this file, loading it if needed.
	 *
	 * @param lineno 1-based line number
	 * @return       the specified source line
	 */
435
	public string? get_source_line (int lineno) {
436
		if (source_array == null) {
437 438 439 440 441
			if (content != null) {
				read_source_lines (content);
			} else {
				read_source_file ();
			}
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
		}
		if (lineno < 1 || lineno > source_array.size) {
			return null;
		}
		return source_array.get (lineno - 1);
	}

	/**
	 * Parses the input file into ::source_array.
	 */
	private void read_source_file () {
		string cont;
		try {
			FileUtils.get_contents (filename, out cont);
		} catch (FileError fe) {
			return;
		}
459 460 461 462 463 464
		read_source_lines (cont);
	}

	private void read_source_lines (string cont)
	{
		source_array = new Gee.ArrayList<string> ();
465
		string[] lines = cont.split ("\n", 0);
466
		int idx;
467 468 469 470 471
		for (idx = 0; lines[idx] != null; ++idx) {
			source_array.add (lines[idx]);
		}
	}

472
	public char* get_mapped_contents () {
473 474 475 476
		if (content != null) {
			return content;
		}

477
		if (mapped_file == null) {
478 479 480 481 482 483
			try {
				mapped_file = new MappedFile (filename, false);
			} catch (FileError e) {
				Report.error (null, "Unable to map file `%s': %s".printf (filename, e.message));
				return null;
			}
484 485 486 487 488 489
		}

		return mapped_file.get_contents ();
	}
	
	public size_t get_mapped_length () {
490 491 492 493
		if (content != null) {
			return content.length;
		}

494 495
		return mapped_file.get_length ();
	}
496 497 498 499 500 501 502

	public bool check (SemanticAnalyzer analyzer) {
		foreach (CodeNode node in nodes) {
			node.check (analyzer);
		}
		return true;
	}
503 504 505 506 507 508
}

public enum Vala.SourceFileDependencyType {
	HEADER_FULL,
	HEADER_SHALLOW,
	SOURCE
509
}