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