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