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