2008-06-10 Martin Baulig <martin@ximian.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 ICompileUnit
49         {
50                 CompileUnitEntry Entry {
51                         get;
52                 }
53         }
54
55         public interface ISourceMethod
56         {
57                 string Name {
58                         get;
59                 }
60
61                 int NamespaceID {
62                         get;
63                 }
64
65                 int Token {
66                         get;
67                 }
68         }
69
70         public class MonoSymbolWriter
71         {
72                 ArrayList methods = null;
73                 ArrayList sources = null;
74                 ArrayList comp_units = null;
75                 protected readonly MonoSymbolFile file;
76                 string filename = null;
77                 
78                 LineNumberEntry [] current_method_lines;
79                 int current_method_lines_pos = 0;
80
81                 private SourceMethod current_method = null;
82
83                 public MonoSymbolWriter (string filename)
84                 {
85                         this.methods = new ArrayList ();
86                         this.sources = new ArrayList ();
87                         this.comp_units = new ArrayList ();
88                         this.file = new MonoSymbolFile ();
89
90                         this.filename = filename + ".mdb";
91                         
92                         this.current_method_lines = new LineNumberEntry [50];
93                 }
94
95                 public MonoSymbolFile SymbolFile {
96                         get { return file; }
97                 }
98
99                 public void CloseNamespace ()
100                 { }
101
102                 public void DefineLocalVariable (int index, string name)
103                 {
104                         if (current_method == null)
105                                 return;
106
107                         current_method.AddLocal (index, name);
108                 }
109
110                 public void DefineCapturedLocal (int scope_id, string name, string captured_name)
111                 {
112                         file.DefineCapturedVariable (scope_id, name, captured_name,
113                                                      CapturedVariable.CapturedKind.Local);
114                 }
115
116                 public void DefineCapturedParameter (int scope_id, string name, string captured_name)
117                 {
118                         file.DefineCapturedVariable (scope_id, name, captured_name,
119                                                      CapturedVariable.CapturedKind.Parameter);
120                 }
121
122                 public void DefineCapturedThis (int scope_id, string captured_name)
123                 {
124                         file.DefineCapturedVariable (scope_id, "this", captured_name,
125                                                      CapturedVariable.CapturedKind.This);
126                 }
127
128                 public void DefineCapturedScope (int scope_id, int id, string captured_name)
129                 {
130                         file.DefineCapturedScope (scope_id, id, captured_name);
131                 }
132
133                 public void DefineScopeVariable (int scope, int index)
134                 {
135                         if (current_method == null)
136                                 return;
137
138                         current_method.AddScopeVariable (scope, index);
139                 }
140
141                 public void MarkSequencePoint (int offset, int file, int line, int column)
142                 {
143                         if (current_method == null)
144                                 throw new ArgumentNullException ();
145
146                         if (current_method_lines_pos == current_method_lines.Length) {
147                                 LineNumberEntry [] tmp = current_method_lines;
148                                 current_method_lines = new LineNumberEntry [current_method_lines.Length * 2];
149                                 Array.Copy (tmp, current_method_lines, current_method_lines_pos);
150                         }
151
152                         current_method_lines [current_method_lines_pos++] = new LineNumberEntry (file, line, offset);
153                 }
154
155                 public void MarkSequencePoint (int offset, int file, int line, int column, bool is_hidden)
156                 {
157                         if (current_method == null)
158                                 return;
159
160                         if (current_method_lines_pos == current_method_lines.Length) {
161                                 LineNumberEntry [] tmp = current_method_lines;
162                                 current_method_lines = new LineNumberEntry [current_method_lines.Length * 2];
163                                 Array.Copy (tmp, current_method_lines, current_method_lines_pos);
164                         }
165
166                         current_method_lines [current_method_lines_pos++] = new LineNumberEntry (
167                                 file, line, offset, is_hidden);
168                 }
169
170                 public void OpenMethod (ICompileUnit file, ISourceMethod method)
171                 {
172                         SourceMethod source = new SourceMethod (file, method);
173
174                         current_method = source;
175                         methods.Add (current_method);
176                 }
177
178                 public void SetRealMethodName (string name)
179                 {
180                         current_method.RealMethodName = name;
181                 }
182
183                 public void SetCompilerGenerated ()
184                 {
185                         current_method.SetCompilerGenerated ();
186                 }
187
188                 public void CloseMethod ()
189                 {
190                         if (current_method == null) {
191                                 Console.WriteLine ("Some clown is calling CloseMethod() while " +
192                                                    "current_method == null: {0}", Environment.StackTrace);
193                                 return;
194                         }
195                                                 
196                         current_method.SetLineNumbers (
197                                 current_method_lines, current_method_lines_pos);
198                         current_method_lines_pos = 0;
199                         
200                         current_method = null;
201                 }
202
203                 public SourceFileEntry DefineDocument (string url)
204                 {
205                         SourceFileEntry entry = new SourceFileEntry (file, url);
206                         sources.Add (entry);
207                         return entry;
208                 }
209
210                 public SourceFileEntry DefineDocument (string url, byte[] guid, byte[] checksum)
211                 {
212                         SourceFileEntry entry = new SourceFileEntry (file, url, guid, checksum);
213                         sources.Add (entry);
214                         return entry;
215                 }
216
217                 public CompileUnitEntry DefineCompilationUnit (SourceFileEntry source)
218                 {
219                         CompileUnitEntry entry = new CompileUnitEntry (file, source);
220                         comp_units.Add (entry);
221                         return entry;
222                 }
223
224                 public int DefineNamespace (string name, CompileUnitEntry unit,
225                                             string[] using_clauses, int parent)
226                 {
227                         if ((unit == null) || (using_clauses == null))
228                                 throw new NullReferenceException ();
229
230                         return unit.DefineNamespace (name, using_clauses, parent);
231                 }
232
233                 public int OpenScope (int start_offset)
234                 {
235                         if (current_method == null)
236                                 return 0;
237
238                         current_method.StartBlock (CodeBlockEntry.Type.Lexical, start_offset);
239                         return 0;
240                 }
241
242                 public void CloseScope (int end_offset)
243                 {
244                         if (current_method == null)
245                                 return;
246
247                         current_method.EndBlock (end_offset);
248                 }
249
250                 public void OpenCompilerGeneratedBlock (int start_offset)
251                 {
252                         if (current_method == null)
253                                 return;
254
255                         current_method.StartBlock (CodeBlockEntry.Type.CompilerGenerated,
256                                                    start_offset);
257                 }
258
259                 public void CloseCompilerGeneratedBlock (int end_offset)
260                 {
261                         if (current_method == null)
262                                 return;
263
264                         current_method.EndBlock (end_offset);
265                 }
266
267                 public void StartIteratorBody (int start_offset)
268                 {
269                         current_method.StartBlock (CodeBlockEntry.Type.IteratorBody,
270                                                    start_offset);
271                 }
272
273                 public void EndIteratorBody (int end_offset)
274                 {
275                         current_method.EndBlock (end_offset);
276                 }
277
278                 public void StartIteratorDispatcher (int start_offset)
279                 {
280                         current_method.StartBlock (CodeBlockEntry.Type.IteratorDispatcher,
281                                                    start_offset);
282                 }
283
284                 public void EndIteratorDispatcher (int end_offset)
285                 {
286                         current_method.EndBlock (end_offset);
287                 }
288
289                 public void DefineAnonymousScope (int id)
290                 {
291                         file.DefineAnonymousScope (id);
292                 }
293
294                 public void WriteSymbolFile (Guid guid)
295                 {
296                         foreach (SourceMethod method in methods)
297                                 method.DefineMethod (file);
298
299                         try {
300                                 // We mmap the file, so unlink the previous version since it may be in use
301                                 File.Delete (filename);
302                         } catch {
303                                 // We can safely ignore
304                         }
305                         using (FileStream fs = new FileStream (filename, FileMode.Create, FileAccess.Write)) {
306                                 file.CreateSymbolFile (guid, fs);
307                         }
308                 }
309
310                 protected class SourceMethod
311                 {
312                         LineNumberEntry [] lines;
313                         private ArrayList _locals;
314                         private ArrayList _blocks;
315                         private ArrayList _scope_vars;
316                         private Stack _block_stack;
317                         private string _real_name;
318                         private ISourceMethod _method;
319                         private ICompileUnit _comp_unit;
320                         private MethodEntry.Flags _method_flags;
321
322                         public SourceMethod (ICompileUnit comp_unit, ISourceMethod method)
323                         {
324                                 this._comp_unit = comp_unit;
325                                 this._method = method;
326                         }
327
328                         public void StartBlock (CodeBlockEntry.Type type, int start_offset)
329                         {
330                                 if (_block_stack == null)
331                                         _block_stack = new Stack ();
332                                 if (_blocks == null)
333                                         _blocks = new ArrayList ();
334
335                                 int parent = CurrentBlock != null ? CurrentBlock.Index : -1;
336
337                                 CodeBlockEntry block = new CodeBlockEntry (
338                                         _blocks.Count + 1, parent, type, start_offset);
339
340                                 _block_stack.Push (block);
341                                 _blocks.Add (block);
342                         }
343
344                         public void EndBlock (int end_offset)
345                         {
346                                 CodeBlockEntry block = (CodeBlockEntry) _block_stack.Pop ();
347                                 block.Close (end_offset);
348                         }
349
350                         public CodeBlockEntry[] Blocks {
351                                 get {
352                                         if (_blocks == null)
353                                                 return new CodeBlockEntry [0];
354
355                                         CodeBlockEntry[] retval = new CodeBlockEntry [_blocks.Count];
356                                         _blocks.CopyTo (retval, 0);
357                                         return retval;
358                                 }
359                         }
360
361                         public CodeBlockEntry CurrentBlock {
362                                 get {
363                                         if ((_block_stack != null) && (_block_stack.Count > 0))
364                                                 return (CodeBlockEntry) _block_stack.Peek ();
365                                         else
366                                                 return null;
367                                 }
368                         }
369
370                         public LineNumberEntry[] Lines {
371                                 get {
372                                         return lines;
373                                 }
374                         }
375
376                         public LocalVariableEntry[] Locals {
377                                 get {
378                                         if (_locals == null)
379                                                 return new LocalVariableEntry [0];
380                                         else {
381                                                 LocalVariableEntry[] retval =
382                                                         new LocalVariableEntry [_locals.Count];
383                                                 _locals.CopyTo (retval, 0);
384                                                 return retval;
385                                         }
386                                 }
387                         }
388
389                         public void AddLocal (int index, string name)
390                         {
391                                 if (_locals == null)
392                                         _locals = new ArrayList ();
393                                 int block_idx = CurrentBlock != null ? CurrentBlock.Index : 0;
394                                 _locals.Add (new LocalVariableEntry (index, name, block_idx));
395                         }
396
397                         public ScopeVariable[] ScopeVariables {
398                                 get {
399                                         if (_scope_vars == null)
400                                                 return new ScopeVariable [0];
401
402                                         ScopeVariable[] retval = new ScopeVariable [_scope_vars.Count];
403                                         _scope_vars.CopyTo (retval);
404                                         return retval;
405                                 }
406                         }
407
408                         public void AddScopeVariable (int scope, int index)
409                         {
410                                 if (_scope_vars == null)
411                                         _scope_vars = new ArrayList ();
412                                 _scope_vars.Add (
413                                         new ScopeVariable (scope, index));
414                         }
415
416                         public string RealMethodName {
417                                 get { return _real_name; }
418                                 set { _real_name = value; }
419                         }
420
421                         public void SetCompilerGenerated ()
422                         {
423                                 _method_flags |= MethodEntry.Flags.IsCompilerGenerated;
424                         }
425
426                         public ICompileUnit SourceFile {
427                                 get { return _comp_unit; }
428                         }
429
430                         public ISourceMethod Method {
431                                 get { return _method; }
432                         }
433
434                         internal void SetLineNumbers (LineNumberEntry [] lns, int count)
435                         {
436                                 lines = new LineNumberEntry [count];
437                                 Array.Copy (lns, lines, count);
438                         }
439
440                         public void DefineMethod (MonoSymbolFile file)
441                         {
442                                 MethodEntry entry = new MethodEntry (
443                                         file, _comp_unit.Entry, _method.Token, ScopeVariables,
444                                         Locals, Lines, Blocks, RealMethodName, _method_flags,
445                                         _method.NamespaceID);
446
447                                 file.AddMethod (entry);
448                         }
449                 }
450         }
451 }