2007-09-25 Marek Habersack <mhabersack@novell.com>
[mono.git] / mcs / class / Mono.CompilerServices.SymbolWriter / MonoSymbolWriter.cs
1 //
2 // Mono.CSharp.Debugger/MonoSymbolWriter.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@ximian.com)
6 //
7 // This is the default implementation of the System.Diagnostics.SymbolStore.ISymbolWriter
8 // interface.
9 //
10 // (C) 2002 Ximian, Inc.  http://www.ximian.com
11 //
12
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System;
35 using System.Runtime.CompilerServices;
36 using System.Collections;
37 using System.IO;
38         
39 namespace Mono.CompilerServices.SymbolWriter
40 {
41         public interface ISourceFile
42         {
43                 SourceFileEntry Entry {
44                         get;
45                 }
46         }
47
48         public interface ISourceMethod
49         {
50                 string Name {
51                         get;
52                 }
53
54                 int NamespaceID {
55                         get;
56                 }
57
58                 int Token {
59                         get;
60                 }
61         }
62
63         public class MonoSymbolWriter
64         {
65                 protected ArrayList locals = null;
66                 protected ArrayList methods = null;
67                 protected ArrayList sources = null;
68                 protected readonly MonoSymbolFile file;
69                 private string filename = null;
70                 
71                 LineNumberEntry [] current_method_lines;
72                 int current_method_lines_pos = 0;
73
74                 internal ISourceFile[] Sources {
75                         get {
76                                 ISourceFile[] retval = new ISourceFile [sources.Count];
77                                 sources.CopyTo (retval, 0);
78                                 return retval;
79                         }
80                 }
81
82                 private SourceMethod current_method = null;
83
84                 public MonoSymbolWriter (string filename)
85                 {
86                         this.methods = new ArrayList ();
87                         this.sources = new ArrayList ();
88                         this.locals = new ArrayList ();
89                         this.file = new MonoSymbolFile ();
90
91                         this.filename = filename + ".mdb";
92                         
93                         this.current_method_lines = new LineNumberEntry [50];
94                 }
95
96                 public void CloseNamespace ()
97                 { }
98
99                 public void DefineLocalVariable (int index, string name, byte[] signature)
100                 {
101                         if (current_method == null)
102                                 return;
103
104                         current_method.AddLocal (index, name, signature);
105                 }
106
107                 public void MarkSequencePoint (int offset, int line, int column)
108                 {
109                         if (current_method == null)
110                                 return;
111
112                         if (current_method_lines_pos == current_method_lines.Length) {
113                                 LineNumberEntry [] tmp = current_method_lines;
114                                 current_method_lines = new LineNumberEntry [current_method_lines.Length * 2];
115                                 Array.Copy (tmp, current_method_lines, current_method_lines_pos);
116                         }
117                         
118                         current_method_lines [current_method_lines_pos++] = new LineNumberEntry (line, offset);
119                 }
120
121                 public void OpenMethod (ISourceFile file, ISourceMethod method,
122                                         int startRow, int startColumn,
123                                         int endRow, int endColumn)
124                 {
125                         SourceMethod source = new SourceMethod (
126                                 file, method, startRow, startColumn, endRow, endColumn);
127
128                         current_method = source;
129                         methods.Add (current_method);
130                 }
131
132                 public void CloseMethod ()
133                 {
134                         current_method.SetLineNumbers (
135                                 current_method_lines, current_method_lines_pos);
136                         current_method_lines_pos = 0;
137                         
138                         current_method = null;
139                 }
140
141                 public SourceFileEntry DefineDocument (string url)
142                 {
143                         SourceFileEntry entry = new SourceFileEntry (file, url);
144                         sources.Add (entry);
145                         return entry;
146                 }
147
148                 public int DefineNamespace (string name, SourceFileEntry source,
149                                             string[] using_clauses, int parent)
150                 {
151                         if ((source == null) || (using_clauses == null))
152                                 throw new NullReferenceException ();
153
154                         return source.DefineNamespace (name, using_clauses, parent);
155                 }
156
157                 public int OpenScope (int startOffset)
158                 {
159                         if (current_method == null)
160                                 return 0;
161
162                         current_method.StartBlock (startOffset);
163                         return 0;
164                 }
165
166                 public void CloseScope (int endOffset)
167                 {
168                         if (current_method == null)
169                                 return;
170
171                         current_method.EndBlock (endOffset);
172                 }
173
174                 public void WriteSymbolFile (Guid guid)
175                 {
176                         foreach (SourceMethod method in methods) {
177                                 method.SourceFile.Entry.DefineMethod (
178                                         method.Method.Name, method.Method.Token,
179                                         method.Locals, method.Lines, method.Blocks,
180                                         method.Start.Row, method.End.Row,
181                                         method.Method.NamespaceID);
182                         }
183
184                         try {
185                                 // We mmap the file, so unlink the previous version since it may be in use
186                                 File.Delete (filename);
187                         } catch {
188                                 // We can safely ignore
189                         }
190                         using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) {
191                                 file.CreateSymbolFile (guid, fs);
192                         }
193                 }
194
195                 protected class SourceMethod
196                 {
197                         LineNumberEntry [] lines;
198                         private ArrayList _locals;
199                         private ArrayList _blocks;
200                         private Stack _block_stack;
201                         private int next_block_id = 0;
202                         private ISourceMethod _method;
203                         private ISourceFile _file;
204                         private LineNumberEntry _start, _end;
205
206                         private LexicalBlockEntry _implicit_block;
207
208                         public SourceMethod (ISourceFile file, ISourceMethod method,
209                                              int startLine, int startColumn,
210                                              int endLine, int endColumn)
211                         {
212                                 this._file = file;
213                                 this._method = method;
214
215                                 this._start = new LineNumberEntry (startLine, 0);
216                                 this._end = new LineNumberEntry (endLine, 0);
217
218                                 this._implicit_block = new LexicalBlockEntry (0, 0);
219                         }
220
221                         public void StartBlock (int startOffset)
222                         {
223                                 LexicalBlockEntry block = new LexicalBlockEntry (
224                                         ++next_block_id, startOffset);
225                                 if (_block_stack == null)
226                                         _block_stack = new Stack ();
227                                 _block_stack.Push (block);
228                                 if (_blocks == null)
229                                         _blocks = new ArrayList ();
230                                 _blocks.Add (block);
231                         }
232
233                         public void EndBlock (int endOffset)
234                         {
235                                 LexicalBlockEntry block =
236                                         (LexicalBlockEntry) _block_stack.Pop ();
237
238                                 block.Close (endOffset);
239                         }
240
241                         public LexicalBlockEntry[] Blocks {
242                                 get {
243                                         if (_blocks == null)
244                                                 return new LexicalBlockEntry [0];
245                                         else {
246                                                 LexicalBlockEntry[] retval =
247                                                         new LexicalBlockEntry [_blocks.Count];
248                                                 _blocks.CopyTo (retval, 0);
249                                                 return retval;
250                                         }
251                                 }
252                         }
253
254                         public LexicalBlockEntry CurrentBlock {
255                                 get {
256                                         if ((_block_stack != null) && (_block_stack.Count > 0))
257                                                 return (LexicalBlockEntry) _block_stack.Peek ();
258                                         else
259                                                 return _implicit_block;
260                                 }
261                         }
262
263                         public LineNumberEntry[] Lines {
264                                 get {
265                                         return lines;
266                                 }
267                         }
268
269                         public LocalVariableEntry[] Locals {
270                                 get {
271                                         if (_locals == null)
272                                                 return new LocalVariableEntry [0];
273                                         else {
274                                                 LocalVariableEntry[] retval =
275                                                         new LocalVariableEntry [_locals.Count];
276                                                 _locals.CopyTo (retval, 0);
277                                                 return retval;
278                                         }
279                                 }
280                         }
281
282                         public void AddLocal (int index, string name, byte[] signature)
283                         {
284                                 if (_locals == null)
285                                         _locals = new ArrayList ();
286                                 _locals.Add (new LocalVariableEntry (
287                                                      index, name, signature, CurrentBlock.Index));
288                         }
289
290                         public ISourceFile SourceFile {
291                                 get { return _file; }
292                         }
293
294                         public ISourceMethod Method {
295                                 get { return _method; }
296                         }
297
298                         public LineNumberEntry Start {
299                                 get { return _start; }
300                         }
301
302                         public LineNumberEntry End {
303                                 get { return _end; }
304                         }
305
306                         //
307                         // Passes on the lines from the MonoSymbolWriter. This method is
308                         // free to mutate the lns array, and it does.
309                         //
310                         internal void SetLineNumbers (LineNumberEntry [] lns, int count)
311                         {
312                                 int pos = 0;
313
314                                 int last_offset = -1;
315                                 int last_row = -1;
316                                 for (int i = 0; i < count; i++) {
317                                         LineNumberEntry line = lns [i];
318
319                                         if (line.Offset > last_offset) {
320                                                 if (last_row >= 0)
321                                                         lns [pos++] = new LineNumberEntry (
322                                                                 last_row, last_offset);
323
324                                                 last_row = line.Row;
325                                                 last_offset = line.Offset;
326                                         } else if (line.Row > last_row) {
327                                                 last_row = line.Row;
328                                         }
329                                 }
330                         
331                                 lines = new LineNumberEntry [count + ((last_row >= 0) ? 1 : 0)];
332                                 Array.Copy (lns, lines, pos);
333                                 if (last_row >= 0)
334                                         lines [pos] = new LineNumberEntry (
335                                                 last_row, last_offset);
336                         }
337                 }
338         }
339 }