2004-07-14 Martin Baulig <martin@ximian.com>
[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 System.Diagnostics.SymbolStore;
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 {
24                 public readonly string Name;
25                 public readonly string Path;
26                 public readonly int Index;
27                 public ISymbolDocumentWriter SymbolDocument;
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
38         /// <summary>
39         ///   Keeps track of the location in the program
40         /// </summary>
41         ///
42         /// <remarks>
43         ///   This uses a compact representation and a couple of auxiliary
44         ///   structures to keep track of tokens to (file,line) mappings.
45         ///
46         ///   We could probably also keep track of columns by storing those
47         ///   in 8 bits (and say, map anything after char 255 to be `255+').
48         /// </remarks>
49         public struct Location {
50                 public int token; 
51
52                 static ArrayList source_list;
53                 static Hashtable source_files;
54                 static int source_bits;
55                 static int source_mask;
56                 static int source_count;
57                 static int current_source;
58
59                 public readonly static Location Null;
60                 
61                 static Location ()
62                 {
63                         source_files = new Hashtable ();
64                         source_list = new ArrayList ();
65                         current_source = 0;
66                         Null.token = 0;
67                 }
68
69                 // <summary>
70                 //   This must be called before parsing/tokenizing any files.
71                 // </summary>
72                 static public void AddFile (string name)
73                 {
74                         string path = Path.GetFullPath (name);
75
76                         if (source_files.Contains (path)){
77                                 Report.Warning (
78                                         2002,
79                                         "Source file `" + name + "' specified multiple times");
80                                 return;
81                         }
82
83                         source_files.Add (path, ++source_count);
84                         source_list.Add (new SourceFile (name, path, source_count));
85                 }
86
87                 static public SourceFile[] SourceFiles {
88                         get {
89                                 SourceFile[] retval = new SourceFile [source_list.Count];
90                                 source_list.CopyTo (retval, 0);
91                                 return retval;
92                         }
93                 }
94
95                 static int log2 (int number)
96                 {
97                         int bits = 0;
98                         while (number > 0) {
99                                 bits++;
100                                 number /= 2;
101                         }
102
103                         return bits;
104                 }
105
106                 // <summary>
107                 //   After adding all source files we want to compile with AddFile(), this method
108                 //   must be called to `reserve' an appropriate number of bits in the token for the
109                 //   source file.  We reserve some extra space for files we encounter via #line
110                 //   directives while parsing.
111                 // </summary>
112                 static public void Initialize ()
113                 {
114                         source_bits = log2 (source_list.Count) + 2;
115                         source_mask = (1 << source_bits) - 1;
116                 }
117
118                 // <remarks>
119                 //   This is used when we encounter a #line preprocessing directive.
120                 // </remarks>
121                 static public SourceFile LookupFile (string name)
122                 {
123                         string path = name == "" ? "" : Path.GetFullPath (name);
124
125                         if (!source_files.Contains (path)) {
126                                 if (source_count >= (1 << source_bits))
127                                         return new SourceFile (name, path, 0);
128
129                                 source_files.Add (path, ++source_count);
130                                 SourceFile retval = new SourceFile (name, path, source_count);
131                                 source_list.Add (retval);
132                                 return retval;
133                         }
134
135                         int index = (int) source_files [path];
136                         return (SourceFile) source_list [index - 1];
137                 }
138
139                 static public void Push (SourceFile file)
140                 {
141                         current_source = file.Index;
142                 }
143
144                 // <remarks>
145                 //   If we're compiling with debugging support, this is called between parsing and
146                 //   code generation to register all the source files with the symbol writer.           //
147                 // </remarks>
148                 static public void DefineSymbolDocuments (SymbolWriter symwriter)
149                 {
150                         foreach (SourceFile file in source_list) {
151                                 if (file.HasLineDirective)
152                                         continue;
153                                 file.SymbolDocument = symwriter.DefineDocument (file.Path);
154                         }
155                 }
156                 
157                 public Location (int row)
158                 {
159                         if (row < 0)
160                                 token = 0;
161                         else
162                                 token = current_source + (row << source_bits);
163                 }
164
165                 public override string ToString ()
166                 {
167                         return Name + ": (" + Row + ")";
168                 }
169                 
170                 /// <summary>
171                 ///   Whether the Location is Null
172                 /// </summary>
173                 static public bool IsNull (Location l)
174                 {
175                         return l.token == 0;
176                 }
177
178                 public string Name {
179                         get {
180                                 int index = token & source_mask;
181                                 if ((token == 0) || (index == 0))
182                                         return "Internal";
183
184                                 SourceFile file = (SourceFile) source_list [index - 1];
185                                 return file.Name;
186                         }
187                 }
188
189                 public int Row {
190                         get {
191                                 if (token == 0)
192                                         return 1;
193
194                                 return token >> source_bits;
195                         }
196                 }
197
198                 public int File {
199                         get {
200                                 return token & source_mask;
201                         }
202                 }
203
204                 // The ISymbolDocumentWriter interface is used by the symbol writer to
205                 // describe a single source file - for each source file there's exactly
206                 // one corresponding ISymbolDocumentWriter instance.
207                 //
208                 // This class has an internal hash table mapping source document names
209                 // to such ISymbolDocumentWriter instances - so there's exactly one
210                 // instance per document.
211                 //
212                 // This property returns the ISymbolDocumentWriter instance which belongs
213                 // to the location's source file.
214                 //
215                 // If we don't have a symbol writer, this property is always null.
216                 public ISymbolDocumentWriter SymbolDocument {
217                         get {
218                                 int index = token & source_mask;
219                                 if (index == 0)
220                                         return null;
221                                 SourceFile file = (SourceFile) source_list [index - 1];
222                                 return file.SymbolDocument;
223                         }
224                 }
225         }
226 }