2004-01-19 Atsushi Enomoto <atsushi@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 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                 public 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;
70                 
71                 static Location ()
72                 {
73                         source_files = new Hashtable ();
74                         source_list = new ArrayList ();
75                         current_source = 0;
76                         Null.token = 0;
77                 }
78
79                 // <summary>
80                 //   This must be called before parsing/tokenizing any files.
81                 // </summary>
82                 static public void AddFile (string name)
83                 {
84                         string path = Path.GetFullPath (name);
85
86                         if (source_files.Contains (path)){
87                                 Report.Warning (2002, name, "Source file '{0}' specified multiple times");
88                                 return;
89                         }
90
91                         source_files.Add (path, ++source_count);
92                         source_list.Add (new SourceFile (name, path, source_count));
93                 }
94
95                 static public SourceFile[] SourceFiles {
96                         get {
97                                 SourceFile[] retval = new SourceFile [source_list.Count];
98                                 source_list.CopyTo (retval, 0);
99                                 return retval;
100                         }
101                 }
102
103                 static int log2 (int number)
104                 {
105                         int bits = 0;
106                         while (number > 0) {
107                                 bits++;
108                                 number /= 2;
109                         }
110
111                         return bits;
112                 }
113
114                 // <summary>
115                 //   After adding all source files we want to compile with AddFile(), this method
116                 //   must be called to `reserve' an appropriate number of bits in the token for the
117                 //   source file.  We reserve some extra space for files we encounter via #line
118                 //   directives while parsing.
119                 // </summary>
120                 static public void Initialize ()
121                 {
122                         source_bits = log2 (source_list.Count) + 2;
123                         source_mask = (1 << source_bits) - 1;
124                 }
125
126                 // <remarks>
127                 //   This is used when we encounter a #line preprocessing directive.
128                 // </remarks>
129                 static public SourceFile LookupFile (string name)
130                 {
131                         string path = name == "" ? "" : Path.GetFullPath (name);
132
133                         if (!source_files.Contains (path)) {
134                                 if (source_count >= (1 << source_bits))
135                                         return new SourceFile (name, path, 0);
136
137                                 source_files.Add (path, ++source_count);
138                                 SourceFile retval = new SourceFile (name, path, source_count);
139                                 source_list.Add (retval);
140                                 return retval;
141                         }
142
143                         int index = (int) source_files [path];
144                         return (SourceFile) source_list [index - 1];
145                 }
146
147                 static public void Push (SourceFile file)
148                 {
149                         current_source = file.Index;
150                 }
151
152                 // <remarks>
153                 //   If we're compiling with debugging support, this is called between parsing
154                 //   and code generation to register all the source files with the
155                 //   symbol writer.
156                 // </remarks>
157                 static public void DefineSymbolDocuments (SymbolWriter symwriter)
158                 {
159                         foreach (SourceFile file in source_list) {
160                                 file.SourceFileEntry = symwriter.DefineDocument (file.Path);
161                         }
162                 }
163                 
164                 public Location (int row)
165                 {
166                         if (row < 0)
167                                 token = 0;
168                         else
169                                 token = current_source + (row << source_bits);
170                 }
171
172                 public override string ToString ()
173                 {
174                         return Name + ": (" + Row + ")";
175                 }
176                 
177                 /// <summary>
178                 ///   Whether the Location is Null
179                 /// </summary>
180                 static public bool IsNull (Location l)
181                 {
182                         return l.token == 0;
183                 }
184
185                 public string Name {
186                         get {
187                                 int index = token & source_mask;
188                                 if ((token == 0) || (index == 0))
189                                         return "Internal";
190
191                                 SourceFile file = (SourceFile) source_list [index - 1];
192                                 return file.Name;
193                         }
194                 }
195
196                 public int Row {
197                         get {
198                                 if (token == 0)
199                                         return 1;
200
201                                 return token >> source_bits;
202                         }
203                 }
204
205                 public int File {
206                         get {
207                                 return token & source_mask;
208                         }
209                 }
210
211                 // The ISymbolDocumentWriter interface is used by the symbol writer to
212                 // describe a single source file - for each source file there's exactly
213                 // one corresponding ISymbolDocumentWriter instance.
214                 //
215                 // This class has an internal hash table mapping source document names
216                 // to such ISymbolDocumentWriter instances - so there's exactly one
217                 // instance per document.
218                 //
219                 // This property returns the ISymbolDocumentWriter instance which belongs
220                 // to the location's source file.
221                 //
222                 // If we don't have a symbol writer, this property is always null.
223                 public SourceFile SourceFile {
224                         get {
225                                 int index = token & source_mask;
226                                 if (index == 0)
227                                         return null;
228                                 return (SourceFile) source_list [index - 1];
229                         }
230                 }
231         }
232 }