Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[mono.git] / mcs / mcs / location.cs
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
13 using System;
14 using System.Collections.Generic;
15 using Mono.CompilerServices.SymbolWriter;
16 using System.Diagnostics;
17 using System.Linq;
18
19 namespace Mono.CSharp
20 {
21         //
22         //  This is one single source file.
23         //
24         public class SourceFile : IEquatable<SourceFile>
25         {
26                 //
27                 // Used by #line directive to track hidden sequence point
28                 // regions
29                 //
30                 struct LocationRegion : IComparable<LocationRegion>
31                 {
32                         public readonly Location Start;
33                         public readonly Location End;
34
35                         public LocationRegion (Location start, Location end)
36                         {
37                                 this.Start = start;
38                                 this.End = end;
39                         }
40
41                         public int CompareTo (LocationRegion other)
42                         {
43                                 if (Start.Row == other.Start.Row)
44                                         return Start.Column.CompareTo (other.Start.Column);
45
46                                 return Start.Row.CompareTo (other.Start.Row);
47                         }
48
49                         public override string ToString ()
50                         {
51                                 return Start.ToString () + " - " + End.ToString ();
52                         }
53                 }
54
55                 static readonly byte[] MD5Algorith = { 96, 166, 110, 64, 207, 100, 130, 76, 182, 240, 66, 212, 129, 114, 167, 153 };
56
57                 public readonly string Name;
58                 public readonly string FullPathName;
59                 public readonly int Index;
60                 public bool AutoGenerated;
61
62                 SourceFileEntry file;
63                 byte[] algGuid, checksum;
64                 List<LocationRegion> hidden_lines;
65
66                 public SourceFile (string name, string path, int index)
67                 {
68                         this.Index = index;
69                         this.Name = name;
70                         this.FullPathName = path;
71                 }
72
73                 public byte[] Checksum {
74                         get {
75                                 return checksum;
76                         }
77                 }
78
79                 public bool HasChecksum {
80                         get {
81                                 return checksum != null;
82                         }
83                 }
84
85                 public SourceFileEntry SourceFileEntry {
86                         get {
87                                 return file;
88                         }
89                 }
90
91                 public void SetChecksum (byte[] checksum)
92                 {
93                         SetChecksum (MD5Algorith, checksum);
94                 }
95
96                 public void SetChecksum (byte[] algorithmGuid, byte[] checksum)
97                 {
98                         this.algGuid = algorithmGuid;
99                         this.checksum = checksum;
100                 }
101
102                 public SourceFileEntry CreateSymbolInfo (MonoSymbolFile symwriter)
103                 {
104                         if (hidden_lines != null)
105                                 hidden_lines.Sort ();
106
107                         file = new SourceFileEntry (symwriter, FullPathName, algGuid, checksum);
108                         if (AutoGenerated)
109                                 file.SetAutoGenerated ();
110
111                         return file;
112                 }
113
114                 public bool Equals (SourceFile other)
115                 {
116                         return FullPathName == other.FullPathName;
117                 }
118
119                 public bool IsHiddenLocation (Location loc)
120                 {
121                         if (hidden_lines == null)
122                                 return false;
123
124                         int index = hidden_lines.BinarySearch (new LocationRegion (loc, loc));
125                         index = ~index;
126                         if (index > 0) {
127                                 var found = hidden_lines[index - 1];
128                                 if (loc.Row < found.End.Row)
129                                         return true;
130                         }
131
132                         return false;
133                 }
134
135                 public void RegisterHiddenScope (Location start, Location end)
136                 {
137                         if (hidden_lines == null)
138                                 hidden_lines = new List<LocationRegion> ();
139
140                         hidden_lines.Add (new LocationRegion (start, end));
141                 }
142
143                 public override string ToString ()
144                 {
145                         return String.Format ("SourceFile ({0}:{1}:{2})", Name, FullPathName, Index);
146                 }
147         }
148
149         /// <summary>
150         ///   Keeps track of the location in the program
151         /// </summary>
152         ///
153         /// <remarks>
154         ///   This uses a compact representation and a couple of auxiliary
155         ///   structures to keep track of tokens to (file,line and column) 
156         ///   mappings. The usage of the bits is:
157         ///   
158         ///     - 16 bits for "checkpoint" which is a mixed concept of
159         ///       file and "line segment"
160         ///     - 8 bits for line delta (offset) from the line segment
161         ///     - 8 bits for column number.
162         ///
163         ///   http://lists.ximian.com/pipermail/mono-devel-list/2004-December/009508.html
164         /// </remarks>
165         public struct Location : IEquatable<Location>
166         {
167                 struct Checkpoint {
168                         public readonly int LineOffset;
169                         public readonly int File;
170
171                         public Checkpoint (int file, int line)
172                         {
173                                 File = file;
174                                 LineOffset = line - (int) (line % (1 << line_delta_bits));
175                         }
176                 }
177
178 #if FULL_AST
179                 readonly long token;
180
181                 const int column_bits = 24;
182                 const int line_delta_bits = 24;
183 #else
184                 readonly int token;
185
186                 const int column_bits = 8;
187                 const int line_delta_bits = 8;
188 #endif
189                 const int checkpoint_bits = 16;
190
191                 const int column_mask = (1 << column_bits) - 1;
192                 const int max_column = column_mask;
193
194                 static List<SourceFile> source_list;
195                 static Checkpoint [] checkpoints;
196                 static int checkpoint_index;
197                 
198                 public readonly static Location Null = new Location ();
199                 public static bool InEmacs;
200                 
201                 static Location ()
202                 {
203                         Reset ();
204                 }
205
206                 public static void Reset ()
207                 {
208                         source_list = new List<SourceFile> ();
209                         checkpoint_index = 0;
210                 }
211
212                 public static void AddFile (SourceFile file)
213                 {
214                         source_list.Add (file);
215                 }
216
217                 // <summary>
218                 //   After adding all source files we want to compile with AddFile(), this method
219                 //   must be called to `reserve' an appropriate number of bits in the token for the
220                 //   source file.  We reserve some extra space for files we encounter via #line
221                 //   directives while parsing.
222                 // </summary>
223                 static public void Initialize (List<SourceFile> files)
224                 {
225                         source_list.AddRange (files);
226
227                         checkpoints = new Checkpoint [System.Math.Max (1, source_list.Count * 2)];
228                         if (checkpoints.Length > 0)
229                                 checkpoints [0] = new Checkpoint (0, 0);
230                 }
231
232                 public Location (SourceFile file, int row, int column)
233                 {
234                         if (row <= 0)
235                                 token = 0;
236                         else {
237                                 if (column > max_column)
238                                         column = max_column;
239
240                                 long target = -1;
241                                 long delta = 0;
242
243                                 // TODO: For eval only, need better handling of empty
244                                 int file_index = file == null ? 0 : file.Index;
245
246                                 // FIXME: This value is certainly wrong but what was the intension
247                                 int max = checkpoint_index < 10 ?
248                                         checkpoint_index : 10;
249                                 for (int i = 0; i < max; i++) {
250                                         int offset = checkpoints [checkpoint_index - i].LineOffset;
251                                         delta = row - offset;
252                                         if (delta >= 0 &&
253                                                 delta < (1 << line_delta_bits) &&
254                                                 checkpoints[checkpoint_index - i].File == file_index) {
255                                                 target = checkpoint_index - i;
256                                                 break;
257                                         }
258                                 }
259                                 if (target == -1) {
260                                         AddCheckpoint (file_index, row);
261                                         target = checkpoint_index;
262                                         delta = row % (1 << line_delta_bits);
263                                 }
264
265                                 long l = column +
266                                         (delta << column_bits) +
267                                         (target << (line_delta_bits + column_bits));
268 #if FULL_AST
269                                 token = l;
270 #else
271                                 token = l > 0xFFFFFFFF ? 0 : (int) l;
272 #endif
273                         }
274                 }
275
276                 public static Location operator - (Location loc, int columns)
277                 {
278                         return new Location (loc.SourceFile, loc.Row, loc.Column - columns);
279                 }
280
281                 static void AddCheckpoint (int file, int row)
282                 {
283                         if (checkpoints.Length == ++checkpoint_index) {
284                                 Array.Resize (ref checkpoints, checkpoint_index * 2);
285                         }
286                         checkpoints [checkpoint_index] = new Checkpoint (file, row);
287                 }
288
289                 string FormatLocation (string fileName)
290                 {
291                         if (column_bits == 0 || InEmacs)
292                                 return fileName + "(" + Row.ToString () + "):";
293
294                         return fileName + "(" + Row.ToString () + "," + Column.ToString () +
295                                 (Column == max_column ? "+):" : "):");
296                 }
297                 
298                 public override string ToString ()
299                 {
300                         return FormatLocation (Name);
301                 }
302
303                 public string ToStringFullName ()
304                 {
305                         return FormatLocation (NameFullPath);
306                 }
307                 
308                 /// <summary>
309                 ///   Whether the Location is Null
310                 /// </summary>
311                 public bool IsNull {
312                         get { return token == 0; }
313                 }
314
315                 public string Name {
316                         get {
317                                 int index = File;
318                                 if (token == 0 || index <= 0)
319                                         return null;
320
321                                 SourceFile file = source_list [index - 1];
322                                 return file.Name;
323                         }
324                 }
325
326                 public string NameFullPath {
327                         get {
328                                 int index = File;
329                                 if (token == 0 || index <= 0)
330                                         return null;
331
332                                 return source_list[index - 1].FullPathName;
333                         }
334                 }
335
336                 int CheckpointIndex {
337                         get {
338                                 const int checkpoint_mask = (1 << checkpoint_bits) - 1;
339                                 return ((int) (token >> (line_delta_bits + column_bits))) & checkpoint_mask;
340                         }
341                 }
342
343                 public int Row {
344                         get {
345                                 if (token == 0)
346                                         return 1;
347
348                                 int offset = checkpoints[CheckpointIndex].LineOffset;
349
350                                 const int line_delta_mask = (1 << column_bits) - 1;
351                                 return offset + (((int)(token >> column_bits)) & line_delta_mask);
352                         }
353                 }
354
355                 public int Column {
356                         get {
357                                 if (token == 0)
358                                         return 1;
359                                 return (int) (token & column_mask);
360                         }
361                 }
362
363                 public int File {
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].File;
369                         }
370                 }
371
372                 // The ISymbolDocumentWriter interface is used by the symbol writer to
373                 // describe a single source file - for each source file there's exactly
374                 // one corresponding ISymbolDocumentWriter instance.
375                 //
376                 // This class has an internal hash table mapping source document names
377                 // to such ISymbolDocumentWriter instances - so there's exactly one
378                 // instance per document.
379                 //
380                 // This property returns the ISymbolDocumentWriter instance which belongs
381                 // to the location's source file.
382                 //
383                 // If we don't have a symbol writer, this property is always null.
384                 public SourceFile SourceFile {
385                         get {
386                                 int index = File;
387                                 if (index == 0)
388                                         return null;
389                                 return source_list [index - 1];
390                         }
391                 }
392
393                 #region IEquatable<Location> Members
394
395                 public bool Equals (Location other)
396                 {
397                         return this.token == other.token;
398                 }
399
400                 #endregion
401         }
402
403         //
404         // A bag of additional locations to support full ast tree
405         //
406         public class LocationsBag
407         {
408                 public class MemberLocations
409                 {
410                         public readonly IList<Tuple<Modifiers, Location>> Modifiers;
411                         List<Location> locations;
412
413                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods)
414                         {
415                                 Modifiers = mods;
416                         }
417
418                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, Location loc)
419                                 : this (mods)
420                         {
421                                 AddLocations (loc);
422                         }
423
424                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, Location[] locs)
425                                 : this (mods)
426                         {
427                                 AddLocations (locs);
428                         }
429
430                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, List<Location> locs)
431                                 : this (mods)
432                         {
433                                 locations = locs;
434                         }
435
436                         #region Properties
437
438                         public Location this [int index] {
439                                 get {
440                                         return locations [index];
441                                 }
442                         }
443                         
444                         public int Count {
445                                 get {
446                                         return locations.Count;
447                                 }
448                         }
449
450                         #endregion
451
452                         public void AddLocations (Location loc)
453                         {
454                                 if (locations == null) {
455                                         locations = new List<Location> ();
456                                 }
457
458                                 locations.Add (loc);
459                         }
460
461                         public void AddLocations (params Location[] additional)
462                         {
463                                 if (locations == null) {
464                                         locations = new List<Location> (additional);
465                                 } else {
466                                         locations.AddRange (additional);
467                                 }
468                         }
469                 }
470
471                 Dictionary<object, List<Location>> simple_locs = new Dictionary<object, List<Location>> (ReferenceEquality<object>.Default);
472                 Dictionary<MemberCore, MemberLocations> member_locs = new Dictionary<MemberCore, MemberLocations> (ReferenceEquality<MemberCore>.Default);
473
474                 [Conditional ("FULL_AST")]
475                 public void AddLocation (object element, params Location[] locations)
476                 {
477                         simple_locs.Add (element, new List<Location> (locations));
478                 }
479
480                 [Conditional ("FULL_AST")]
481                 public void InsertLocation (object element, int index, Location location)
482                 {
483                         List<Location> found;
484                         if (!simple_locs.TryGetValue (element, out found)) {
485                                 found = new List<Location> ();
486                                 simple_locs.Add (element, found);
487                         }
488
489                         found.Insert (index, location);
490                 }
491
492                 [Conditional ("FULL_AST")]
493                 public void AddStatement (object element, params Location[] locations)
494                 {
495                         if (locations.Length == 0)
496                                 throw new ArgumentException ("Statement is missing semicolon location");
497
498                         AddLocation (element, locations);
499                 }
500
501                 [Conditional ("FULL_AST")]
502                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations)
503                 {
504                         member_locs.Add (member, new MemberLocations (modLocations));
505                 }
506
507                 [Conditional ("FULL_AST")]
508                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, Location location)
509                 {
510                         member_locs.Add (member, new MemberLocations (modLocations, location));
511                 }
512
513                 [Conditional ("FULL_AST")]
514                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, params Location[] locations)
515                 {
516                         member_locs.Add (member, new MemberLocations (modLocations, locations));
517                 }
518
519                 [Conditional ("FULL_AST")]
520                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, List<Location> locations)
521                 {
522                         member_locs.Add (member, new MemberLocations (modLocations, locations));
523                 }
524
525                 [Conditional ("FULL_AST")]
526                 public void AppendTo (object element, Location location)
527                 {
528                         List<Location> found;
529                         if (!simple_locs.TryGetValue (element, out found)) {
530                                 found = new List<Location> ();
531                                 simple_locs.Add (element, found);
532                         }
533
534                         found.Add (location);
535                 }
536
537                 [Conditional ("FULL_AST")]
538                 public void AppendToMember (MemberCore existing, params Location[] locations)
539                 {
540                         MemberLocations member;
541                         if (member_locs.TryGetValue (existing, out member)) {
542                                 member.AddLocations (locations);
543                                 return;
544                         }
545                 }
546
547                 public List<Location> GetLocations (object element)
548                 {
549                         List<Location> found;
550                         simple_locs.TryGetValue (element, out found);
551                         return found;
552                 }
553
554                 public MemberLocations GetMemberLocation (MemberCore element)
555                 {
556                         MemberLocations found;
557                         member_locs.TryGetValue (element, out found);
558                         return found;
559                 }
560         }
561 }