2003-02-07 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / class / Mono.CSharp.Debugger / MonoSymbolWriter.cs
1 //
2 // System.Diagnostics.SymbolStore/MonoSymbolWriter.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@gnome.org)
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 using System;
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Runtime.CompilerServices;
17 using System.Diagnostics.SymbolStore;
18 using System.Collections;
19 using System.IO;
20         
21 namespace Mono.CSharp.Debugger
22 {
23         internal class SourceFile
24         {
25                 private ArrayList _methods = new ArrayList ();
26                 private string _file_name;
27
28                 public SourceFile (string filename)
29                 {
30                         this._file_name = filename;
31                 }
32
33                 public override string ToString ()
34                 {
35                         return _file_name;
36                 }
37
38                 public string FileName {
39                         get {
40                                 return _file_name;
41                         }
42                 }
43
44                 public SourceMethod[] Methods {
45                         get {
46                                 SourceMethod[] retval = new SourceMethod [_methods.Count];
47                                 _methods.CopyTo (retval);
48                                 return retval;
49                         }
50                 }
51
52                 public void AddMethod (SourceMethod method)
53                 {
54                         _methods.Add (method);
55                 }
56         }
57
58         internal class SourceBlock
59         {
60                 static private int next_index;
61                 private readonly int _index;
62
63                 public SourceBlock (SourceMethod method, LineNumberEntry start, LineNumberEntry end)
64                 {
65                         this._method = method;
66                         this._start = start;
67                         this._end = end;
68                         this._index = ++next_index;
69                 }
70
71                 internal SourceBlock (SourceMethod method, int startOffset)
72                 {
73                         this._method = method;
74                         this._start_offset = startOffset;
75                         this._index = ++next_index;
76                 }
77
78                 public override string ToString ()
79                 {
80                         return "SourceBlock #" + ID + " (" + Start + " - " + End + ")";
81                 }
82
83                 private readonly SourceMethod _method;
84                 private ArrayList _blocks = new ArrayList ();
85                 internal LineNumberEntry _start = LineNumberEntry.Null;
86                 internal LineNumberEntry _end = LineNumberEntry.Null;
87                 internal int _start_offset, _end_offset;
88                 bool _has_source;
89
90                 internal void SetSourceRange (int startLine, int endLine)
91                 {
92                         _start = new LineNumberEntry (startLine, _start_offset);
93                         _end = new LineNumberEntry (endLine, _end_offset);
94                         _has_source = true;
95                 }
96
97                 private ArrayList _locals = new ArrayList ();
98
99                 public SourceMethod SourceMethod {
100                         get {
101                                 return _method;
102                         }
103                 }
104
105                 public SourceBlock[] Blocks {
106                         get {
107                                 SourceBlock[] retval = new SourceBlock [_blocks.Count];
108                                 _blocks.CopyTo (retval);
109                                 return retval;
110                         }
111                 }
112
113                 public void AddBlock (SourceBlock block)
114                 {
115                         _blocks.Add (block);
116                 }
117
118                 public bool HasSource {
119                         get {
120                                 return _has_source;
121                         }
122                 }
123
124                 public LineNumberEntry Start {
125                         get {
126                                 return _start;
127                         }
128                 }
129
130                 public LineNumberEntry End {
131                         get {
132                                 return _end;
133                         }
134                 }
135
136                 public int ID {
137                         get {
138                                 return _index;
139                         }
140                 }
141
142                 public LocalVariableEntry[] Locals {
143                         get {
144                                 LocalVariableEntry[] retval = new LocalVariableEntry [_locals.Count];
145                                 _locals.CopyTo (retval);
146                                 return retval;
147                         }
148                 }
149
150                 public void AddLocal (LocalVariableEntry local)
151                 {
152                         _locals.Add (local);
153                 }
154         }
155
156         internal class SourceMethod
157         {
158                 private ArrayList _lines = new ArrayList ();
159                 private ArrayList _blocks = new ArrayList ();
160                 private Hashtable _block_hash = new Hashtable ();
161                 private Stack _block_stack = new Stack ();
162
163                 internal readonly MethodBase _method_base;
164                 internal SourceFile _source_file;
165                 internal int _token;
166
167                 private SourceBlock _implicit_block;
168
169                 public SourceMethod (MethodBase method_base, SourceFile source_file)
170                         : this (method_base)
171                 {
172                         this._source_file = source_file;
173                 }
174
175                 internal SourceMethod (MethodBase method_base)
176                 {
177                         this._method_base = method_base;
178
179                         this._implicit_block = new SourceBlock (this, 0);
180                 }
181
182                 public void SetSourceRange (SourceFile sourceFile,
183                                             int startLine, int startColumn,
184                                             int endLine, int endColumn)
185                 {
186                         _source_file = sourceFile;
187                         _implicit_block.SetSourceRange (startLine, endLine);
188                 }
189
190                 public void StartBlock (SourceBlock block)
191                 {
192                         _block_stack.Push (block);
193                 }
194
195                 public void EndBlock (int endOffset) {
196                         SourceBlock block = (SourceBlock) _block_stack.Pop ();
197
198                         block._end_offset = endOffset;
199
200                         if (_block_stack.Count > 0) {
201                                 SourceBlock parent = (SourceBlock) _block_stack.Peek ();
202
203                                 parent.AddBlock (block);
204                         } else
205                                 _blocks.Add (block);
206
207                         _block_hash.Add (block.ID, block);
208                 }
209
210                 public void SetBlockRange (int BlockID, int startOffset, int endOffset)
211                 {
212                         SourceBlock block = (SourceBlock) _block_hash [BlockID];
213                         block._start_offset = startOffset;
214                         block._end_offset = endOffset;
215                 }
216
217                 public SourceBlock CurrentBlock {
218                         get {
219                                 if (_block_stack.Count > 0)
220                                         return (SourceBlock) _block_stack.Peek ();
221                                 else
222                                         return _implicit_block;
223                         }
224                 }
225
226                 public LineNumberEntry[] Lines {
227                         get {
228                                 LineNumberEntry[] retval = new LineNumberEntry [_lines.Count];
229                                 _lines.CopyTo (retval);
230                                 return retval;
231                         }
232                 }
233
234                 public void AddLine (LineNumberEntry line)
235                 {
236                         _lines.Add (line);
237                 }
238
239                 public SourceBlock[] Blocks {
240                         get {
241                                 SourceBlock[] retval = new SourceBlock [_blocks.Count];
242                                 _blocks.CopyTo (retval);
243                                 return retval;
244                         }
245                 }
246
247                 public LocalVariableEntry[] Locals {
248                         get {
249                                 return _implicit_block.Locals;
250                         }
251                 }
252
253                 public void AddLocal (LocalVariableEntry local)
254                 {
255                         _implicit_block.AddLocal (local);
256                 }
257
258                 public MethodBase MethodBase {
259                         get {
260                                 return _method_base;
261                         }
262                 }
263
264                 public string FullName {
265                         get {
266                                 return _method_base.DeclaringType.FullName + "." + _method_base.Name;
267                         }
268                 }
269
270                 public Type ReturnType {
271                         get {
272                                 if (_method_base is MethodInfo)
273                                         return ((MethodInfo)_method_base).ReturnType;
274                                 else if (_method_base is ConstructorInfo)
275                                         return _method_base.DeclaringType;
276                                 else
277                                         throw new NotSupportedException ();
278                         }
279                 }
280
281                 public ParameterInfo[] Parameters {
282                         get {
283                                 if (_method_base == null)
284                                         return new ParameterInfo [0];
285
286                                 ParameterInfo [] retval = _method_base.GetParameters ();
287                                 if (retval == null)
288                                         return new ParameterInfo [0];
289                                 else
290                                         return retval;
291                         }
292                 }
293
294                 public SourceFile SourceFile {
295                         get {
296                                 return _source_file;
297                         }
298                 }
299
300                 public int Token {
301                         get {
302                                 if (_token != 0)
303                                         return _token;
304                                 else
305                                         throw new NotSupportedException ();
306                         }
307                 }
308
309                 public bool HasSource {
310                         get {
311                                 return _implicit_block.HasSource && (_source_file != null);
312                         }
313                 }
314
315                 public LineNumberEntry Start {
316                         get {
317                                 return _implicit_block.Start;
318                         }
319                 }
320
321                 public LineNumberEntry End {
322                         get {
323                                 return _implicit_block.End;
324                         }
325                 }
326         }
327
328         public class MonoSymbolWriter : IMonoSymbolWriter
329         {
330                 protected ModuleBuilder module_builder;
331                 protected ArrayList locals = null;
332                 protected ArrayList orphant_methods = null;
333                 protected ArrayList methods = null;
334                 protected Hashtable sources = null;
335                 private ArrayList mbuilder_array = null;
336
337                 internal SourceMethod[] Methods {
338                         get {
339                                 SourceMethod[] retval = new SourceMethod [methods.Count];
340                                 methods.CopyTo (retval);
341                                 return retval;
342                         }
343                 }
344
345                 internal SourceFile[] Sources {
346                         get {
347                                 SourceFile[] retval = new SourceFile [sources.Count];
348                                 sources.Values.CopyTo (retval, 0);
349                                 return retval;
350                         }
351                 }
352
353                 private SourceMethod current_method = null;
354
355                 //
356                 // Interface IMonoSymbolWriter
357                 //
358
359                 public MonoSymbolWriter (ModuleBuilder mb, ArrayList mbuilder_array)
360                 {
361                         this.module_builder = mb;
362                         this.methods = new ArrayList ();
363                         this.sources = new Hashtable ();
364                         this.orphant_methods = new ArrayList ();
365                         this.locals = new ArrayList ();
366                         this.mbuilder_array = mbuilder_array;
367                 }
368
369                 public void Close ()
370                 {
371                         throw new InvalidOperationException ();
372                 }
373
374                 public byte[] CreateSymbolFile (AssemblyBuilder assembly_builder)
375                 {
376                         DoFixups (assembly_builder);
377
378                         return CreateOutput (assembly_builder);
379                 }
380
381                 public void CloseNamespace () {
382                 }
383
384                 // Create and return a new IMonoSymbolDocumentWriter.
385                 public ISymbolDocumentWriter DefineDocument (string url,
386                                                              Guid language,
387                                                              Guid languageVendor,
388                                                              Guid documentType)
389                 {
390                         return new MonoSymbolDocumentWriter (url);
391                 }
392
393                 public void DefineField (
394                         SymbolToken parent,
395                         string name,
396                         FieldAttributes attributes,
397                         byte[] signature,
398                         SymAddressKind addrKind,
399                         int addr1,
400                         int addr2,
401                         int addr3)
402                 {
403                         throw new NotSupportedException ();
404                 }
405
406                 public void DefineGlobalVariable (
407                         string name,
408                         FieldAttributes attributes,
409                         byte[] signature,
410                         SymAddressKind addrKind,
411                         int addr1,
412                         int addr2,
413                         int addr3)
414                 {
415                         throw new NotSupportedException ();
416                 }
417
418                 public void DefineLocalVariable (string name,
419                                                  FieldAttributes attributes,
420                                                  byte[] signature,
421                                                  SymAddressKind addrKind,
422                                                  int addr1,
423                                                  int addr2,
424                                                  int addr3,
425                                                  int startOffset,
426                                                  int endOffset)
427                 {
428                         if (current_method == null)
429                                 return;
430
431                         current_method.AddLocal (new LocalVariableEntry (name, attributes, signature));
432                 }
433
434                 public void DefineParameter (string name,
435                                              ParameterAttributes attributes,
436                                              int sequence,
437                                              SymAddressKind addrKind,
438                                              int addr1,
439                                              int addr2,
440                                              int addr3)
441                 {
442                         throw new NotSupportedException ();
443                 }
444
445                 public void DefineSequencePoints (ISymbolDocumentWriter document,
446                                                   int[] offsets,
447                                                   int[] lines,
448                                                   int[] columns,
449                                                   int[] endLines,
450                                                   int[] endColumns)
451                 {
452                         if (current_method == null)
453                                 return;
454
455                         LineNumberEntry source_line = new LineNumberEntry (lines [0], offsets [0]);
456
457                         if (current_method != null)
458                                 current_method.AddLine (source_line);
459                 }
460
461                 public void Initialize (IntPtr emitter, string filename, bool fFullBuild)
462                 {
463                         throw new NotSupportedException ();
464                 }
465
466                 public void OpenMethod (SymbolToken symbol_token)
467                 {
468                         int token = symbol_token.GetToken ();
469
470                         if ((token & 0xff000000) != 0x06000000)
471                                 throw new ArgumentException ();
472
473                         int index = (token & 0xffffff) - 1;
474
475                         MethodBase mb = (MethodBase) mbuilder_array [index];
476
477                         current_method = new SourceMethod (mb);
478
479                         methods.Add (current_method);
480                 }
481
482                 public void SetMethodSourceRange (ISymbolDocumentWriter startDoc,
483                                                   int startLine, int startColumn,
484                                                   ISymbolDocumentWriter endDoc,
485                                                   int endLine, int endColumn)
486                 {
487                         if (current_method == null)
488                                 return;
489
490                         if ((startDoc == null) || (endDoc == null))
491                                 throw new NullReferenceException ();
492
493                         if (!(startDoc is MonoSymbolDocumentWriter) || !(endDoc is MonoSymbolDocumentWriter))
494                                 throw new NotSupportedException ("both startDoc and endDoc must be of type "
495                                                                  + "MonoSymbolDocumentWriter");
496
497                         if (!startDoc.Equals (endDoc))
498                                 throw new NotSupportedException ("startDoc and endDoc must be the same");
499
500                         string source_file = ((MonoSymbolDocumentWriter) startDoc).FileName;
501                         SourceFile source_info;
502
503                         if (sources.ContainsKey (source_file))
504                                 source_info = (SourceFile) sources [source_file];
505                         else {
506                                 source_info = new SourceFile (source_file);
507                                 sources.Add (source_file, source_info);
508                         }
509
510                         current_method.SetSourceRange (source_info, startLine, startColumn,
511                                                        endLine, endColumn);
512
513                         source_info.AddMethod (current_method);
514                 }
515
516                 public void CloseMethod () {
517                         current_method = null;
518                 }
519
520                 public void OpenNamespace (string name)
521                 {
522                         throw new NotSupportedException ();
523                 }
524
525                 public int OpenScope (int startOffset)
526                 {
527                         if (current_method == null)
528                                 return 0;
529
530                         SourceBlock block = new SourceBlock (current_method, startOffset);
531                         current_method.StartBlock (block);
532
533                         return block.ID;
534                 }
535
536                 public void CloseScope (int endOffset) {
537                         if (current_method == null)
538                                 return;
539
540                         current_method.EndBlock (endOffset);
541                 }
542
543                 public void SetScopeRange (int scopeID, int startOffset, int endOffset)
544                 {
545                         if (current_method == null)
546                                 return;
547
548                         current_method.SetBlockRange (scopeID, startOffset, endOffset);
549                 }
550
551                 public void SetSymAttribute (SymbolToken parent, string name, byte[] data)
552                 {
553                         throw new NotSupportedException ();
554                 }
555
556                 public void SetUnderlyingWriter (IntPtr underlyingWriter)
557                 {
558                         throw new NotSupportedException ();
559                 }
560
561                 public void SetUserEntryPoint (SymbolToken entryMethod)
562                 {
563                         throw new NotSupportedException ();
564                 }
565
566                 public void UsingNamespace (string fullName)
567                 {
568                         throw new NotSupportedException ();
569                 }
570
571                 //
572                 // MonoSymbolWriter implementation
573                 //
574                 protected void DoFixups (Assembly assembly)
575                 {
576                         foreach (SourceMethod method in methods) {
577                                 if (method._method_base is MethodBuilder) {
578                                         MethodBuilder mb = (MethodBuilder) method._method_base;
579                                         method._token = mb.GetToken ().Token;
580                                 } else if (method._method_base is ConstructorBuilder) {
581                                         ConstructorBuilder cb = (ConstructorBuilder) method._method_base;
582                                         method._token = cb.GetToken ().Token;
583                                 } else
584                                         throw new NotSupportedException ();
585
586                                 if (method.SourceFile == null)
587                                         orphant_methods.Add (method);
588                         }
589                 }
590
591                 protected byte[] CreateOutput (Assembly assembly)
592                 {
593                         MonoSymbolFile file = new MonoSymbolFile ();
594
595                         foreach (SourceMethod method in Methods) {
596                                 if (!method.HasSource) {
597                                         Console.WriteLine ("INGORING METHOD: {0}", method);
598                                         continue;
599                                 }
600
601                                 SourceFileEntry source = file.DefineSource (method.SourceFile.FileName);
602
603                                 source.DefineMethod (method.MethodBase, method.Token, method.Locals,
604                                                      method.Lines, method.Start.Row, method.End.Row);
605                         }
606
607                         return file.CreateSymbolFile ();
608                 }
609         }
610 }
611