2002-05-22 Martin Baulig <martin@gnome.org>
[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
24         public class MonoSymbolWriter : IMonoSymbolWriter
25         {
26                 protected Assembly assembly;
27                 protected ModuleBuilder module_builder;
28                 protected string output_filename = null;
29                 protected ArrayList locals = null;
30                 protected ArrayList orphant_methods = null;
31                 protected ArrayList methods = null;
32                 protected Hashtable sources = null;
33
34                 protected class SourceFile : ISourceFile
35                 {
36                         private ArrayList _methods = new ArrayList ();
37                         private string _file_name;
38
39                         public SourceFile (string filename)
40                         {
41                                 this._file_name = filename;
42                         }
43
44                         public override string ToString ()
45                         {
46                                 return _file_name;
47                         }
48
49                         // interface ISourceFile
50
51                         public string FileName {
52                                 get {
53                                         return _file_name;
54                                 }
55                         }
56
57                         public ISourceMethod[] Methods {
58                                 get {
59                                         ISourceMethod[] retval = new ISourceMethod [_methods.Count];
60                                         _methods.CopyTo (retval);
61                                         return retval;
62                                 }
63                         }
64
65                         public void AddMethod (ISourceMethod method)
66                         {
67                                 _methods.Add (method);
68                         }
69                 }
70
71                 protected class SourceBlock : ISourceBlock
72                 {
73                         static private int next_index;
74                         private readonly int _index;
75
76                         public SourceBlock (ISourceMethod method, ISourceLine start, ISourceLine end)
77                         {
78                                 this._method = method;
79                                 this._start = start;
80                                 this._end = end;
81                                 this._index = ++next_index;
82                         }
83
84                         internal SourceBlock (ISourceMethod method, int startOffset)
85                         {
86                                 this._method = method;
87                                 this._start = new SourceLine (startOffset);
88                                 this._index = ++next_index;
89                         }
90
91                         public override string ToString ()
92                         {
93                                 return "SourceBlock #" + ID + " (" + Start + " - " + End + ")";
94                         }
95
96                         private readonly ISourceMethod _method;
97                         private ArrayList _blocks = new ArrayList ();
98                         internal ISourceLine _start;
99                         internal ISourceLine _end;
100
101                         private ArrayList _locals = new ArrayList ();
102
103                         public ISourceMethod SourceMethod {
104                                 get {
105                                         return _method;
106                                 }
107                         }
108
109                         public ISourceBlock[] Blocks {
110                                 get {
111                                         ISourceBlock[] retval = new ISourceBlock [_blocks.Count];
112                                         _blocks.CopyTo (retval);
113                                         return retval;
114                                 }
115                         }
116
117                         public void AddBlock (ISourceBlock block)
118                         {
119                                 _blocks.Add (block);
120                         }
121
122                         public ISourceLine Start {
123                                 get {
124                                         return _start;
125                                 }
126                         }
127
128                         public ISourceLine End {
129                                 get {
130                                         return _end;
131                                 }
132                         }
133
134                         public int ID {
135                                 get {
136                                         return _index;
137                                 }
138                         }
139
140                         public ILocalVariable[] Locals {
141                                 get {
142                                         ILocalVariable[] retval = new ILocalVariable [_locals.Count];
143                                         _locals.CopyTo (retval);
144                                         return retval;
145                                 }
146                         }
147
148                         public void AddLocal (ILocalVariable local)
149                         {
150                                 _locals.Add (local);
151                         }
152                 }
153
154                 protected class SourceLine : ISourceLine
155                 {
156                         public SourceLine (int row, int column)
157                                 : this (0, row, column)
158                         {
159                                 this._type = SourceOffsetType.OFFSET_NONE;
160                         }
161
162                         public SourceLine (int offset, int row, int column)
163                         {
164                                 this._offset = offset;
165                                 this._row = row;
166                                 this._column = column;
167                                 this._type = SourceOffsetType.OFFSET_IL;
168                         }
169
170                         internal SourceLine (int offset)
171                                 : this (offset, 0, 0)
172                         { }
173
174                         public override string ToString ()
175                         {
176                                 return "SourceLine (" + _offset + "@" + _row + ":" + _column + ")";
177                         }
178
179                         internal SourceOffsetType _type;
180                         internal int _offset;
181                         internal int _row;
182                         internal int _column;
183
184                         // interface ISourceLine
185
186                         public SourceOffsetType OffsetType {
187                                 get {
188                                         return _type;
189                                 }
190                         }
191
192                         public int Offset {
193                                 get {
194                                         return _offset;
195                                 }
196                         }
197
198                         public int Row {
199                                 get {
200                                         return _row;
201                                 }
202                         }
203
204                         public int Column {
205                                 get {
206                                         return _column;
207                                 }
208                         }
209                 }
210
211                 protected class LocalVariable : ILocalVariable
212                 {
213                         public LocalVariable (string name, byte[] signature, ISourceMethod method,
214                                               int index)
215                                 : this (name, signature, method, index, null)
216                         { }
217
218                         public LocalVariable (string name, byte[] signature, ISourceMethod method,
219                                               int index, ISourceLine line)
220                         {
221                                 this._name = name;
222                                 this._signature = signature;
223                                 this._method = method;
224                                 this._index = index;
225                                 this._line = line;
226                         }
227
228                         private readonly string _name;
229                         internal Type _type;
230                         private readonly byte[] _signature;
231                         private readonly ISourceMethod _method;
232                         private readonly int _index;
233                         private readonly ISourceLine _line;
234
235                         public override string ToString ()
236                         {
237                                 return "LocalVariable (" + _index + "," + _name + ")";
238                         }
239
240                         // interface ILocalVariable
241
242                         public string Name {
243                                 get {
244                                         return _name;
245                                 }
246                         }
247
248                         public ISourceMethod Method {
249                                 get {
250                                         return _method;
251                                 }
252                         }
253
254                         public int Index {
255                                 get {
256                                         return _index;
257                                 }
258                         }
259
260                         public Type Type {
261                                 get {
262                                         return _type;
263                                 }
264                         }
265
266                         public byte[] Signature {
267                                 get {
268                                         return _signature;
269                                 }
270                         }
271
272                         public ISourceLine Line {
273                                 get {
274                                         return _line;
275                                 }
276                         }
277                 }
278
279                 protected class SourceMethod : ISourceMethod
280                 {
281                         private ArrayList _lines = new ArrayList ();
282                         private ArrayList _blocks = new ArrayList ();
283                         private Hashtable _block_hash = new Hashtable ();
284                         private Stack _block_stack = new Stack ();
285
286                         internal readonly MethodBase _method_base;
287                         internal ISourceFile _source_file;
288                         internal int _token;
289
290                         private SourceBlock _implicit_block;
291
292                         public SourceMethod (MethodBase method_base, ISourceFile source_file)
293                                 : this (method_base)
294                         {
295                                 this._source_file = source_file;
296                         }
297
298                         internal SourceMethod (MethodBase method_base)
299                         {
300                                 this._method_base = method_base;
301
302                                 this._implicit_block = new SourceBlock (this, 0);
303                         }
304
305                         public void SetSourceRange (ISourceFile sourceFile,
306                                                     int startLine, int startColumn,
307                                                     int endLine, int endColumn)
308                         {
309                                 _source_file = sourceFile;
310                                 _implicit_block._start = new SourceLine (startLine, startColumn);
311                                 _implicit_block._end = new SourceLine (endLine, endColumn);
312                         }
313
314
315                         public void StartBlock (ISourceBlock block)
316                         {
317                                 _block_stack.Push (block);
318                         }
319
320                         public void EndBlock (int endOffset) {
321                                 SourceBlock block = (SourceBlock) _block_stack.Pop ();
322
323                                 block._end = new SourceLine (endOffset);
324
325                                 if (_block_stack.Count > 0) {
326                                         ISourceBlock parent = (ISourceBlock) _block_stack.Peek ();
327
328                                         parent.AddBlock (block);
329                                 } else
330                                         _blocks.Add (block);
331
332                                 _block_hash.Add (block.ID, block);
333                         }
334
335                         public void SetBlockRange (int BlockID, int startOffset, int endOffset)
336                         {
337                                 SourceBlock block = (SourceBlock) _block_hash [BlockID];
338                                 ((SourceLine) block.Start)._offset = startOffset;
339                                 ((SourceLine) block.End)._offset = endOffset;
340                         }
341
342                         public ISourceBlock CurrentBlock {
343                                 get {
344                                         if (_block_stack.Count > 0)
345                                                 return (ISourceBlock) _block_stack.Peek ();
346                                         else
347                                                 return _implicit_block;
348                                 }
349                         }
350
351                         // interface ISourceMethod
352
353                         public ISourceLine[] Lines {
354                                 get {
355                                         ISourceLine[] retval = new ISourceLine [_lines.Count];
356                                         _lines.CopyTo (retval);
357                                         return retval;
358                                 }
359                         }
360
361                         public void AddLine (ISourceLine line)
362                         {
363                                 _lines.Add (line);
364                         }
365
366                         public ISourceBlock[] Blocks {
367                                 get {
368                                         ISourceBlock[] retval = new ISourceBlock [_blocks.Count];
369                                         _blocks.CopyTo (retval);
370                                         return retval;
371                                 }
372                         }
373
374                         public ILocalVariable[] Locals {
375                                 get {
376                                         return _implicit_block.Locals;
377                                 }
378                         }
379
380                         public void AddLocal (ILocalVariable local)
381                         {
382                                 _implicit_block.AddLocal (local);
383                         }
384
385                         public MethodBase MethodBase {
386                                 get {
387                                         return _method_base;
388                                 }
389                         }
390
391                         public Type ReturnType {
392                                 get {
393                                         if (_method_base is MethodInfo)
394                                                 return ((MethodInfo)_method_base).ReturnType;
395                                         else if (_method_base is ConstructorInfo)
396                                                 return _method_base.DeclaringType;
397                                         else
398                                                 throw new NotSupportedException ();
399                                 }
400                         }
401
402                         public ISourceFile SourceFile {
403                                 get {
404                                         return _source_file;
405                                 }
406                         }
407
408                         public int Token {
409                                 get {
410                                         if (_token != 0)
411                                                 return _token;
412                                         else
413                                                 throw new NotSupportedException ();
414                                 }
415                         }
416
417                         public ISourceLine Start {
418                                 get {
419                                         return _implicit_block.Start;
420                                 }
421                         }
422
423                         public ISourceLine End {
424                                 get {
425                                         return _implicit_block.End;
426                                 }
427                         }
428                 }
429
430                 public ISourceMethod[] Methods {
431                         get {
432                                 ISourceMethod[] retval = new ISourceMethod [methods.Count];
433                                 methods.CopyTo (retval);
434                                 return retval;
435                         }
436                 }
437
438                 public ISourceFile[] Sources {
439                         get {
440                                 ISourceFile[] retval = new ISourceFile [sources.Count];
441                                 sources.Values.CopyTo (retval, 0);
442                                 return retval;
443                         }
444                 }
445
446                 protected SourceMethod current_method = null;
447                 private readonly string assembly_filename = null;
448
449                 //
450                 // Interface IMonoSymbolWriter
451                 //
452
453                 public MonoSymbolWriter (ModuleBuilder mb, string filename)
454                 {
455                         this.assembly_filename = filename;
456                         this.module_builder = mb;
457
458                         this.methods = new ArrayList ();
459                         this.sources = new Hashtable ();
460                         this.orphant_methods = new ArrayList ();
461                         this.locals = new ArrayList ();
462                 }
463
464                 public void Close () {
465                         if (assembly == null)
466                                 assembly = Assembly.LoadFrom (assembly_filename);
467
468                         DoFixups (assembly);
469
470                         CreateDwarfFile (output_filename);
471                 }
472
473                 public void CloseNamespace () {
474                 }
475
476                 // Create and return a new IMonoSymbolDocumentWriter.
477                 public ISymbolDocumentWriter DefineDocument (string url,
478                                                              Guid language,
479                                                              Guid languageVendor,
480                                                              Guid documentType)
481                 {
482                         return new MonoSymbolDocumentWriter (url);
483                 }
484
485                 public void DefineField (
486                         SymbolToken parent,
487                         string name,
488                         FieldAttributes attributes,
489                         byte[] signature,
490                         SymAddressKind addrKind,
491                         int addr1,
492                         int addr2,
493                         int addr3)
494                 {
495                 }
496
497                 public void DefineGlobalVariable (
498                         string name,
499                         FieldAttributes attributes,
500                         byte[] signature,
501                         SymAddressKind addrKind,
502                         int addr1,
503                         int addr2,
504                         int addr3)
505                 {
506                 }
507
508                 public void DefineLocalVariable (string name,
509                                                  FieldAttributes attributes,
510                                                  byte[] signature,
511                                                  SymAddressKind addrKind,
512                                                  int addr1,
513                                                  int addr2,
514                                                  int addr3,
515                                                  int startOffset,
516                                                  int endOffset)
517                 {
518                         if (current_method == null)
519                                 return;
520
521                         LocalVariable local_info = new LocalVariable (name, signature,
522                                                                       current_method, addr1);
523
524                         current_method.CurrentBlock.AddLocal (local_info);
525                         locals.Add (local_info);
526                 }
527
528                 public void DefineParameter (string name,
529                                              ParameterAttributes attributes,
530                                              int sequence,
531                                              SymAddressKind addrKind,
532                                              int addr1,
533                                              int addr2,
534                                              int addr3)
535                 {
536                         throw new NotSupportedException ();
537                 }
538
539                 public void DefineSequencePoints (ISymbolDocumentWriter document,
540                                                   int[] offsets,
541                                                   int[] lines,
542                                                   int[] columns,
543                                                   int[] endLines,
544                                                   int[] endColumns)
545                 {
546                         SourceLine source_line = new SourceLine (offsets [0], lines [0], columns [0]);
547
548                         if (current_method != null)
549                                 current_method.AddLine (source_line);
550                 }
551
552                 public void Initialize (IntPtr emitter, string filename, bool fFullBuild)
553                 {
554                         this.output_filename = filename;
555                 }
556
557                 public void OpenMethod (SymbolToken symbol_token)
558                 {
559                         int token = symbol_token.GetToken ();
560
561                         MethodBuilder mb = get_method (module_builder, token);
562
563                         current_method = new SourceMethod (mb);
564
565                         methods.Add (current_method);
566                 }
567
568                 public void SetMethodSourceRange (ISymbolDocumentWriter startDoc,
569                                                   int startLine, int startColumn,
570                                                   ISymbolDocumentWriter endDoc,
571                                                   int endLine, int endColumn)
572                 {
573                         if (current_method == null)
574                                 return;
575
576                         if ((startDoc == null) || (endDoc == null))
577                                 throw new NullReferenceException ();
578
579                         if (!(startDoc is MonoSymbolDocumentWriter) || !(endDoc is MonoSymbolDocumentWriter))
580                                 throw new NotSupportedException ("both startDoc and endDoc must be of type "
581                                                                  + "MonoSymbolDocumentWriter");
582
583                         if (!startDoc.Equals (endDoc))
584                                 throw new NotSupportedException ("startDoc and endDoc must be the same");
585
586                         string source_file = ((MonoSymbolDocumentWriter) startDoc).FileName;
587                         SourceFile source_info;
588
589                         if (sources.ContainsKey (source_file))
590                                 source_info = (SourceFile) sources [source_file];
591                         else {
592                                 source_info = new SourceFile (source_file);
593                                 sources.Add (source_file, source_info);
594                         }
595
596                         current_method.SetSourceRange (source_info, startLine, startColumn,
597                                                        endLine, endColumn);
598
599                         source_info.AddMethod (current_method);
600                 }
601
602                 public void CloseMethod () {
603                         current_method = null;
604                 }
605
606                 public void OpenNamespace (string name)
607                 {
608                 }
609
610                 public int OpenScope (int startOffset)
611                 {
612                         if (current_method == null)
613                                 return 0;
614
615                         ISourceBlock block = new SourceBlock (current_method, startOffset);
616                         current_method.StartBlock (block);
617
618                         return block.ID;
619                 }
620
621                 public void CloseScope (int endOffset) {
622                         if (current_method == null)
623                                 return;
624
625                         current_method.EndBlock (endOffset);
626                 }
627
628                 public void SetScopeRange (int scopeID, int startOffset, int endOffset)
629                 {
630                         if (current_method == null)
631                                 return;
632
633                         current_method.SetBlockRange (scopeID, startOffset, endOffset);
634                 }
635
636                 public void SetSymAttribute (SymbolToken parent, string name, byte[] data)
637                 {
638                 }
639
640                 public void SetUnderlyingWriter (IntPtr underlyingWriter)
641                 {
642                         throw new NotSupportedException ();
643                 }
644
645                 public void SetUserEntryPoint (SymbolToken entryMethod)
646                 {
647                 }
648
649                 public void UsingNamespace (string fullName)
650                 {
651                 }
652
653                 //
654                 // MonoSymbolWriter implementation
655                 //
656                 protected void WriteLocal (DwarfFileWriter.Die parent_die, ILocalVariable local)
657                 {
658                         DwarfFileWriter.DieMethodVariable die;
659
660                         die = new DwarfFileWriter.DieMethodVariable (parent_die, local);
661                 }
662
663                 protected void WriteBlock (DwarfFileWriter.Die parent_die, ISourceBlock block)
664                 {
665                         DwarfFileWriter.DieLexicalBlock die;
666
667                         die = new DwarfFileWriter.DieLexicalBlock (parent_die, block);
668
669                         foreach (ILocalVariable local in block.Locals)
670                                 WriteLocal (die, local);
671
672                         foreach (ISourceBlock subblock in block.Blocks)
673                                 WriteBlock (die, subblock);
674                 }
675
676                 protected void WriteMethod (DwarfFileWriter.DieCompileUnit parent_die, ISourceMethod method)
677                 {
678                         DwarfFileWriter.DieSubProgram die;
679
680                         die = new DwarfFileWriter.DieSubProgram (parent_die, method);
681
682                         foreach (ILocalVariable local in method.Locals)
683                                 WriteLocal (die, local);
684
685                         foreach (ISourceBlock block in method.Blocks)
686                                 WriteBlock (die, block);
687                 }
688
689                 protected void WriteSource (DwarfFileWriter writer, ISourceFile source)
690                 {
691                         DwarfFileWriter.CompileUnit compile_unit = new DwarfFileWriter.CompileUnit (
692                                 writer, source.FileName);
693
694                         DwarfFileWriter.DieCompileUnit die = new DwarfFileWriter.DieCompileUnit (compile_unit);
695
696                         foreach (ISourceMethod method in source.Methods)
697                                 WriteMethod (die, method);
698                 }
699
700
701                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
702                 internal extern static Type get_local_type_from_sig (Assembly assembly, byte[] sig);
703
704                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
705                 internal extern static MethodBuilder get_method (ModuleBuilder mb, int token);
706
707                 protected void DoFixups (Assembly assembly)
708                 {
709                         foreach (SourceMethod method in methods) {
710                                 if (method._method_base is MethodBuilder) {
711                                         MethodBuilder mb = (MethodBuilder) method._method_base;
712                                         method._token = mb.GetToken ().Token;
713                                 } else if (method._method_base is ConstructorBuilder) {
714                                         ConstructorBuilder cb = (ConstructorBuilder) method._method_base;
715                                         method._token = cb.GetToken ().Token;
716                                 } else
717                                         throw new NotSupportedException ();
718
719                                 if (method.SourceFile == null)
720                                         orphant_methods.Add (method);
721                         }
722
723                         foreach (LocalVariable local in locals) {
724                                 byte[] signature = local.Signature;
725
726                                 Type type = get_local_type_from_sig (assembly, signature);
727
728                                 ((LocalVariable) local)._type = type;
729                         }
730                 }
731
732                 protected void CreateDwarfFile (string filename)
733                 {
734                         DwarfFileWriter writer = new DwarfFileWriter (filename);
735
736                         foreach (ISourceFile source in sources.Values)
737                                 WriteSource (writer, source);
738
739                         if (orphant_methods.Count > 0) {
740                                 SourceFile source = new SourceFile ("<unknown>");
741
742                                 foreach (SourceMethod orphant in orphant_methods) {
743                                         orphant._source_file = source;
744                                         source.AddMethod (orphant);
745                                 }
746
747                                 WriteSource (writer, source);
748                         }
749
750                         writer.WriteSymbolTable (this);
751
752                         writer.Close ();
753                 }
754         }
755 }
756