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