PageRenderTime 56ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/mcs/mcs/location.cs

https://github.com/afaerber/mono
C# | 571 lines | 424 code | 76 blank | 71 comment | 70 complexity | da390d9d619efd17c948022848e63620 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0, Unlicense
  1. //
  2. // location.cs: Keeps track of the location of source code entity
  3. //
  4. // Author:
  5. // Miguel de Icaza
  6. // Atsushi Enomoto <atsushi@ximian.com>
  7. // Marek Safar (marek.safar@gmail.com)
  8. //
  9. // Copyright 2001 Ximian, Inc.
  10. // Copyright 2005 Novell, Inc.
  11. //
  12. using System;
  13. using System.IO;
  14. using System.Collections.Generic;
  15. using Mono.CompilerServices.SymbolWriter;
  16. using System.Diagnostics;
  17. using System.Linq;
  18. namespace Mono.CSharp {
  19. /// <summary>
  20. /// This is one single source file.
  21. /// </summary>
  22. /// <remarks>
  23. /// This is intentionally a class and not a struct since we need
  24. /// to pass this by reference.
  25. /// </remarks>
  26. public class SourceFile : ISourceFile {
  27. public readonly string Name;
  28. public readonly string Path;
  29. public readonly int Index;
  30. public bool AutoGenerated;
  31. public bool IsIncludeFile;
  32. SourceFileEntry file;
  33. byte[] guid, checksum;
  34. public SourceFile (string name, string path, int index, bool is_include)
  35. {
  36. this.Index = index;
  37. this.Name = name;
  38. this.Path = path;
  39. this.IsIncludeFile = is_include;
  40. }
  41. public SourceFileEntry SourceFileEntry {
  42. get { return file; }
  43. }
  44. SourceFileEntry ISourceFile.Entry {
  45. get { return file; }
  46. }
  47. public void SetChecksum (byte[] guid, byte[] checksum)
  48. {
  49. this.guid = guid;
  50. this.checksum = checksum;
  51. }
  52. public virtual void DefineSymbolInfo (MonoSymbolWriter symwriter)
  53. {
  54. if (guid != null)
  55. file = symwriter.DefineDocument (Path, guid, checksum);
  56. else {
  57. file = symwriter.DefineDocument (Path);
  58. if (AutoGenerated)
  59. file.SetAutoGenerated ();
  60. }
  61. }
  62. public override string ToString ()
  63. {
  64. return String.Format ("SourceFile ({0}:{1}:{2}:{3})",
  65. Name, Path, Index, SourceFileEntry);
  66. }
  67. }
  68. public class CompilationUnit : SourceFile, ICompileUnit
  69. {
  70. CompileUnitEntry comp_unit;
  71. Dictionary<string, SourceFile> include_files;
  72. Dictionary<string, bool> conditionals;
  73. public CompilationUnit (string name, string path, int index)
  74. : base (name, path, index, false)
  75. { }
  76. public void AddFile (SourceFile file)
  77. {
  78. if (file == this)
  79. return;
  80. if (include_files == null)
  81. include_files = new Dictionary<string, SourceFile> ();
  82. if (!include_files.ContainsKey (file.Path))
  83. include_files.Add (file.Path, file);
  84. }
  85. public void AddDefine (string value)
  86. {
  87. if (conditionals == null)
  88. conditionals = new Dictionary<string, bool> (2);
  89. conditionals [value] = true;
  90. }
  91. public void AddUndefine (string value)
  92. {
  93. if (conditionals == null)
  94. conditionals = new Dictionary<string, bool> (2);
  95. conditionals [value] = false;
  96. }
  97. CompileUnitEntry ICompileUnit.Entry {
  98. get { return comp_unit; }
  99. }
  100. public CompileUnitEntry CompileUnitEntry {
  101. get { return comp_unit; }
  102. }
  103. public override void DefineSymbolInfo (MonoSymbolWriter symwriter)
  104. {
  105. base.DefineSymbolInfo (symwriter);
  106. comp_unit = symwriter.DefineCompilationUnit (SourceFileEntry);
  107. if (include_files != null) {
  108. foreach (SourceFile include in include_files.Values) {
  109. include.DefineSymbolInfo (symwriter);
  110. comp_unit.AddFile (include.SourceFileEntry);
  111. }
  112. }
  113. }
  114. public bool IsConditionalDefined (string value)
  115. {
  116. if (conditionals != null) {
  117. bool res;
  118. if (conditionals.TryGetValue (value, out res))
  119. return res;
  120. // When conditional was undefined
  121. if (conditionals.ContainsKey (value))
  122. return false;
  123. }
  124. return RootContext.IsConditionalDefined (value);
  125. }
  126. }
  127. /// <summary>
  128. /// Keeps track of the location in the program
  129. /// </summary>
  130. ///
  131. /// <remarks>
  132. /// This uses a compact representation and a couple of auxiliary
  133. /// structures to keep track of tokens to (file,line and column)
  134. /// mappings. The usage of the bits is:
  135. ///
  136. /// - 16 bits for "checkpoint" which is a mixed concept of
  137. /// file and "line segment"
  138. /// - 8 bits for line delta (offset) from the line segment
  139. /// - 8 bits for column number.
  140. ///
  141. /// http://lists.ximian.com/pipermail/mono-devel-list/2004-December/009508.html
  142. /// </remarks>
  143. public struct Location {
  144. int token;
  145. struct Checkpoint {
  146. public readonly int LineOffset;
  147. public readonly int CompilationUnit;
  148. public readonly int File;
  149. public Checkpoint (int compile_unit, int file, int line)
  150. {
  151. File = file;
  152. CompilationUnit = compile_unit;
  153. LineOffset = line - (int) (line % (1 << line_delta_bits));
  154. }
  155. }
  156. static List<SourceFile> source_list;
  157. static List<CompilationUnit> compile_units;
  158. static Dictionary<string, int> source_files;
  159. static int checkpoint_bits;
  160. static int source_count;
  161. static int current_source;
  162. static int current_compile_unit;
  163. static int line_delta_bits;
  164. static int line_delta_mask;
  165. static int column_bits;
  166. static int column_mask;
  167. static Checkpoint [] checkpoints;
  168. static int checkpoint_index;
  169. public readonly static Location Null = new Location (-1);
  170. public static bool InEmacs;
  171. static Location ()
  172. {
  173. Reset ();
  174. checkpoints = new Checkpoint [10];
  175. }
  176. public static void Reset ()
  177. {
  178. source_files = new Dictionary<string, int> ();
  179. source_list = new List<SourceFile> ();
  180. compile_units = new List<CompilationUnit> ();
  181. current_source = 0;
  182. current_compile_unit = 0;
  183. source_count = 0;
  184. }
  185. // <summary>
  186. // This must be called before parsing/tokenizing any files.
  187. // </summary>
  188. static public void AddFile (Report r, string name)
  189. {
  190. string path = Path.GetFullPath (name);
  191. int id;
  192. if (source_files.TryGetValue (path, out id)){
  193. string other_name = source_list [id - 1].Name;
  194. if (name.Equals (other_name))
  195. r.Warning (2002, 1, "Source file `{0}' specified multiple times", other_name);
  196. else
  197. r.Warning (2002, 1, "Source filenames `{0}' and `{1}' both refer to the same file: {2}", name, other_name, path);
  198. return;
  199. }
  200. source_files.Add (path, ++source_count);
  201. CompilationUnit unit = new CompilationUnit (name, path, source_count);
  202. source_list.Add (unit);
  203. compile_units.Add (unit);
  204. }
  205. public static IList<CompilationUnit> SourceFiles {
  206. get {
  207. return compile_units;
  208. }
  209. }
  210. // <summary>
  211. // After adding all source files we want to compile with AddFile(), this method
  212. // must be called to `reserve' an appropriate number of bits in the token for the
  213. // source file. We reserve some extra space for files we encounter via #line
  214. // directives while parsing.
  215. // </summary>
  216. static public void Initialize ()
  217. {
  218. checkpoints = new Checkpoint [source_list.Count * 2];
  219. if (checkpoints.Length > 0)
  220. checkpoints [0] = new Checkpoint (0, 0, 0);
  221. column_bits = 8;
  222. column_mask = 0xFF;
  223. line_delta_bits = 8;
  224. line_delta_mask = 0xFF00;
  225. checkpoint_index = 0;
  226. checkpoint_bits = 16;
  227. }
  228. // <remarks>
  229. // This is used when we encounter a #line preprocessing directive.
  230. // </remarks>
  231. static public SourceFile LookupFile (CompilationUnit comp_unit, string name)
  232. {
  233. string path;
  234. if (!Path.IsPathRooted (name)) {
  235. string root = Path.GetDirectoryName (comp_unit.Path);
  236. path = Path.Combine (root, name);
  237. } else
  238. path = name;
  239. if (!source_files.ContainsKey (path)) {
  240. if (source_count >= (1 << checkpoint_bits))
  241. return new SourceFile (name, path, 0, true);
  242. source_files.Add (path, ++source_count);
  243. SourceFile retval = new SourceFile (name, path, source_count, true);
  244. source_list.Add (retval);
  245. return retval;
  246. }
  247. int index = (int) source_files [path];
  248. return (SourceFile) source_list [index - 1];
  249. }
  250. static public void Push (CompilationUnit compile_unit, SourceFile file)
  251. {
  252. current_source = file != null ? file.Index : -1;
  253. current_compile_unit = compile_unit != null ? compile_unit.Index : -1;
  254. // File is always pushed before being changed.
  255. }
  256. // <remarks>
  257. // If we're compiling with debugging support, this is called between parsing
  258. // and code generation to register all the source files with the
  259. // symbol writer.
  260. // </remarks>
  261. static public void DefineSymbolDocuments (MonoSymbolWriter symwriter)
  262. {
  263. foreach (CompilationUnit unit in compile_units)
  264. unit.DefineSymbolInfo (symwriter);
  265. }
  266. public Location (int row)
  267. : this (row, 0)
  268. {
  269. }
  270. public Location (int row, int column)
  271. {
  272. if (row <= 0)
  273. token = 0;
  274. else {
  275. if (column > 254)
  276. column = 254;
  277. if (column < 0)
  278. column = 255;
  279. int target = -1;
  280. int delta = 0;
  281. int max = checkpoint_index < 10 ?
  282. checkpoint_index : 10;
  283. for (int i = 0; i < max; i++) {
  284. int offset = checkpoints [checkpoint_index - i].LineOffset;
  285. delta = row - offset;
  286. if (delta >= 0 &&
  287. delta < (1 << line_delta_bits) &&
  288. checkpoints [checkpoint_index - i].File == current_source) {
  289. target = checkpoint_index - i;
  290. break;
  291. }
  292. }
  293. if (target == -1) {
  294. AddCheckpoint (current_compile_unit, current_source, row);
  295. target = checkpoint_index;
  296. delta = row % (1 << line_delta_bits);
  297. }
  298. long l = column +
  299. (long) (delta << column_bits) +
  300. (long) (target << (line_delta_bits + column_bits));
  301. token = l > 0xFFFFFFFF ? 0 : (int) l;
  302. }
  303. }
  304. public static Location operator - (Location loc, int columns)
  305. {
  306. return new Location (loc.Row, loc.Column - columns);
  307. }
  308. static void AddCheckpoint (int compile_unit, int file, int row)
  309. {
  310. if (checkpoints.Length == ++checkpoint_index) {
  311. Checkpoint [] tmp = new Checkpoint [checkpoint_index * 2];
  312. Array.Copy (checkpoints, tmp, checkpoints.Length);
  313. checkpoints = tmp;
  314. }
  315. checkpoints [checkpoint_index] = new Checkpoint (compile_unit, file, row);
  316. }
  317. public override string ToString ()
  318. {
  319. if (column_bits == 0 || InEmacs)
  320. return Name + "(" + Row.ToString () + "):";
  321. else
  322. return Name + "(" + Row.ToString () + "," + Column.ToString () +
  323. (Column == column_mask ? "+):" : "):");
  324. }
  325. /// <summary>
  326. /// Whether the Location is Null
  327. /// </summary>
  328. public bool IsNull {
  329. get { return token == 0; }
  330. }
  331. public string Name {
  332. get {
  333. int index = File;
  334. if (token == 0 || index == 0)
  335. return "Internal";
  336. SourceFile file = (SourceFile) source_list [index - 1];
  337. return file.Name;
  338. }
  339. }
  340. int CheckpointIndex {
  341. get { return (int) ((token & 0xFFFF0000) >> (line_delta_bits + column_bits)); }
  342. }
  343. public int Row {
  344. get {
  345. if (token == 0)
  346. return 1;
  347. return checkpoints [CheckpointIndex].LineOffset + ((token & line_delta_mask) >> column_bits);
  348. }
  349. }
  350. public int Column {
  351. get {
  352. if (token == 0)
  353. return 1;
  354. int col = (int) (token & column_mask);
  355. return col == 255 ? 1 : col;
  356. }
  357. }
  358. public bool Hidden {
  359. get {
  360. return (int) (token & column_mask) == 255;
  361. }
  362. }
  363. public int CompilationUnitIndex {
  364. get {
  365. if (token == 0)
  366. return 0;
  367. if (checkpoints.Length <= CheckpointIndex) throw new Exception (String.Format ("Should not happen. Token is {0:X04}, checkpoints are {1}, index is {2}", token, checkpoints.Length, CheckpointIndex));
  368. return checkpoints [CheckpointIndex].CompilationUnit;
  369. }
  370. }
  371. public int File {
  372. get {
  373. if (token == 0)
  374. return 0;
  375. if (checkpoints.Length <= CheckpointIndex) throw new Exception (String.Format ("Should not happen. Token is {0:X04}, checkpoints are {1}, index is {2}", token, checkpoints.Length, CheckpointIndex));
  376. return checkpoints [CheckpointIndex].File;
  377. }
  378. }
  379. // The ISymbolDocumentWriter interface is used by the symbol writer to
  380. // describe a single source file - for each source file there's exactly
  381. // one corresponding ISymbolDocumentWriter instance.
  382. //
  383. // This class has an internal hash table mapping source document names
  384. // to such ISymbolDocumentWriter instances - so there's exactly one
  385. // instance per document.
  386. //
  387. // This property returns the ISymbolDocumentWriter instance which belongs
  388. // to the location's source file.
  389. //
  390. // If we don't have a symbol writer, this property is always null.
  391. public SourceFile SourceFile {
  392. get {
  393. int index = File;
  394. if (index == 0)
  395. return null;
  396. return (SourceFile) source_list [index - 1];
  397. }
  398. }
  399. public CompilationUnit CompilationUnit {
  400. get {
  401. int index = CompilationUnitIndex;
  402. if (index == 0)
  403. return null;
  404. return (CompilationUnit) source_list [index - 1];
  405. }
  406. }
  407. }
  408. //
  409. // A bag of additional locations to support full ast tree
  410. //
  411. public class LocationsBag
  412. {
  413. public class MemberLocations
  414. {
  415. public readonly IList<Tuple<Modifiers, Location>> Modifiers;
  416. Location[] locations;
  417. public MemberLocations (IList<Tuple<Modifiers, Location>> mods, Location[] locs)
  418. {
  419. Modifiers = mods;
  420. locations = locs;
  421. }
  422. #region Properties
  423. public Location this [int index] {
  424. get {
  425. return locations [index];
  426. }
  427. }
  428. public int Count {
  429. get {
  430. return locations.Length;
  431. }
  432. }
  433. #endregion
  434. public void AddLocations (params Location[] additional)
  435. {
  436. if (locations == null) {
  437. locations = additional;
  438. } else {
  439. int pos = locations.Length;
  440. Array.Resize (ref locations, pos + additional.Length);
  441. additional.CopyTo (locations, pos);
  442. }
  443. }
  444. }
  445. Dictionary<object, Location[]> simple_locs = new Dictionary<object, Location[]> (ReferenceEquality<object>.Default);
  446. Dictionary<MemberCore, MemberLocations> member_locs = new Dictionary<MemberCore, MemberLocations> (ReferenceEquality<MemberCore>.Default);
  447. [Conditional ("FULL_AST")]
  448. public void AddLocation (object element, params Location[] locations)
  449. {
  450. simple_locs.Add (element, locations);
  451. }
  452. [Conditional ("FULL_AST")]
  453. public void AddStatement (object element, params Location[] locations)
  454. {
  455. if (locations.Length == 0)
  456. throw new ArgumentException ("Statement is missing semicolon location");
  457. simple_locs.Add (element, locations);
  458. }
  459. [Conditional ("FULL_AST")]
  460. public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, params Location[] locations)
  461. {
  462. member_locs.Add (member, new MemberLocations (modLocations, locations));
  463. }
  464. [Conditional ("FULL_AST")]
  465. public void AppendTo (object existing, params Location[] locations)
  466. {
  467. Location[] locs;
  468. if (simple_locs.TryGetValue (existing, out locs)) {
  469. simple_locs [existing] = locs.Concat (locations).ToArray ();
  470. return;
  471. }
  472. }
  473. [Conditional ("FULL_AST")]
  474. public void AppendToMember (MemberCore existing, params Location[] locations)
  475. {
  476. MemberLocations member;
  477. if (member_locs.TryGetValue (existing, out member)) {
  478. member.AddLocations (locations);
  479. return;
  480. }
  481. }
  482. public Location[] GetLocations (object element)
  483. {
  484. Location[] found;
  485. simple_locs.TryGetValue (element, out found);
  486. return found;
  487. }
  488. public MemberLocations GetMemberLocation (MemberCore element)
  489. {
  490. MemberLocations found;
  491. member_locs.TryGetValue (element, out found);
  492. return found;
  493. }
  494. }
  495. }