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