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 string output_filename = null;
464                 protected ArrayList locals = null;
465                 protected ArrayList orphant_methods = null;
466                 protected ArrayList methods = null;
467                 protected Hashtable sources = null;
468                 protected DwarfFileWriter writer = null;
469                 private ArrayList mbuilder_array = null;
470
471                 public ISourceMethod[] Methods {
472                         get {
473                                 ISourceMethod[] retval = new ISourceMethod [methods.Count];
474                                 methods.CopyTo (retval);
475                                 return retval;
476                         }
477                 }
478
479                 public ISourceFile[] Sources {
480                         get {
481                                 ISourceFile[] retval = new ISourceFile [sources.Count];
482                                 sources.Values.CopyTo (retval, 0);
483                                 return retval;
484                         }
485                 }
486
487                 public DwarfFileWriter DwarfFileWriter {
488                         get {
489                                 return writer;
490                         }
491                 }
492
493                 protected SourceMethod current_method = null;
494                 private readonly string assembly_filename = null;
495
496                 //
497                 // Interface IMonoSymbolWriter
498                 //
499
500                 public MonoSymbolWriter (ModuleBuilder mb, string filename, ArrayList mbuilder_array)
501                 {
502                         this.assembly_filename = filename;
503                         this.module_builder = mb;
504                         this.methods = new ArrayList ();
505                         this.sources = new Hashtable ();
506                         this.orphant_methods = new ArrayList ();
507                         this.locals = new ArrayList ();
508                         this.mbuilder_array = mbuilder_array;
509                 }
510
511                 public void Close () {
512                         if (assembly == null)
513                                 assembly = Assembly.LoadFrom (assembly_filename);
514
515                         DoFixups (assembly);
516
517                         CreateDwarfFile (assembly);
518                 }
519
520                 public void CloseNamespace () {
521                 }
522
523                 // Create and return a new IMonoSymbolDocumentWriter.
524                 public ISymbolDocumentWriter DefineDocument (string url,
525                                                              Guid language,
526                                                              Guid languageVendor,
527                                                              Guid documentType)
528                 {
529                         return new MonoSymbolDocumentWriter (url);
530                 }
531
532                 public void DefineField (
533                         SymbolToken parent,
534                         string name,
535                         FieldAttributes attributes,
536                         byte[] signature,
537                         SymAddressKind addrKind,
538                         int addr1,
539                         int addr2,
540                         int addr3)
541                 {
542                 }
543
544                 public void DefineGlobalVariable (
545                         string name,
546                         FieldAttributes attributes,
547                         byte[] signature,
548                         SymAddressKind addrKind,
549                         int addr1,
550                         int addr2,
551                         int addr3)
552                 {
553                 }
554
555                 public void DefineLocalVariable (string name,
556                                                  FieldAttributes attributes,
557                                                  byte[] signature,
558                                                  SymAddressKind addrKind,
559                                                  int addr1,
560                                                  int addr2,
561                                                  int addr3,
562                                                  int startOffset,
563                                                  int endOffset)
564                 {
565                         throw new NotSupportedException ();
566                 }
567
568                 public void DefineLocalVariable (string name,
569                                                  LocalBuilder local,
570                                                  FieldAttributes attributes,
571                                                  int position,
572                                                  int startOffset,
573                                                  int endOffset)
574                 {
575                         if (current_method == null)
576                                 return;
577
578                         ITypeHandle type = writer.RegisterType (local.LocalType);
579
580                         LocalVariable local_info = new LocalVariable (name, type, current_method,
581                                                                       position, null);
582
583                         current_method.CurrentBlock.AddLocal (local_info);
584                         locals.Add (local_info);
585                 }
586
587
588                 public void DefineParameter (string name,
589                                              ParameterAttributes attributes,
590                                              int sequence,
591                                              SymAddressKind addrKind,
592                                              int addr1,
593                                              int addr2,
594                                              int addr3)
595                 {
596                         throw new NotSupportedException ();
597                 }
598
599                 public void DefineSequencePoints (ISymbolDocumentWriter document,
600                                                   int[] offsets,
601                                                   int[] lines,
602                                                   int[] columns,
603                                                   int[] endLines,
604                                                   int[] endColumns)
605                 {
606                         SourceLine source_line = new SourceLine (offsets [0], lines [0], columns [0]);
607
608                         if (current_method != null)
609                                 current_method.AddLine (source_line);
610                 }
611
612                 public void Initialize (IntPtr emitter, string filename, bool fFullBuild)
613                 {
614                         this.output_filename = filename;
615
616                         this.writer = new DwarfFileWriter (filename);
617                 }
618
619                 public void OpenMethod (SymbolToken symbol_token)
620                 {
621                         int token = symbol_token.GetToken ();
622
623                         if ((token & 0xff000000) != 0x06000000)
624                                 throw new ArgumentException ();
625
626                         int index = (token & 0xffffff) - 1;
627
628                         MethodBuilder mb = (MethodBuilder) mbuilder_array [index];
629
630                         current_method = new SourceMethod (mb);
631
632                         methods.Add (current_method);
633                 }
634
635                 public void SetMethodSourceRange (ISymbolDocumentWriter startDoc,
636                                                   int startLine, int startColumn,
637                                                   ISymbolDocumentWriter endDoc,
638                                                   int endLine, int endColumn)
639                 {
640                         if (current_method == null)
641                                 return;
642
643                         if ((startDoc == null) || (endDoc == null))
644                                 throw new NullReferenceException ();
645
646                         if (!(startDoc is MonoSymbolDocumentWriter) || !(endDoc is MonoSymbolDocumentWriter))
647                                 throw new NotSupportedException ("both startDoc and endDoc must be of type "
648                                                                  + "MonoSymbolDocumentWriter");
649
650                         if (!startDoc.Equals (endDoc))
651                                 throw new NotSupportedException ("startDoc and endDoc must be the same");
652
653                         string source_file = ((MonoSymbolDocumentWriter) startDoc).FileName;
654                         SourceFile source_info;
655
656                         if (sources.ContainsKey (source_file))
657                                 source_info = (SourceFile) sources [source_file];
658                         else {
659                                 source_info = new SourceFile (source_file);
660                                 sources.Add (source_file, source_info);
661                         }
662
663                         current_method.SetSourceRange (source_info, startLine, startColumn,
664                                                        endLine, endColumn);
665
666                         source_info.AddMethod (current_method);
667                 }
668
669                 public void CloseMethod () {
670                         current_method = null;
671                 }
672
673                 public void OpenNamespace (string name)
674                 {
675                 }
676
677                 public int OpenScope (int startOffset)
678                 {
679                         if (current_method == null)
680                                 return 0;
681
682                         ISourceBlock block = new SourceBlock (current_method, startOffset);
683                         current_method.StartBlock (block);
684
685                         return block.ID;
686                 }
687
688                 public void CloseScope (int endOffset) {
689                         if (current_method == null)
690                                 return;
691
692                         current_method.EndBlock (endOffset);
693                 }
694
695                 public void SetScopeRange (int scopeID, int startOffset, int endOffset)
696                 {
697                         if (current_method == null)
698                                 return;
699
700                         current_method.SetBlockRange (scopeID, startOffset, endOffset);
701                 }
702
703                 public void SetSymAttribute (SymbolToken parent, string name, byte[] data)
704                 {
705                 }
706
707                 public void SetUnderlyingWriter (IntPtr underlyingWriter)
708                 {
709                         throw new NotSupportedException ();
710                 }
711
712                 public void SetUserEntryPoint (SymbolToken entryMethod)
713                 {
714                 }
715
716                 public void UsingNamespace (string fullName)
717                 {
718                 }
719
720                 //
721                 // MonoSymbolWriter implementation
722                 //
723                 protected void WriteLocal (DwarfFileWriter.Die parent_die, ILocalVariable local)
724                 {
725                         DwarfFileWriter.DieMethodVariable die;
726
727                         die = new DwarfFileWriter.DieMethodVariable (parent_die, local);
728                 }
729
730                 protected void WriteBlock (DwarfFileWriter.Die parent_die, ISourceBlock block)
731                 {
732                         DwarfFileWriter.DieLexicalBlock die;
733
734                         die = new DwarfFileWriter.DieLexicalBlock (parent_die, block);
735
736                         foreach (ILocalVariable local in block.Locals)
737                                 WriteLocal (die, local);
738
739                         foreach (ISourceBlock subblock in block.Blocks)
740                                 WriteBlock (die, subblock);
741                 }
742
743                 protected void WriteMethod (ISourceMethod method)
744                 {
745                         DwarfFileWriter.DieCompileUnit parent_die = writer.DieGlobalCompileUnit;
746                         DwarfFileWriter.DieSubProgram die;
747
748                         die = new DwarfFileWriter.DieSubProgram (parent_die, method);
749
750                         foreach (ILocalVariable local in method.Locals)
751                                 WriteLocal (die, local);
752
753                         foreach (ISourceBlock block in method.Blocks)
754                                 WriteBlock (die, block);
755                 }
756
757                 protected void WriteSource (DwarfFileWriter writer, ISourceFile source)
758                 {
759                         foreach (ISourceMethod method in source.Methods)
760                                 WriteMethod (method);
761                 }
762
763                 protected void DoFixups (Assembly assembly)
764                 {
765                         foreach (SourceMethod method in methods) {
766                                 if (method._method_base is MethodBuilder) {
767                                         MethodBuilder mb = (MethodBuilder) method._method_base;
768                                         method._token = mb.GetToken ().Token;
769                                 } else if (method._method_base is ConstructorBuilder) {
770                                         ConstructorBuilder cb = (ConstructorBuilder) method._method_base;
771                                         method._token = cb.GetToken ().Token;
772                                 } else
773                                         throw new NotSupportedException ();
774
775                                 if (method.SourceFile == null)
776                                         orphant_methods.Add (method);
777                         }
778                 }
779
780                 protected void CreateDwarfFile (Assembly assembly)
781                 {
782                         foreach (ISourceFile source in sources.Values)
783                                 WriteSource (writer, source);
784
785                         if (orphant_methods.Count > 0) {
786                                 SourceFile source = new SourceFile ("<unknown>");
787
788                                 foreach (SourceMethod orphant in orphant_methods) {
789                                         orphant._source_file = source;
790                                         source.AddMethod (orphant);
791                                 }
792
793                                 WriteSource (writer, source);
794                         }
795
796                         writer.WriteSymbolTable (this);
797
798                         writer.Close ();
799                 }
800         }
801 }
802