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