2008-05-10 Marek Safar <marek.safar@gmail.com>
[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 //
8 // Copyright 2001 Ximian, Inc.
9 // Copyright 2005 Novell, Inc.
10 //
11
12 using System;
13 using System.IO;
14 using System.Collections;
15 using Mono.CompilerServices.SymbolWriter;
16
17 namespace Mono.CSharp {
18         /// <summary>
19         ///   This is one single source file.
20         /// </summary>
21         /// <remarks>
22         ///   This is intentionally a class and not a struct since we need
23         ///   to pass this by reference.
24         /// </remarks>
25         public sealed class SourceFile : ISourceFile {
26                 public readonly string Name;
27                 public readonly string Path;
28                 public readonly int Index;
29                 public SourceFileEntry SourceFileEntry;
30
31                 public SourceFile (string name, string path, int index)
32                 {
33                         this.Index = index;
34                         this.Name = name;
35                         this.Path = path;
36                 }
37
38                 SourceFileEntry ISourceFile.Entry {
39                         get { return SourceFileEntry; }
40                 }
41
42                 public override string ToString ()
43                 {
44                         return String.Format ("SourceFile ({0}:{1}:{2}:{3})",
45                                               Name, Path, Index, SourceFileEntry);
46                 }
47         }
48
49         /// <summary>
50         ///   Keeps track of the location in the program
51         /// </summary>
52         ///
53         /// <remarks>
54         ///   This uses a compact representation and a couple of auxiliary
55         ///   structures to keep track of tokens to (file,line and column) 
56         ///   mappings. The usage of the bits is:
57         ///   
58         ///     - 16 bits for "checkpoint" which is a mixed concept of
59         ///       file and "line segment"
60         ///     - 8 bits for line delta (offset) from the line segment
61         ///     - 8 bits for column number.
62         ///
63         ///   http://lists.ximian.com/pipermail/mono-devel-list/2004-December/009508.html
64         /// </remarks>
65         public struct Location {
66                 int token; 
67
68                 struct Checkpoint {
69                         public readonly int LineOffset;
70                         public readonly int File;
71
72                         public Checkpoint (int file, int line)
73                         {
74                                 File = file;
75                                 LineOffset = line - (int) (line % (1 << line_delta_bits));
76                         }
77                 }
78
79                 static ArrayList source_list;
80                 static Hashtable source_files;
81                 static int checkpoint_bits;
82                 static int source_count;
83                 static int current_source;
84                 static int line_delta_bits;
85                 static int line_delta_mask;
86                 static int column_bits;
87                 static int column_mask;
88                 static Checkpoint [] checkpoints;
89                 static int checkpoint_index;
90                 
91                 public readonly static Location Null = new Location (-1);
92                 public static bool InEmacs;
93                 
94                 static Location ()
95                 {
96                         source_files = new Hashtable ();
97                         source_list = new ArrayList ();
98                         current_source = 0;
99                         checkpoints = new Checkpoint [10];
100                 }
101
102                 public static void Reset ()
103                 {
104                         source_files = new Hashtable ();
105                         source_list = new ArrayList ();
106                         current_source = 0;
107                         source_count = 0;
108                 }
109
110                 // <summary>
111                 //   This must be called before parsing/tokenizing any files.
112                 // </summary>
113                 static public void AddFile (string name)
114                 {
115                         string path = Path.GetFullPath (name);
116
117                         if (source_files.Contains (path)){
118                                 int id = (int) source_files [path];
119                                 string other_name = ((SourceFile) source_list [id - 1]).Name;
120                                 if (name.Equals (other_name))
121                                         Report.Warning (2002, 1, "Source file `{0}' specified multiple times", other_name);
122                                 else
123                                         Report.Warning (2002, 1, "Source filenames `{0}' and `{1}' both refer to the same file: {2}", name, other_name, path);
124                                 return;
125                         }
126
127                         source_files.Add (path, ++source_count);
128                         source_list.Add (new SourceFile (name, path, source_count));
129                 }
130
131                 static public SourceFile[] SourceFiles {
132                         get {
133                                 SourceFile[] retval = new SourceFile [source_list.Count];
134                                 source_list.CopyTo (retval, 0);
135                                 return retval;
136                         }
137                 }
138
139                 // <summary>
140                 //   After adding all source files we want to compile with AddFile(), this method
141                 //   must be called to `reserve' an appropriate number of bits in the token for the
142                 //   source file.  We reserve some extra space for files we encounter via #line
143                 //   directives while parsing.
144                 // </summary>
145                 static public void Initialize ()
146                 {
147                         checkpoints = new Checkpoint [source_list.Count * 2];
148                         if (checkpoints.Length > 0)
149                                 checkpoints [0] = new Checkpoint (0, 0);
150
151                         column_bits = 8;
152                         column_mask = 0xFF;
153                         line_delta_bits = 8;
154                         line_delta_mask = 0xFF00;
155                         checkpoint_index = 0;
156                         checkpoint_bits = 16;
157                 }
158
159                 // <remarks>
160                 //   This is used when we encounter a #line preprocessing directive.
161                 // </remarks>
162                 static public SourceFile LookupFile (string name)
163                 {
164                         string path = name.Length == 0 ? string.Empty : Path.GetFullPath (name);
165
166                         if (!source_files.Contains (path)) {
167                                 if (source_count >= (1 << checkpoint_bits))
168                                         return new SourceFile (name, path, 0);
169
170                                 source_files.Add (path, ++source_count);
171                                 SourceFile retval = new SourceFile (name, path, source_count);
172                                 source_list.Add (retval);
173                                 return retval;
174                         }
175
176                         int index = (int) source_files [path];
177                         return (SourceFile) source_list [index - 1];
178                 }
179
180                 static public void Push (SourceFile file)
181                 {
182                         current_source = file.Index;
183                         // File is always pushed before being changed.
184                 }
185
186                 // <remarks>
187                 //   If we're compiling with debugging support, this is called between parsing
188                 //   and code generation to register all the source files with the
189                 //   symbol writer.
190                 // </remarks>
191                 static public void DefineSymbolDocuments (MonoSymbolWriter symwriter)
192                 {
193                         foreach (SourceFile file in source_list) {
194                                 file.SourceFileEntry = symwriter.DefineDocument (file.Path);
195                         }
196                 }
197                 
198                 public Location (int row)
199                         : this (row, 0)
200                 {
201                 }
202
203                 public Location (int row, int column)
204                 {
205                         if (row <= 0)
206                                 token = 0;
207                         else {
208                                 if (column > 255)
209                                         column = 255;
210                                 int target = -1;
211                                 int delta = 0;
212                                 int max = checkpoint_index < 10 ?
213                                         checkpoint_index : 10;
214                                 for (int i = 0; i < max; i++) {
215                                         int offset = checkpoints [checkpoint_index - i].LineOffset;
216                                         delta = row - offset;
217                                         if (delta >= 0 &&
218                                                 delta < (1 << line_delta_bits) &&
219                                                 checkpoints [checkpoint_index - i].File == current_source) {
220                                                 target = checkpoint_index - i;
221                                                 break;
222                                         }
223                                 }
224                                 if (target == -1) {
225                                         AddCheckpoint (current_source, row);
226                                         target = checkpoint_index;
227                                         delta = row % (1 << line_delta_bits);
228                                 }
229                                 long l = column +
230                                         (long) (delta << column_bits) +
231                                         (long) (target << (line_delta_bits + column_bits));
232                                 token = l > 0xFFFFFFFF ? 0 : (int) l;
233                         }
234                 }
235
236                 static void AddCheckpoint (int file, int row)
237                 {
238                         if (checkpoints.Length == ++checkpoint_index) {
239                                 Checkpoint [] tmp = new Checkpoint [checkpoint_index * 2];
240                                 Array.Copy (checkpoints, tmp, checkpoints.Length);
241                                 checkpoints = tmp;
242                         }
243                         checkpoints [checkpoint_index] = new Checkpoint (file, row);
244                 }
245                 
246                 public override string ToString ()
247                 {
248                         if (column_bits == 0 || InEmacs)
249                                 return Name + "(" + Row.ToString () + "):";
250                         else
251                                 return Name + "(" + Row.ToString () + "," + Column.ToString () +
252                                         (Column == column_mask ? "+):" : "):");
253                 }
254                 
255                 /// <summary>
256                 ///   Whether the Location is Null
257                 /// </summary>
258                 public bool IsNull {
259                         get { return token == 0; }
260                 }
261
262                 public string Name {
263                         get {
264                                 int index = File;
265                                 if (token == 0 || index == 0)
266                                         return "Internal";
267
268                                 SourceFile file = (SourceFile) source_list [index - 1];
269                                 return file.Name;
270                         }
271                 }
272
273                 int CheckpointIndex {
274                         get { return (int) ((token & 0xFFFF0000) >> (line_delta_bits + column_bits)); }
275                 }
276
277                 public int Row {
278                         get {
279                                 if (token == 0)
280                                         return 1;
281                                 return checkpoints [CheckpointIndex].LineOffset + ((token & line_delta_mask) >> column_bits);
282                         }
283                 }
284
285                 public int Column {
286                         get {
287                                 if (token == 0)
288                                         return 1;
289                                 return (int) (token & column_mask);
290                         }
291                 }
292
293                 public int File {
294                         get {
295                                 if (token == 0)
296                                         return 0;
297 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));
298                                 return checkpoints [CheckpointIndex].File;
299                         }
300                 }
301
302                 // The ISymbolDocumentWriter interface is used by the symbol writer to
303                 // describe a single source file - for each source file there's exactly
304                 // one corresponding ISymbolDocumentWriter instance.
305                 //
306                 // This class has an internal hash table mapping source document names
307                 // to such ISymbolDocumentWriter instances - so there's exactly one
308                 // instance per document.
309                 //
310                 // This property returns the ISymbolDocumentWriter instance which belongs
311                 // to the location's source file.
312                 //
313                 // If we don't have a symbol writer, this property is always null.
314                 public SourceFile SourceFile {
315                         get {
316                                 int index = File;
317                                 if (index == 0)
318                                         return null;
319                                 return (SourceFile) source_list [index - 1];
320                         }
321                 }
322         }
323
324         public class LocatedToken
325         {
326                 public readonly Location Location;
327                 public readonly string Value;
328
329                 public LocatedToken (Location loc, string value)
330                 {
331                         Location = loc;
332                         Value = value;
333                 }
334
335                 public override string ToString ()
336                 {
337                         return Location.ToString () + Value;
338                 }
339         }
340 }