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