Merge pull request #334 from robwilkens/bug5747
[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 int current_source;
196                 static Checkpoint [] checkpoints;
197                 static int checkpoint_index;
198                 
199                 public readonly static Location Null = new Location ();
200                 public static bool InEmacs;
201                 
202                 static Location ()
203                 {
204                         Reset ();
205                 }
206
207                 public static void Reset ()
208                 {
209                         source_list = new List<SourceFile> ();
210                         current_source = 0;
211                         checkpoint_index = 0;
212                 }
213
214                 public static void AddFile (SourceFile file)
215                 {
216                         source_list.Add (file);
217                 }
218
219                 // <summary>
220                 //   After adding all source files we want to compile with AddFile(), this method
221                 //   must be called to `reserve' an appropriate number of bits in the token for the
222                 //   source file.  We reserve some extra space for files we encounter via #line
223                 //   directives while parsing.
224                 // </summary>
225                 static public void Initialize (List<SourceFile> files)
226                 {
227 #if NET_4_0 || MONODROID
228                         source_list.AddRange (files);
229 #else
230                         source_list.AddRange (files.ToArray ());
231 #endif
232
233                         checkpoints = new Checkpoint [System.Math.Max (1, source_list.Count * 2)];
234                         if (checkpoints.Length > 0)
235                                 checkpoints [0] = new Checkpoint (0, 0);
236                 }
237
238                 static public void Push (SourceFile file)
239                 {
240                         current_source = file != null ? file.Index : -1;
241                         // File is always pushed before being changed.
242                 }
243                 
244                 public Location (int row, int column)
245                 {
246                         if (row <= 0)
247                                 token = 0;
248                         else {
249                                 if (column > max_column)
250                                         column = max_column;
251
252                                 long target = -1;
253                                 long delta = 0;
254
255                                 // FIXME: This value is certainly wrong but what was the intension
256                                 int max = checkpoint_index < 10 ?
257                                         checkpoint_index : 10;
258                                 for (int i = 0; i < max; i++) {
259                                         int offset = checkpoints [checkpoint_index - i].LineOffset;
260                                         delta = row - offset;
261                                         if (delta >= 0 &&
262                                                 delta < (1 << line_delta_bits) &&
263                                                 checkpoints [checkpoint_index - i].File == current_source) {
264                                                 target = checkpoint_index - i;
265                                                 break;
266                                         }
267                                 }
268                                 if (target == -1) {
269                                         AddCheckpoint (current_source, row);
270                                         target = checkpoint_index;
271                                         delta = row % (1 << line_delta_bits);
272                                 }
273
274                                 long l = column +
275                                         (delta << column_bits) +
276                                         (target << (line_delta_bits + column_bits));
277 #if FULL_AST
278                                 token = l;
279 #else
280                                 token = l > 0xFFFFFFFF ? 0 : (int) l;
281 #endif
282                         }
283                 }
284
285                 public static Location operator - (Location loc, int columns)
286                 {
287                         return new Location (loc.Row, loc.Column - columns);
288                 }
289
290                 static void AddCheckpoint (int file, int row)
291                 {
292                         if (checkpoints.Length == ++checkpoint_index) {
293                                 Array.Resize (ref checkpoints, checkpoint_index * 2);
294                         }
295                         checkpoints [checkpoint_index] = new Checkpoint (file, row);
296                 }
297
298                 string FormatLocation (string fileName)
299                 {
300                         if (column_bits == 0 || InEmacs)
301                                 return fileName + "(" + Row.ToString () + "):";
302
303                         return fileName + "(" + Row.ToString () + "," + Column.ToString () +
304                                 (Column == max_column ? "+):" : "):");
305                 }
306                 
307                 public override string ToString ()
308                 {
309                         return FormatLocation (Name);
310                 }
311
312                 public string ToStringFullName ()
313                 {
314                         return FormatLocation (NameFullPath);
315                 }
316                 
317                 /// <summary>
318                 ///   Whether the Location is Null
319                 /// </summary>
320                 public bool IsNull {
321                         get { return token == 0; }
322                 }
323
324                 public string Name {
325                         get {
326                                 int index = File;
327                                 if (token == 0 || index <= 0)
328                                         return null;
329
330                                 SourceFile file = source_list [index - 1];
331                                 return file.Name;
332                         }
333                 }
334
335                 public string NameFullPath {
336                         get {
337                                 int index = File;
338                                 if (token == 0 || index <= 0)
339                                         return null;
340
341                                 return source_list[index - 1].FullPathName;
342                         }
343                 }
344
345                 int CheckpointIndex {
346                         get {
347                                 const int checkpoint_mask = (1 << checkpoint_bits) - 1;
348                                 return ((int) (token >> (line_delta_bits + column_bits))) & checkpoint_mask;
349                         }
350                 }
351
352                 public int Row {
353                         get {
354                                 if (token == 0)
355                                         return 1;
356
357                                 int offset = checkpoints[CheckpointIndex].LineOffset;
358
359                                 const int line_delta_mask = (1 << column_bits) - 1;
360                                 return offset + (((int)(token >> column_bits)) & line_delta_mask);
361                         }
362                 }
363
364                 public int Column {
365                         get {
366                                 if (token == 0)
367                                         return 1;
368                                 return (int) (token & column_mask);
369                         }
370                 }
371
372                 public int File {
373                         get {
374                                 if (token == 0)
375                                         return 0;
376 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));
377                                 return checkpoints [CheckpointIndex].File;
378                         }
379                 }
380
381                 // The ISymbolDocumentWriter interface is used by the symbol writer to
382                 // describe a single source file - for each source file there's exactly
383                 // one corresponding ISymbolDocumentWriter instance.
384                 //
385                 // This class has an internal hash table mapping source document names
386                 // to such ISymbolDocumentWriter instances - so there's exactly one
387                 // instance per document.
388                 //
389                 // This property returns the ISymbolDocumentWriter instance which belongs
390                 // to the location's source file.
391                 //
392                 // If we don't have a symbol writer, this property is always null.
393                 public SourceFile SourceFile {
394                         get {
395                                 int index = File;
396                                 if (index == 0)
397                                         return null;
398                                 return source_list [index - 1];
399                         }
400                 }
401
402                 #region IEquatable<Location> Members
403
404                 public bool Equals (Location other)
405                 {
406                         return this.token == other.token;
407                 }
408
409                 #endregion
410         }
411
412         //
413         // A bag of additional locations to support full ast tree
414         //
415         public class LocationsBag
416         {
417                 public class MemberLocations
418                 {
419                         public readonly IList<Tuple<Modifiers, Location>> Modifiers;
420                         Location[] locations;
421
422                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, Location[] locs)
423                         {
424                                 Modifiers = mods;
425                                 locations = locs;
426                         }
427
428                         #region Properties
429
430                         public Location this [int index] {
431                                 get {
432                                         return locations [index];
433                                 }
434                         }
435                         
436                         public int Count {
437                                 get {
438                                         return locations.Length;
439                                 }
440                         }
441
442                         #endregion
443
444                         public void AddLocations (params Location[] additional)
445                         {
446                                 if (locations == null) {
447                                         locations = additional;
448                                 } else {
449                                         int pos = locations.Length;
450                                         Array.Resize (ref locations, pos + additional.Length);
451                                         additional.CopyTo (locations, pos);
452                                 }
453                         }
454                 }
455
456                 Dictionary<object, Location[]> simple_locs = new Dictionary<object, Location[]> (ReferenceEquality<object>.Default);
457                 Dictionary<MemberCore, MemberLocations> member_locs = new Dictionary<MemberCore, MemberLocations> (ReferenceEquality<MemberCore>.Default);
458
459                 [Conditional ("FULL_AST")]
460                 public void AddLocation (object element, params Location[] locations)
461                 {
462                         simple_locs.Add (element, locations);
463                 }
464
465                 [Conditional ("FULL_AST")]
466                 public void AddStatement (object element, params Location[] locations)
467                 {
468                         if (locations.Length == 0)
469                                 throw new ArgumentException ("Statement is missing semicolon location");
470
471                         simple_locs.Add (element, locations);
472                 }
473
474                 [Conditional ("FULL_AST")]
475                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, params Location[] locations)
476                 {
477                         member_locs.Add (member, new MemberLocations (modLocations, locations));
478                 }
479
480                 [Conditional ("FULL_AST")]
481                 public void AppendTo (object existing, params Location[] locations)
482                 {
483                         Location[] locs;
484                         if (simple_locs.TryGetValue (existing, out locs)) {
485                                 simple_locs [existing] = locs.Concat (locations).ToArray ();
486                                 return;
487                         }
488                 }
489
490                 [Conditional ("FULL_AST")]
491                 public void AppendToMember (MemberCore existing, params Location[] locations)
492                 {
493                         MemberLocations member;
494                         if (member_locs.TryGetValue (existing, out member)) {
495                                 member.AddLocations (locations);
496                                 return;
497                         }
498                 }
499
500                 public Location[] GetLocations (object element)
501                 {
502                         Location[] found;
503                         simple_locs.TryGetValue (element, out found);
504                         return found;
505                 }
506
507                 public MemberLocations GetMemberLocation (MemberCore element)
508                 {
509                         MemberLocations found;
510                         member_locs.TryGetValue (element, out found);
511                         return found;
512                 }
513         }
514 }