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