New test.
[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                         using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) {
185                                 file.CreateSymbolFile (guid, fs);
186                         }
187                 }
188
189                 protected class SourceMethod
190                 {
191                         LineNumberEntry [] lines;
192                         private ArrayList _locals;
193                         private ArrayList _blocks;
194                         private Stack _block_stack;
195                         private int next_block_id = 0;
196                         private ISourceMethod _method;
197                         private ISourceFile _file;
198                         private LineNumberEntry _start, _end;
199
200                         private LexicalBlockEntry _implicit_block;
201
202                         public SourceMethod (ISourceFile file, ISourceMethod method,
203                                              int startLine, int startColumn,
204                                              int endLine, int endColumn)
205                         {
206                                 this._file = file;
207                                 this._method = method;
208
209                                 this._start = new LineNumberEntry (startLine, 0);
210                                 this._end = new LineNumberEntry (endLine, 0);
211
212                                 this._implicit_block = new LexicalBlockEntry (0, 0);
213                         }
214
215                         public void StartBlock (int startOffset)
216                         {
217                                 LexicalBlockEntry block = new LexicalBlockEntry (
218                                         ++next_block_id, startOffset);
219                                 if (_block_stack == null)
220                                         _block_stack = new Stack ();
221                                 _block_stack.Push (block);
222                                 if (_blocks == null)
223                                         _blocks = new ArrayList ();
224                                 _blocks.Add (block);
225                         }
226
227                         public void EndBlock (int endOffset)
228                         {
229                                 LexicalBlockEntry block =
230                                         (LexicalBlockEntry) _block_stack.Pop ();
231
232                                 block.Close (endOffset);
233                         }
234
235                         public LexicalBlockEntry[] Blocks {
236                                 get {
237                                         if (_blocks == null)
238                                                 return new LexicalBlockEntry [0];
239                                         else {
240                                                 LexicalBlockEntry[] retval =
241                                                         new LexicalBlockEntry [_blocks.Count];
242                                                 _blocks.CopyTo (retval, 0);
243                                                 return retval;
244                                         }
245                                 }
246                         }
247
248                         public LexicalBlockEntry CurrentBlock {
249                                 get {
250                                         if ((_block_stack != null) && (_block_stack.Count > 0))
251                                                 return (LexicalBlockEntry) _block_stack.Peek ();
252                                         else
253                                                 return _implicit_block;
254                                 }
255                         }
256
257                         public LineNumberEntry[] Lines {
258                                 get {
259                                         return lines;
260                                 }
261                         }
262
263                         public LocalVariableEntry[] Locals {
264                                 get {
265                                         if (_locals == null)
266                                                 return new LocalVariableEntry [0];
267                                         else {
268                                                 LocalVariableEntry[] retval =
269                                                         new LocalVariableEntry [_locals.Count];
270                                                 _locals.CopyTo (retval, 0);
271                                                 return retval;
272                                         }
273                                 }
274                         }
275
276                         public void AddLocal (int index, string name, byte[] signature)
277                         {
278                                 if (_locals == null)
279                                         _locals = new ArrayList ();
280                                 _locals.Add (new LocalVariableEntry (
281                                                      index, name, signature, CurrentBlock.Index));
282                         }
283
284                         public ISourceFile SourceFile {
285                                 get { return _file; }
286                         }
287
288                         public ISourceMethod Method {
289                                 get { return _method; }
290                         }
291
292                         public LineNumberEntry Start {
293                                 get { return _start; }
294                         }
295
296                         public LineNumberEntry End {
297                                 get { return _end; }
298                         }
299
300                         //
301                         // Passes on the lines from the MonoSymbolWriter. This method is
302                         // free to mutate the lns array, and it does.
303                         //
304                         internal void SetLineNumbers (LineNumberEntry [] lns, int count)
305                         {
306                                 int pos = 0;
307
308                                 int last_offset = -1;
309                                 int last_row = -1;
310                                 for (int i = 0; i < count; i++) {
311                                         LineNumberEntry line = lns [i];
312
313                                         if (line.Offset > last_offset) {
314                                                 if (last_row >= 0)
315                                                         lns [pos++] = new LineNumberEntry (
316                                                                 last_row, last_offset);
317
318                                                 last_row = line.Row;
319                                                 last_offset = line.Offset;
320                                         } else if (line.Row > last_row) {
321                                                 last_row = line.Row;
322                                         }
323                                 }
324                         
325                                 lines = new LineNumberEntry [count + ((last_row >= 0) ? 1 : 0)];
326                                 Array.Copy (lns, lines, pos);
327                                 if (last_row >= 0)
328                                         lines [pos] = new LineNumberEntry (
329                                                 last_row, last_offset);
330                         }
331                 }
332         }
333 }