Add licensing info
[mono.git] / mcs / class / Mono.CSharp.Debugger / MonoSymbolFile.cs
1 //
2 // System.Diagnostics.SymbolStore/MonoSymbolFile.cs
3 //
4 // Author:
5 //   Martin Baulig (martin@gnome.org)
6 //
7 // (C) 2003 Ximian, Inc.  http://www.ximian.com
8 //
9
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30
31 using System;
32 using System.Reflection;
33 using System.Reflection.Emit;
34 using System.Collections;
35 using System.Text;
36 using System.IO;
37         
38 namespace Mono.CSharp.Debugger
39 {
40         public class MonoSymbolFileException : Exception
41         {
42                 public MonoSymbolFileException ()
43                         : base ()
44                 { }
45
46                 public MonoSymbolFileException (string message, params object[] args)
47                         : base (String.Format (message, args))
48                 { }
49         }
50
51         internal class MyMemoryStream : Stream
52         {
53                 int length;
54                 int real_length;
55                 int position;
56
57                 int chunk_size = 4096;
58                 ArrayList chunks = new ArrayList ();
59
60                 private struct Chunk {
61                         public readonly int Offset;
62                         public readonly int Length;
63                         public byte[] Buffer;
64
65                         public Chunk (int offset, int length)
66                         {
67                                 this.Offset = offset;
68                                 this.Length = length;
69                                 this.Buffer = new Byte [length];
70                         }
71                 }
72
73                 public override long Position {
74                         get { return position; }
75
76                         set {
77                                 if (value > length)
78                                         throw new ArgumentOutOfRangeException ();
79
80                                 position = (int) value;
81                         }
82                 }
83
84                 public override long Length {
85                         get { return length; }
86                 }
87
88                 public override bool CanRead {
89                         get { return true; }
90                 }
91
92                 public override bool CanWrite {
93                         get { return true; }
94                 }
95
96                 public override bool CanSeek {
97                         get { return true; }
98                 }
99
100                 public override void SetLength (long new_length)
101                 {
102                         if (new_length < length)
103                                 throw new ArgumentException ();
104
105                         while (new_length >= real_length) {
106                                 Chunk new_chunk = new Chunk (real_length, chunk_size);
107                                 chunks.Add (new_chunk);
108                                 real_length += chunk_size;
109                         }
110
111                         length = (int) new_length;
112                 }
113
114                 public override void Flush ()
115                 { }
116
117                 public override long Seek (long offset, SeekOrigin origin)
118                 {
119                         int ref_point;
120
121                         switch (origin) {
122                         case SeekOrigin.Begin:
123                                 ref_point = 0;
124                                 break;
125                         case SeekOrigin.Current:
126                                 ref_point = position;
127                                 break;
128                         case SeekOrigin.End:
129                                 ref_point = length;
130                                 break;
131                         default:
132                                 throw new ArgumentException ("Invalid SeekOrigin");
133                         }
134
135                         if ((ref_point + offset < 0) || (offset > real_length))
136                                 throw new ArgumentOutOfRangeException ();
137
138                         position = ref_point + (int) offset;
139
140                         return position;
141                 }
142
143                 Chunk FindChunk (int offset)
144                 {
145                         return (Chunk) chunks [offset / chunk_size];
146                 }
147
148                 public override int Read (byte[] buffer, int offset, int count)
149                 {
150                         int old_count = count;
151
152                         while (count > 0) {
153                                 Chunk chunk = FindChunk (position);
154                                 int coffset = position - chunk.Offset;
155                                 int rest = chunk.Length - coffset;
156                                 int size = System.Math.Min (count, rest);
157
158                                 Array.Copy (chunk.Buffer, coffset, buffer, offset, size);
159                                 position += size;
160                                 offset += size;
161                                 count -= size;
162                         }
163
164                         return old_count;
165                 }
166
167                 public override void Write (byte[] buffer, int offset, int count)
168                 {
169                         if (position + count > length)
170                                 SetLength (position + count);
171
172                         while (count > 0) {
173                                 Chunk chunk = FindChunk (position);
174                                 int coffset = position - chunk.Offset;
175                                 int rest = chunk.Length - coffset;
176                                 int size = System.Math.Min (count, rest);
177
178                                 Array.Copy (buffer, offset, chunk.Buffer, coffset, size);
179                                 position += size;
180                                 offset += size;
181                                 count -= size;
182                         }
183                 }
184
185                 public byte[] GetContents ()
186                 {
187                         byte[] retval = new byte [length];
188                         position = 0;
189                         Read (retval, 0, length);
190                         return retval;
191                 }
192         }
193
194         public class MonoDebuggerSupport
195         {
196                 static GetTypeFunc get_type;
197                 static GetMethodTokenFunc get_method_token;
198                 static GetMethodFunc get_method;
199                 static GetLocalTypeFromSignatureFunc local_type_from_sig;
200
201                 delegate Type GetTypeFunc (Assembly assembly, int token);
202                 delegate int GetMethodTokenFunc (Assembly assembly, MethodBase method);
203                 delegate MethodBase GetMethodFunc (Assembly assembly, int token);
204                 delegate Type GetLocalTypeFromSignatureFunc (Assembly assembly, byte[] sig);
205
206                 static Delegate create_delegate (Type delegate_type, string name)
207                 {
208                         Type type = typeof (Assembly);
209
210                         MethodInfo mi = type.GetMethod (name, BindingFlags.Static |
211                                                         BindingFlags.NonPublic);
212                         if (mi == null)
213                                 throw new Exception ("Can't find " + name);
214
215                         return Delegate.CreateDelegate (delegate_type, mi);
216                 }
217
218                 static MonoDebuggerSupport ()
219                 {
220                         get_type = (GetTypeFunc) create_delegate (
221                                 typeof (GetTypeFunc), "MonoDebugger_GetType");
222
223                         get_method_token = (GetMethodTokenFunc) create_delegate (
224                                 typeof (GetMethodTokenFunc), "MonoDebugger_GetMethodToken");
225
226                         get_method = (GetMethodFunc) create_delegate (
227                                 typeof (GetMethodFunc), "MonoDebugger_GetMethod");
228
229                         local_type_from_sig = (GetLocalTypeFromSignatureFunc) create_delegate (
230                                 typeof (GetLocalTypeFromSignatureFunc),
231                                 "MonoDebugger_GetLocalTypeFromSignature");
232                 }
233
234                 public static Type GetType (Assembly assembly, int token)
235                 {
236                         return get_type (assembly, token);
237                 }
238
239                 public static int GetMethodToken (MethodBase method)
240                 {
241                         return get_method_token (method.ReflectedType.Assembly, method);
242                 }
243
244                 public static MethodBase GetMethod (Assembly assembly, int token)
245                 {
246                         return get_method (assembly, token);
247                 }
248
249                 public static Type GetLocalTypeFromSignature (Assembly assembly, byte[] sig)
250                 {
251                         return local_type_from_sig (assembly, sig);
252                 }
253         }
254
255         public class MonoSymbolFile : IDisposable
256         {
257                 ArrayList methods = new ArrayList ();
258                 ArrayList sources = new ArrayList ();
259                 Hashtable method_source_hash = new Hashtable ();
260                 Hashtable type_hash = new Hashtable ();
261
262                 OffsetTable ot;
263                 int last_type_index;
264                 int last_method_index;
265                 int last_source_index;
266                 int last_namespace_index;
267
268                 public MonoSymbolFile ()
269                 { }
270
271                 internal int AddSource (SourceFileEntry source)
272                 {
273                         sources.Add (source);
274                         return ++last_source_index;
275                 }
276
277                 internal int DefineType (Type type)
278                 {
279                         if (type_hash.Contains (type))
280                                 return (int) type_hash [type];
281
282                         int index = ++last_type_index;
283                         type_hash.Add (type, index);
284                         return index;
285                 }
286
287                 internal void AddMethod (MethodEntry entry)
288                 {
289                         methods.Add (entry);
290                 }
291
292                 internal int GetNextTypeIndex ()
293                 {
294                         return ++last_type_index;
295                 }
296
297                 internal int GetNextMethodIndex ()
298                 {
299                         return ++last_method_index;
300                 }
301
302                 internal int GetNextNamespaceIndex ()
303                 {
304                         return ++last_namespace_index;
305                 }
306
307                 byte [] stringBuffer;
308                 int maxCharsPerRound;
309                 static Encoding enc = Encoding.UTF8;
310                 
311                 internal void WriteString (BinaryWriter bw, string s)
312                 {
313                         int len = enc.GetByteCount (s);
314                         bw.Write (len);
315                         StringSize += len;
316                         
317                         if (stringBuffer == null) {
318                                 stringBuffer = new byte [512];
319                                 maxCharsPerRound = 512 / enc.GetMaxByteCount (1);
320                         }
321                         
322                         int chpos = 0;
323                         int chrem = s.Length;
324                         while (chrem > 0) {
325                                 int cch = (chrem > maxCharsPerRound) ? maxCharsPerRound : chrem;
326                                 int blen = enc.GetBytes (s, chpos, cch, stringBuffer, 0);
327                                 bw.Write (stringBuffer, 0, blen);
328                                 
329                                 chpos += cch;
330                                 chrem -= cch;
331                         }
332                 }
333
334                 internal string ReadString (int offset)
335                 {
336                         int old_pos = (int) reader.BaseStream.Position;
337                         reader.BaseStream.Position = offset;
338
339                         string text = ReadString ();
340
341                         reader.BaseStream.Position = old_pos;
342                         return text;
343                 }
344
345                 internal string ReadString ()
346                 {
347                         int length = reader.ReadInt32 ();
348                         byte[] data = reader.ReadBytes (length);
349                         return Encoding.UTF8.GetString (data);
350                 }
351
352                 void Write (BinaryWriter bw)
353                 {
354                         // Magic number and file version.
355                         bw.Write (OffsetTable.Magic);
356                         bw.Write (OffsetTable.Version);
357
358                         //
359                         // Offsets of file sections; we must write this after we're done
360                         // writing the whole file, so we just reserve the space for it here.
361                         //
362                         long offset_table_offset = bw.BaseStream.Position;
363                         ot.Write (bw);
364
365                         //
366                         // Write data sections.
367                         //
368                         ot.DataSectionOffset = (int) bw.BaseStream.Position;
369                         foreach (SourceFileEntry source in sources)
370                                 source.WriteData (bw);
371                         ot.DataSectionSize = (int) bw.BaseStream.Position - ot.DataSectionOffset;
372
373                         //
374                         // Write method table.
375                         //
376                         ot.MethodTableOffset = (int) bw.BaseStream.Position;
377                         for (int i = 0; i < methods.Count; i++) {
378                                 MethodEntry entry = (MethodEntry) methods [i];
379                                 entry.WriteIndex (bw);
380                         }
381                         ot.MethodTableSize = (int) bw.BaseStream.Position - ot.MethodTableOffset;
382
383                         //
384                         // Write source table.
385                         //
386                         ot.SourceTableOffset = (int) bw.BaseStream.Position;
387                         for (int i = 0; i < sources.Count; i++) {
388                                 SourceFileEntry source = (SourceFileEntry) sources [i];
389                                 source.Write (bw);
390                         }
391                         ot.SourceTableSize = (int) bw.BaseStream.Position - ot.SourceTableOffset;
392
393                         //
394                         // Fixup offset table.
395                         //
396                         ot.TypeCount = last_type_index;
397                         ot.MethodCount = methods.Count;
398                         ot.SourceCount = sources.Count;
399
400                         //
401                         // Write offset table.
402                         //
403                         ot.TotalFileSize = (int) bw.BaseStream.Position;
404                         bw.Seek ((int) offset_table_offset, SeekOrigin.Begin);
405                         ot.Write (bw);
406                         bw.Seek (0, SeekOrigin.End);
407                 }
408
409                 public byte[] CreateSymbolFile ()
410                 {
411                         if (reader != null)
412                                 throw new InvalidOperationException ();
413
414                         using (MyMemoryStream stream = new MyMemoryStream ()) {
415                                 Write (new BinaryWriter (stream));
416                                 return stream.GetContents ();
417                         }
418                 }
419
420                 Assembly assembly;
421                 BinaryReader reader;
422                 Hashtable method_hash;
423                 Hashtable source_file_hash;
424
425                 Hashtable method_token_hash;
426                 Hashtable source_name_hash;
427
428                 protected MonoSymbolFile (Assembly assembly, Stream stream)
429                 {
430                         this.assembly = assembly;
431
432                         reader = new BinaryReader (stream);
433
434                         try {
435                                 long magic = reader.ReadInt64 ();
436                                 long version = reader.ReadInt32 ();
437                                 if ((magic != OffsetTable.Magic) || (version != OffsetTable.Version))
438                                         throw new MonoSymbolFileException ();
439                                 ot = new OffsetTable (reader);
440                         } catch {
441                                 throw new MonoSymbolFileException ();
442                         }
443
444                         method_hash = new Hashtable ();
445                         source_file_hash = new Hashtable ();
446                 }
447
448                 public static MonoSymbolFile ReadSymbolFile (Assembly assembly)
449                 {
450                         Stream stream = assembly.GetManifestResourceStream ("MonoSymbolFile");
451                         if (stream == null)
452                                 return null;
453
454                         return new MonoSymbolFile (assembly, stream);
455                 }
456
457                 public Assembly Assembly {
458                         get { return assembly; }
459                 }
460
461                 public int SourceCount {
462                         get { return ot.SourceCount; }
463                 }
464
465                 public int MethodCount {
466                         get { return ot.MethodCount; }
467                 }
468
469                 public int TypeCount {
470                         get { return ot.TypeCount; }
471                 }
472
473                 public int NamespaceCount {
474                         get { return last_namespace_index; }
475                 }
476
477                 internal int LineNumberCount = 0;
478                 internal int LocalCount = 0;
479                 internal int StringSize = 0;
480
481                 public SourceFileEntry GetSourceFile (int index)
482                 {
483                         if ((index < 1) || (index > ot.SourceCount))
484                                 throw new ArgumentException ();
485                         if (reader == null)
486                                 throw new InvalidOperationException ();
487
488                         SourceFileEntry source = (SourceFileEntry) source_file_hash [index];
489                         if (source != null)
490                                 return source;
491
492                         reader.BaseStream.Position = ot.SourceTableOffset +
493                                 SourceFileEntry.Size * (index - 1);
494                         source = new SourceFileEntry (this, reader);
495                         source_file_hash.Add (index, source);
496                         return source;
497                 }
498
499                 public SourceFileEntry[] Sources {
500                         get {
501                                 if (reader == null)
502                                         throw new InvalidOperationException ();
503
504                                 SourceFileEntry[] retval = new SourceFileEntry [SourceCount];
505                                 for (int i = 0; i < SourceCount; i++)
506                                         retval [i] = GetSourceFile (i + 1);
507                                 return retval;
508                         }
509                 }
510
511                 public MethodIndexEntry GetMethodIndexEntry (int index)
512                 {
513                         int old_pos = (int) reader.BaseStream.Position;
514                         reader.BaseStream.Position = ot.MethodTableOffset +
515                                 MethodIndexEntry.Size * (index - 1);
516                         MethodIndexEntry ie = new MethodIndexEntry (reader);
517                         reader.BaseStream.Position = old_pos;
518                         return ie;
519                 }
520
521                 public MethodEntry GetMethodByToken (int token)
522                 {
523                         if (reader == null)
524                                 throw new InvalidOperationException ();
525
526                         if (method_token_hash == null) {
527                                 method_token_hash = new Hashtable ();
528
529                                 for (int i = 0; i < MethodCount; i++) {
530                                         MethodIndexEntry ie = GetMethodIndexEntry (i + 1);
531
532                                         method_token_hash.Add (ie.Token, i + 1);
533                                 }
534                         }
535
536                         object value = method_token_hash [token];
537                         if (value == null)
538                                 return null;
539
540                         return GetMethod ((int) value);
541                 }
542
543                 public MethodEntry GetMethod (MethodBase method)
544                 {
545                         if (reader == null)
546                                 throw new InvalidOperationException ();
547                         int token = MonoDebuggerSupport.GetMethodToken (method);
548                         return GetMethodByToken (token);
549                 }
550
551                 public MethodEntry GetMethod (int index)
552                 {
553                         if ((index < 1) || (index > ot.MethodCount))
554                                 throw new ArgumentException ();
555                         if (reader == null)
556                                 throw new InvalidOperationException ();
557
558                         MethodEntry entry = (MethodEntry) method_hash [index];
559                         if (entry != null)
560                                 return entry;
561
562                         MethodIndexEntry ie = GetMethodIndexEntry (index);
563                         reader.BaseStream.Position = ie.FileOffset;
564
565                         entry = new MethodEntry (this, reader, index);
566                         method_hash.Add (index, entry);
567                         return entry;
568                 }
569
570                 public MethodEntry[] Methods {
571                         get {
572                                 if (reader == null)
573                                         throw new InvalidOperationException ();
574
575                                 MethodEntry[] retval = new MethodEntry [MethodCount];
576                                 for (int i = 0; i < MethodCount; i++)
577                                         retval [i] = GetMethod (i + 1);
578                                 return retval;
579                         }
580                 }
581
582                 public MethodSourceEntry GetMethodSource (int index)
583                 {
584                         if ((index < 1) || (index > ot.MethodCount))
585                                 throw new ArgumentException ();
586                         if (reader == null)
587                                 throw new InvalidOperationException ();
588
589                         object entry = method_source_hash [index];
590                         if (entry != null)
591                                 return (MethodSourceEntry) entry;
592
593                         MethodEntry method = GetMethod (index);
594                         foreach (MethodSourceEntry source in method.SourceFile.Methods) {
595                                 if (source.Index == index) {
596                                         method_source_hash.Add (index, source);
597                                         return source;
598                                 }
599                         }
600
601                         throw new MonoSymbolFileException ("Internal error.");
602                 }
603
604                 public int FindSource (string file_name)
605                 {
606                         if (reader == null)
607                                 throw new InvalidOperationException ();
608
609                         if (source_name_hash == null) {
610                                 source_name_hash = new Hashtable ();
611
612                                 for (int i = 0; i < ot.SourceCount; i++) {
613                                         SourceFileEntry source = GetSourceFile (i + 1);
614
615                                         source_name_hash.Add (source.FileName, i);
616                                 }
617                         }
618
619                         object value = source_name_hash [file_name];
620                         if (value == null)
621                                 return -1;
622                         return (int) value;
623                 }
624
625                 internal BinaryReader BinaryReader {
626                         get {
627                                 if (reader == null)
628                                         throw new InvalidOperationException ();
629
630                                 return reader;
631                         }
632                 }
633
634                 void IDisposable.Dispose ()
635                 {
636                         Dispose (true);
637                 }
638
639                 protected virtual void Dispose (bool disposing)
640                 {
641                         if (disposing) {
642                                 if (reader != null) {
643                                         reader.Close ();
644                                         reader = null;
645                                 }
646                         }
647                 }
648         }
649 }