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