In .:
[mono.git] / mcs / gmcs / location.cs
1 //
2 // location.cs: Keeps track of the location of source code entity
3 //
4 // Author:
5 //   Miguel de Icaza
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9
10 using System;
11 using System.IO;
12 using System.Collections;
13 using Mono.CompilerServices.SymbolWriter;
14
15 namespace Mono.CSharp {
16         /// <summary>
17         ///   This is one single source file.
18         /// </summary>
19         /// <remarks>
20         ///   This is intentionally a class and not a struct since we need
21         ///   to pass this by reference.
22         /// </remarks>
23         public sealed class SourceFile : ISourceFile {
24                 public readonly string Name;
25                 public readonly string Path;
26                 public readonly int Index;
27                 public SourceFileEntry SourceFileEntry;
28                 public bool HasLineDirective;
29
30                 public SourceFile (string name, string path, int index)
31                 {
32                         this.Index = index;
33                         this.Name = name;
34                         this.Path = path;
35                 }
36
37                 SourceFileEntry ISourceFile.Entry {
38                         get { return SourceFileEntry; }
39                 }
40
41                 public override string ToString ()
42                 {
43                         return String.Format ("SourceFile ({0}:{1}:{2}:{3})",
44                                               Name, Path, Index, SourceFileEntry);
45                 }
46         }
47
48         /// <summary>
49         ///   Keeps track of the location in the program
50         /// </summary>
51         ///
52         /// <remarks>
53         ///   This uses a compact representation and a couple of auxiliary
54         ///   structures to keep track of tokens to (file,line) mappings.
55         ///
56         ///   We could probably also keep track of columns by storing those
57         ///   in 8 bits (and say, map anything after char 255 to be `255+').
58         /// </remarks>
59         public struct Location {
60                 int token; 
61
62                 static ArrayList source_list;
63                 static Hashtable source_files;
64                 static int source_bits;
65                 static int source_mask;
66                 static int source_count;
67                 static int current_source;
68
69                 public readonly static Location Null = new Location (-1);
70                 
71                 static Location ()
72                 {
73                         source_files = new Hashtable ();
74                         source_list = new ArrayList ();
75                         current_source = 0;
76                 }
77
78                 public static void Reset ()
79                 {
80                         source_files = new Hashtable ();
81                         source_list = new ArrayList ();
82                         current_source = 0;
83                         source_count = 0;
84                 }
85
86                 // <summary>
87                 //   This must be called before parsing/tokenizing any files.
88                 // </summary>
89                 static public void AddFile (string name)
90                 {
91                         string path = Path.GetFullPath (name);
92
93                         if (source_files.Contains (path)){
94                                 int id = (int) source_files [path];
95                                 string other_name = ((SourceFile) source_list [id - 1]).Name;
96                                 if (name.Equals (other_name))
97                                         Report.Warning (2002, "Source file `{0}' specified multiple times", name);
98                                 else
99                                         Report.Warning (2002, "Source filenames `{0}' and `{1}' both refer to the same file: {2}", name, other_name, path);
100                                 return;
101                         }
102
103                         source_files.Add (path, ++source_count);
104                         source_list.Add (new SourceFile (name, path, source_count));
105                 }
106
107                 static public SourceFile[] SourceFiles {
108                         get {
109                                 SourceFile[] retval = new SourceFile [source_list.Count];
110                                 source_list.CopyTo (retval, 0);
111                                 return retval;
112                         }
113                 }
114
115                 static int log2 (int number)
116                 {
117                         int bits = 0;
118                         while (number > 0) {
119                                 bits++;
120                                 number /= 2;
121                         }
122
123                         return bits;
124                 }
125
126                 // <summary>
127                 //   After adding all source files we want to compile with AddFile(), this method
128                 //   must be called to `reserve' an appropriate number of bits in the token for the
129                 //   source file.  We reserve some extra space for files we encounter via #line
130                 //   directives while parsing.
131                 // </summary>
132                 static public void Initialize ()
133                 {
134                         source_bits = log2 (source_list.Count) + 2;
135                         source_mask = (1 << source_bits) - 1;
136                 }
137
138                 // <remarks>
139                 //   This is used when we encounter a #line preprocessing directive.
140                 // </remarks>
141                 static public SourceFile LookupFile (string name)
142                 {
143                         string path = name == "" ? "" : Path.GetFullPath (name);
144
145                         if (!source_files.Contains (path)) {
146                                 if (source_count >= (1 << source_bits))
147                                         return new SourceFile (name, path, 0);
148
149                                 source_files.Add (path, ++source_count);
150                                 SourceFile retval = new SourceFile (name, path, source_count);
151                                 source_list.Add (retval);
152                                 return retval;
153                         }
154
155                         int index = (int) source_files [path];
156                         return (SourceFile) source_list [index - 1];
157                 }
158
159                 static public void Push (SourceFile file)
160                 {
161                         current_source = file.Index;
162                 }
163
164                 // <remarks>
165                 //   If we're compiling with debugging support, this is called between parsing
166                 //   and code generation to register all the source files with the
167                 //   symbol writer.
168                 // </remarks>
169                 static public void DefineSymbolDocuments (SymbolWriter symwriter)
170                 {
171                         foreach (SourceFile file in source_list) {
172                                 file.SourceFileEntry = symwriter.DefineDocument (file.Path);
173                         }
174                 }
175                 
176                 public Location (int row)
177                 {
178                         if (row < 0)
179                                 token = 0;
180                         else
181                                 token = current_source + (row << source_bits);
182                 }
183
184                 public override string ToString ()
185                 {
186                         return Name + ": (" + Row + ")";
187                 }
188                 
189                 /// <summary>
190                 ///   Whether the Location is Null
191                 /// </summary>
192                 static public bool IsNull (Location l)
193                 {
194                         return l.token == 0;
195                 }
196
197                 public string Name {
198                         get {
199                                 int index = token & source_mask;
200                                 if ((token == 0) || (index == 0))
201                                         return "Internal";
202
203                                 SourceFile file = (SourceFile) source_list [index - 1];
204                                 return file.Name;
205                         }
206                 }
207
208                 public int Row {
209                         get {
210                                 if (token == 0)
211                                         return 1;
212
213                                 return token >> source_bits;
214                         }
215                 }
216
217                 public int File {
218                         get {
219                                 return token & source_mask;
220                         }
221                 }
222
223                 // The ISymbolDocumentWriter interface is used by the symbol writer to
224                 // describe a single source file - for each source file there's exactly
225                 // one corresponding ISymbolDocumentWriter instance.
226                 //
227                 // This class has an internal hash table mapping source document names
228                 // to such ISymbolDocumentWriter instances - so there's exactly one
229                 // instance per document.
230                 //
231                 // This property returns the ISymbolDocumentWriter instance which belongs
232                 // to the location's source file.
233                 //
234                 // If we don't have a symbol writer, this property is always null.
235                 public SourceFile SourceFile {
236                         get {
237                                 int index = token & source_mask;
238                                 if (index == 0)
239                                         return null;
240                                 return (SourceFile) source_list [index - 1];
241                         }
242                 }
243         }
244 }