Merge pull request #900 from Blewzman/FixAggregateExceptionGetBaseException
[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 #if NET_4_0 || MOBILE_DYNAMIC
226                         source_list.AddRange (files);
227 #else
228                         source_list.AddRange (files.ToArray ());
229 #endif
230
231                         checkpoints = new Checkpoint [System.Math.Max (1, source_list.Count * 2)];
232                         if (checkpoints.Length > 0)
233                                 checkpoints [0] = new Checkpoint (0, 0);
234                 }
235
236                 public Location (SourceFile file, int row, int column)
237                 {
238                         if (row <= 0)
239                                 token = 0;
240                         else {
241                                 if (column > max_column)
242                                         column = max_column;
243
244                                 long target = -1;
245                                 long delta = 0;
246
247                                 // TODO: For eval only, need better handling of empty
248                                 int file_index = file == null ? 0 : file.Index;
249
250                                 // FIXME: This value is certainly wrong but what was the intension
251                                 int max = checkpoint_index < 10 ?
252                                         checkpoint_index : 10;
253                                 for (int i = 0; i < max; i++) {
254                                         int offset = checkpoints [checkpoint_index - i].LineOffset;
255                                         delta = row - offset;
256                                         if (delta >= 0 &&
257                                                 delta < (1 << line_delta_bits) &&
258                                                 checkpoints[checkpoint_index - i].File == file_index) {
259                                                 target = checkpoint_index - i;
260                                                 break;
261                                         }
262                                 }
263                                 if (target == -1) {
264                                         AddCheckpoint (file_index, row);
265                                         target = checkpoint_index;
266                                         delta = row % (1 << line_delta_bits);
267                                 }
268
269                                 long l = column +
270                                         (delta << column_bits) +
271                                         (target << (line_delta_bits + column_bits));
272 #if FULL_AST
273                                 token = l;
274 #else
275                                 token = l > 0xFFFFFFFF ? 0 : (int) l;
276 #endif
277                         }
278                 }
279
280                 public static Location operator - (Location loc, int columns)
281                 {
282                         return new Location (loc.SourceFile, loc.Row, loc.Column - columns);
283                 }
284
285                 static void AddCheckpoint (int file, int row)
286                 {
287                         if (checkpoints.Length == ++checkpoint_index) {
288                                 Array.Resize (ref checkpoints, checkpoint_index * 2);
289                         }
290                         checkpoints [checkpoint_index] = new Checkpoint (file, row);
291                 }
292
293                 string FormatLocation (string fileName)
294                 {
295                         if (column_bits == 0 || InEmacs)
296                                 return fileName + "(" + Row.ToString () + "):";
297
298                         return fileName + "(" + Row.ToString () + "," + Column.ToString () +
299                                 (Column == max_column ? "+):" : "):");
300                 }
301                 
302                 public override string ToString ()
303                 {
304                         return FormatLocation (Name);
305                 }
306
307                 public string ToStringFullName ()
308                 {
309                         return FormatLocation (NameFullPath);
310                 }
311                 
312                 /// <summary>
313                 ///   Whether the Location is Null
314                 /// </summary>
315                 public bool IsNull {
316                         get { return token == 0; }
317                 }
318
319                 public string Name {
320                         get {
321                                 int index = File;
322                                 if (token == 0 || index <= 0)
323                                         return null;
324
325                                 SourceFile file = source_list [index - 1];
326                                 return file.Name;
327                         }
328                 }
329
330                 public string NameFullPath {
331                         get {
332                                 int index = File;
333                                 if (token == 0 || index <= 0)
334                                         return null;
335
336                                 return source_list[index - 1].FullPathName;
337                         }
338                 }
339
340                 int CheckpointIndex {
341                         get {
342                                 const int checkpoint_mask = (1 << checkpoint_bits) - 1;
343                                 return ((int) (token >> (line_delta_bits + column_bits))) & checkpoint_mask;
344                         }
345                 }
346
347                 public int Row {
348                         get {
349                                 if (token == 0)
350                                         return 1;
351
352                                 int offset = checkpoints[CheckpointIndex].LineOffset;
353
354                                 const int line_delta_mask = (1 << column_bits) - 1;
355                                 return offset + (((int)(token >> column_bits)) & line_delta_mask);
356                         }
357                 }
358
359                 public int Column {
360                         get {
361                                 if (token == 0)
362                                         return 1;
363                                 return (int) (token & column_mask);
364                         }
365                 }
366
367                 public int File {
368                         get {
369                                 if (token == 0)
370                                         return 0;
371 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));
372                                 return checkpoints [CheckpointIndex].File;
373                         }
374                 }
375
376                 // The ISymbolDocumentWriter interface is used by the symbol writer to
377                 // describe a single source file - for each source file there's exactly
378                 // one corresponding ISymbolDocumentWriter instance.
379                 //
380                 // This class has an internal hash table mapping source document names
381                 // to such ISymbolDocumentWriter instances - so there's exactly one
382                 // instance per document.
383                 //
384                 // This property returns the ISymbolDocumentWriter instance which belongs
385                 // to the location's source file.
386                 //
387                 // If we don't have a symbol writer, this property is always null.
388                 public SourceFile SourceFile {
389                         get {
390                                 int index = File;
391                                 if (index == 0)
392                                         return null;
393                                 return source_list [index - 1];
394                         }
395                 }
396
397                 #region IEquatable<Location> Members
398
399                 public bool Equals (Location other)
400                 {
401                         return this.token == other.token;
402                 }
403
404                 #endregion
405         }
406
407         //
408         // A bag of additional locations to support full ast tree
409         //
410         public class LocationsBag
411         {
412                 public class MemberLocations
413                 {
414                         public readonly IList<Tuple<Modifiers, Location>> Modifiers;
415                         List<Location> locations;
416
417                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods)
418                         {
419                                 Modifiers = mods;
420                         }
421
422                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, Location loc)
423                                 : this (mods)
424                         {
425                                 AddLocations (loc);
426                         }
427
428                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, Location[] locs)
429                                 : this (mods)
430                         {
431                                 AddLocations (locs);
432                         }
433
434                         public MemberLocations (IList<Tuple<Modifiers, Location>> mods, List<Location> locs)
435                                 : this (mods)
436                         {
437                                 locations = locs;
438                         }
439
440                         #region Properties
441
442                         public Location this [int index] {
443                                 get {
444                                         return locations [index];
445                                 }
446                         }
447                         
448                         public int Count {
449                                 get {
450                                         return locations.Count;
451                                 }
452                         }
453
454                         #endregion
455
456                         public void AddLocations (Location loc)
457                         {
458                                 if (locations == null) {
459                                         locations = new List<Location> ();
460                                 }
461
462                                 locations.Add (loc);
463                         }
464
465                         public void AddLocations (params Location[] additional)
466                         {
467                                 if (locations == null) {
468                                         locations = new List<Location> (additional);
469                                 } else {
470                                         locations.AddRange (additional);
471                                 }
472                         }
473                 }
474
475                 Dictionary<object, List<Location>> simple_locs = new Dictionary<object, List<Location>> (ReferenceEquality<object>.Default);
476                 Dictionary<MemberCore, MemberLocations> member_locs = new Dictionary<MemberCore, MemberLocations> (ReferenceEquality<MemberCore>.Default);
477
478                 [Conditional ("FULL_AST")]
479                 public void AddLocation (object element, params Location[] locations)
480                 {
481                         simple_locs.Add (element, new List<Location> (locations));
482                 }
483
484                 [Conditional ("FULL_AST")]
485                 public void InsertLocation (object element, int index, Location location)
486                 {
487                         List<Location> found;
488                         if (!simple_locs.TryGetValue (element, out found)) {
489                                 found = new List<Location> ();
490                                 simple_locs.Add (element, found);
491                         }
492
493                         found.Insert (index, location);
494                 }
495
496                 [Conditional ("FULL_AST")]
497                 public void AddStatement (object element, params Location[] locations)
498                 {
499                         if (locations.Length == 0)
500                                 throw new ArgumentException ("Statement is missing semicolon location");
501
502                         AddLocation (element, locations);
503                 }
504
505                 [Conditional ("FULL_AST")]
506                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations)
507                 {
508                         member_locs.Add (member, new MemberLocations (modLocations));
509                 }
510
511                 [Conditional ("FULL_AST")]
512                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, Location location)
513                 {
514                         member_locs.Add (member, new MemberLocations (modLocations, location));
515                 }
516
517                 [Conditional ("FULL_AST")]
518                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, params Location[] locations)
519                 {
520                         member_locs.Add (member, new MemberLocations (modLocations, locations));
521                 }
522
523                 [Conditional ("FULL_AST")]
524                 public void AddMember (MemberCore member, IList<Tuple<Modifiers, Location>> modLocations, List<Location> locations)
525                 {
526                         member_locs.Add (member, new MemberLocations (modLocations, locations));
527                 }
528
529                 [Conditional ("FULL_AST")]
530                 public void AppendTo (object element, Location location)
531                 {
532                         List<Location> found;
533                         if (!simple_locs.TryGetValue (element, out found)) {
534                                 found = new List<Location> ();
535                                 simple_locs.Add (element, found);
536                         }
537
538                         found.Add (location);
539                 }
540
541                 [Conditional ("FULL_AST")]
542                 public void AppendToMember (MemberCore existing, params Location[] locations)
543                 {
544                         MemberLocations member;
545                         if (member_locs.TryGetValue (existing, out member)) {
546                                 member.AddLocations (locations);
547                                 return;
548                         }
549                 }
550
551                 public List<Location> GetLocations (object element)
552                 {
553                         List<Location> found;
554                         simple_locs.TryGetValue (element, out found);
555                         return found;
556                 }
557
558                 public MemberLocations GetMemberLocation (MemberCore element)
559                 {
560                         MemberLocations found;
561                         member_locs.TryGetValue (element, out found);
562                         return found;
563                 }
564         }
565 }