do not check order sequence if option /order was not used
[mono.git] / mcs / class / IKVM.Reflection / Reader / ModuleReader.cs
1 /*
2   Copyright (C) 2009-2012 Jeroen Frijters
3
4   This software is provided 'as-is', without any express or implied
5   warranty.  In no event will the authors be held liable for any damages
6   arising from the use of this software.
7
8   Permission is granted to anyone to use this software for any purpose,
9   including commercial applications, and to alter it and redistribute it
10   freely, subject to the following restrictions:
11
12   1. The origin of this software must not be misrepresented; you must not
13      claim that you wrote the original software. If you use this software
14      in a product, an acknowledgment in the product documentation would be
15      appreciated but is not required.
16   2. Altered source versions must be plainly marked as such, and must not be
17      misrepresented as being the original software.
18   3. This notice may not be removed or altered from any source distribution.
19
20   Jeroen Frijters
21   jeroen@frijters.net
22   
23 */
24 using System;
25 using System.IO;
26 using System.Text;
27 using System.Collections.Generic;
28 using IKVM.Reflection.Metadata;
29
30 namespace IKVM.Reflection.Reader
31 {
32         sealed class StreamHeader
33         {
34                 internal uint Offset;
35                 internal uint Size;
36                 internal string Name;
37
38                 internal void Read(BinaryReader br)
39                 {
40                         Offset = br.ReadUInt32();
41                         Size = br.ReadUInt32();
42                         byte[] buf = new byte[32];
43                         byte b;
44                         int len = 0;
45                         while ((b = br.ReadByte()) != 0)
46                         {
47                                 buf[len++] = b;
48                         }
49                         Name = Encoding.UTF8.GetString(buf, 0, len); ;
50                         int padding = -1 + ((len + 4) & ~3) - len;
51                         br.BaseStream.Seek(padding, SeekOrigin.Current);
52                 }
53         }
54
55         sealed class ModuleReader : Module
56         {
57                 internal readonly Stream stream;
58                 private readonly string location;
59                 private Assembly assembly;
60                 private readonly PEReader peFile = new PEReader();
61                 private readonly CliHeader cliHeader = new CliHeader();
62                 private string imageRuntimeVersion;
63                 private int metadataStreamVersion;
64                 private byte[] stringHeap;
65                 private byte[] blobHeap;
66                 private byte[] userStringHeap;
67                 private byte[] guidHeap;
68                 private TypeDefImpl[] typeDefs;
69                 private TypeDefImpl moduleType;
70                 private Assembly[] assemblyRefs;
71                 private Type[] typeRefs;
72                 private Type[] typeSpecs;
73                 private FieldInfo[] fields;
74                 private MethodBase[] methods;
75                 private MemberInfo[] memberRefs;
76                 private Dictionary<int, string> strings = new Dictionary<int, string>();
77                 private Dictionary<TypeName, Type> types = new Dictionary<TypeName, Type>();
78                 private Dictionary<TypeName, LazyForwardedType> forwardedTypes = new Dictionary<TypeName, LazyForwardedType>();
79
80                 private sealed class LazyForwardedType
81                 {
82                         private readonly int assemblyRef;
83                         private Type type;
84
85                         internal LazyForwardedType(int assemblyRef)
86                         {
87                                 this.assemblyRef = assemblyRef;
88                         }
89
90                         internal Type GetType(ModuleReader module, TypeName typeName)
91                         {
92                                 if (type == null)
93                                 {
94                                         Assembly asm = module.ResolveAssemblyRef(assemblyRef);
95                                         type = asm.ResolveType(typeName);
96                                         if (type == null)
97                                         {
98                                                 throw new TypeLoadException(typeName.ToString());
99                                         }
100                                 }
101                                 return type;
102                         }
103                 }
104
105                 internal ModuleReader(AssemblyReader assembly, Universe universe, Stream stream, string location)
106                         : base(universe)
107                 {
108                         this.stream = stream;
109                         this.location = location;
110                         Read();
111                         if (assembly == null && AssemblyTable.records.Length != 0)
112                         {
113                                 assembly = new AssemblyReader(location, this);
114                         }
115                         this.assembly = assembly;
116                 }
117
118                 private void Read()
119                 {
120                         BinaryReader br = new BinaryReader(stream);
121                         peFile.Read(br);
122                         stream.Seek(peFile.RvaToFileOffset(peFile.GetComDescriptorVirtualAddress()), SeekOrigin.Begin);
123                         cliHeader.Read(br);
124                         stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress), SeekOrigin.Begin);
125                         foreach (StreamHeader sh in ReadStreamHeaders(br, out imageRuntimeVersion))
126                         {
127                                 switch (sh.Name)
128                                 {
129                                         case "#Strings":
130                                                 stringHeap = ReadHeap(stream, sh);
131                                                 break;
132                                         case "#Blob":
133                                                 blobHeap = ReadHeap(stream, sh);
134                                                 break;
135                                         case "#US":
136                                                 userStringHeap = ReadHeap(stream, sh);
137                                                 break;
138                                         case "#GUID":
139                                                 guidHeap = ReadHeap(stream, sh);
140                                                 break;
141                                         case "#~":
142                                         case "#-":
143                                                 stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + sh.Offset), SeekOrigin.Begin);
144                                                 ReadTables(br);
145                                                 break;
146                                         default:
147                                                 // we ignore unknown streams, because the CLR does so too
148                                                 // (and some obfuscators add bogus streams)
149                                                 break;
150                                 }
151                         }
152                 }
153
154                 internal void SetAssembly(Assembly assembly)
155                 {
156                         this.assembly = assembly;
157                 }
158
159                 private static StreamHeader[] ReadStreamHeaders(BinaryReader br, out string Version)
160                 {
161                         uint Signature = br.ReadUInt32();
162                         if (Signature != 0x424A5342)
163                         {
164                                 throw new BadImageFormatException("Invalid metadata signature");
165                         }
166                         /*ushort MajorVersion =*/ br.ReadUInt16();
167                         /*ushort MinorVersion =*/ br.ReadUInt16();
168                         /*uint Reserved =*/ br.ReadUInt32();
169                         uint Length = br.ReadUInt32();
170                         byte[] buf = br.ReadBytes((int)Length);
171                         Version = Encoding.UTF8.GetString(buf).TrimEnd('\u0000');
172                         /*ushort Flags =*/ br.ReadUInt16();
173                         ushort Streams = br.ReadUInt16();
174                         StreamHeader[] streamHeaders = new StreamHeader[Streams];
175                         for (int i = 0; i < streamHeaders.Length; i++)
176                         {
177                                 streamHeaders[i] = new StreamHeader();
178                                 streamHeaders[i].Read(br);
179                         }
180                         return streamHeaders;
181                 }
182
183                 private void ReadTables(BinaryReader br)
184                 {
185                         Table[] tables = GetTables();
186                         /*uint Reserved0 =*/ br.ReadUInt32();
187                         byte MajorVersion = br.ReadByte();
188                         byte MinorVersion = br.ReadByte();
189                         metadataStreamVersion = MajorVersion << 16 | MinorVersion;
190                         byte HeapSizes = br.ReadByte();
191                         /*byte Reserved7 =*/ br.ReadByte();
192                         ulong Valid = br.ReadUInt64();
193                         ulong Sorted = br.ReadUInt64();
194                         for (int i = 0; i < 64; i++)
195                         {
196                                 if ((Valid & (1UL << i)) != 0)
197                                 {
198                                         tables[i].Sorted = (Sorted & (1UL << i)) != 0;
199                                         tables[i].RowCount = br.ReadInt32();
200                                 }
201                                 else if (tables[i] != null)
202                                 {
203                                         tables[i].RowCount = 0;
204                                 }
205                         }
206                         MetadataReader mr = new MetadataReader(this, br.BaseStream, HeapSizes);
207                         for (int i = 0; i < 64; i++)
208                         {
209                                 if ((Valid & (1UL << i)) != 0)
210                                 {
211                                         tables[i].Read(mr);
212                                 }
213                         }
214                         if (ParamPtr.RowCount != 0)
215                         {
216                                 throw new NotImplementedException("ParamPtr table support has not yet been implemented.");
217                         }
218                 }
219
220                 private byte[] ReadHeap(Stream stream, StreamHeader sh)
221                 {
222                         byte[] buf = new byte[sh.Size];
223                         stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaData.VirtualAddress + sh.Offset), SeekOrigin.Begin);
224                         for (int pos = 0; pos < buf.Length; )
225                         {
226                                 int read = stream.Read(buf, pos, buf.Length - pos);
227                                 if (read == 0)
228                                 {
229                                         throw new BadImageFormatException();
230                                 }
231                                 pos += read;
232                         }
233                         return buf;
234                 }
235
236                 internal void SeekRVA(int rva)
237                 {
238                         stream.Seek(peFile.RvaToFileOffset((uint)rva), SeekOrigin.Begin);
239                 }
240
241                 internal override void GetTypesImpl(List<Type> list)
242                 {
243                         PopulateTypeDef();
244                         foreach (TypeDefImpl type in typeDefs)
245                         {
246                                 if (type != moduleType)
247                                 {
248                                         list.Add(type);
249                                 }
250                         }
251                 }
252
253                 private void PopulateTypeDef()
254                 {
255                         if (typeDefs == null)
256                         {
257                                 typeDefs = new TypeDefImpl[TypeDef.records.Length];
258                                 for (int i = 0; i < typeDefs.Length; i++)
259                                 {
260                                         TypeDefImpl type = new TypeDefImpl(this, i);
261                                         typeDefs[i] = type;
262                                         if (type.IsModulePseudoType)
263                                         {
264                                                 moduleType = type;
265                                         }
266                                         else if (!type.IsNestedByFlags)
267                                         {
268                                                 types.Add(new TypeName(type.__Namespace, type.__Name), type);
269                                         }
270                                 }
271                                 // add forwarded types to forwardedTypes dictionary (because Module.GetType(string) should return them)
272                                 for (int i = 0; i < ExportedType.records.Length; i++)
273                                 {
274                                         int implementation = ExportedType.records[i].Implementation;
275                                         if (implementation >> 24 == AssemblyRefTable.Index)
276                                         {
277                                                 TypeName typeName = GetTypeName(ExportedType.records[i].TypeNamespace, ExportedType.records[i].TypeName);
278                                                 forwardedTypes.Add(typeName, new LazyForwardedType((implementation & 0xFFFFFF) - 1));
279                                         }
280                                 }
281                         }
282                 }
283
284                 internal override string GetString(int index)
285                 {
286                         if (index == 0)
287                         {
288                                 return null;
289                         }
290                         string str;
291                         if (!strings.TryGetValue(index, out str))
292                         {
293                                 int len = 0;
294                                 while (stringHeap[index + len] != 0)
295                                 {
296                                         len++;
297                                 }
298                                 str = Encoding.UTF8.GetString(stringHeap, index, len);
299                                 strings.Add(index, str);
300                         }
301                         return str;
302                 }
303
304                 private static int ReadCompressedInt(byte[] buffer, ref int offset)
305                 {
306                         byte b1 = buffer[offset++];
307                         if (b1 <= 0x7F)
308                         {
309                                 return b1;
310                         }
311                         else if ((b1 & 0xC0) == 0x80)
312                         {
313                                 byte b2 = buffer[offset++];
314                                 return ((b1 & 0x3F) << 8) | b2;
315                         }
316                         else
317                         {
318                                 byte b2 = buffer[offset++];
319                                 byte b3 = buffer[offset++];
320                                 byte b4 = buffer[offset++];
321                                 return ((b1 & 0x3F) << 24) + (b2 << 16) + (b3 << 8) + b4;
322                         }
323                 }
324
325                 internal byte[] GetBlobCopy(int blobIndex)
326                 {
327                         int len = ReadCompressedInt(blobHeap, ref blobIndex);
328                         byte[] buf = new byte[len];
329                         Buffer.BlockCopy(blobHeap, blobIndex, buf, 0, len);
330                         return buf;
331                 }
332
333                 internal override ByteReader GetBlob(int blobIndex)
334                 {
335                         return ByteReader.FromBlob(blobHeap, blobIndex);
336                 }
337
338                 public override string ResolveString(int metadataToken)
339                 {
340                         string str;
341                         if (!strings.TryGetValue(metadataToken, out str))
342                         {
343                                 if ((metadataToken >> 24) != 0x70)
344                                 {
345                                         throw TokenOutOfRangeException(metadataToken);
346                                 }
347                                 int index = metadataToken & 0xFFFFFF;
348                                 int len = ReadCompressedInt(userStringHeap, ref index) & ~1;
349                                 StringBuilder sb = new StringBuilder(len / 2);
350                                 for (int i = 0; i < len; i += 2)
351                                 {
352                                         char ch = (char)(userStringHeap[index + i] | userStringHeap[index + i + 1] << 8);
353                                         sb.Append(ch);
354                                 }
355                                 str = sb.ToString();
356                                 strings.Add(metadataToken, str);
357                         }
358                         return str;
359                 }
360
361                 internal override Type ResolveType(int metadataToken, IGenericContext context)
362                 {
363                         int index = (metadataToken & 0xFFFFFF) - 1;
364                         if (index < 0)
365                         {
366                                 throw TokenOutOfRangeException(metadataToken);
367                         }
368                         else if ((metadataToken >> 24) == TypeDefTable.Index && index < TypeDef.RowCount)
369                         {
370                                 PopulateTypeDef();
371                                 return typeDefs[index];
372                         }
373                         else if ((metadataToken >> 24) == TypeRefTable.Index && index < TypeRef.RowCount)
374                         {
375                                 if (typeRefs == null)
376                                 {
377                                         typeRefs = new Type[TypeRef.records.Length];
378                                 }
379                                 if (typeRefs[index] == null)
380                                 {
381                                         int scope = TypeRef.records[index].ResolutionScope;
382                                         switch (scope >> 24)
383                                         {
384                                                 case AssemblyRefTable.Index:
385                                                         {
386                                                                 Assembly assembly = ResolveAssemblyRef((scope & 0xFFFFFF) - 1);
387                                                                 TypeName typeName = GetTypeName(TypeRef.records[index].TypeNameSpace, TypeRef.records[index].TypeName);
388                                                                 typeRefs[index] = assembly.ResolveType(typeName);
389                                                                 break;
390                                                         }
391                                                 case TypeRefTable.Index:
392                                                         {
393                                                                 Type outer = ResolveType(scope, null);
394                                                                 TypeName typeName = GetTypeName(TypeRef.records[index].TypeNameSpace, TypeRef.records[index].TypeName);
395                                                                 typeRefs[index] = outer.ResolveNestedType(typeName);
396                                                                 break;
397                                                         }
398                                                 case ModuleTable.Index:
399                                                 case ModuleRefTable.Index:
400                                                         {
401                                                                 Module module;
402                                                                 if (scope >> 24 == ModuleTable.Index)
403                                                                 {
404                                                                         if (scope == 0 || scope == 1)
405                                                                         {
406                                                                                 module = this;
407                                                                         }
408                                                                         else
409                                                                         {
410                                                                                 throw new NotImplementedException("self reference scope?");
411                                                                         }
412                                                                 }
413                                                                 else
414                                                                 {
415                                                                         module = ResolveModuleRef(ModuleRef.records[(scope & 0xFFFFFF) - 1]);
416                                                                 }
417                                                                 TypeName typeName = GetTypeName(TypeRef.records[index].TypeNameSpace, TypeRef.records[index].TypeName);
418                                                                 typeRefs[index] = module.FindType(typeName) ?? module.universe.GetMissingTypeOrThrow(module, null, typeName);
419                                                                 break;
420                                                         }
421                                                 default:
422                                                         throw new NotImplementedException("ResolutionScope = " + scope.ToString("X"));
423                                         }
424                                 }
425                                 return typeRefs[index];
426                         }
427                         else if ((metadataToken >> 24) == TypeSpecTable.Index && index < TypeSpec.RowCount)
428                         {
429                                 if (typeSpecs == null)
430                                 {
431                                         typeSpecs = new Type[TypeSpec.records.Length];
432                                 }
433                                 Type type = typeSpecs[index];
434                                 if (type == null)
435                                 {
436                                         TrackingGenericContext tc = context == null ? null : new TrackingGenericContext(context);
437                                         type = Signature.ReadTypeSpec(this, ByteReader.FromBlob(blobHeap, TypeSpec.records[index]), tc);
438                                         if (tc == null || !tc.IsUsed)
439                                         {
440                                                 typeSpecs[index] = type;
441                                         }
442                                 }
443                                 return type;
444                         }
445                         else
446                         {
447                                 throw TokenOutOfRangeException(metadataToken);
448                         }
449                 }
450
451                 private Module ResolveModuleRef(int moduleNameIndex)
452                 {
453                         string moduleName = GetString(moduleNameIndex);
454                         Module module = assembly.GetModule(moduleName);
455                         if (module == null)
456                         {
457                                 throw new FileNotFoundException(moduleName);
458                         }
459                         return module;
460                 }
461
462                 private sealed class TrackingGenericContext : IGenericContext
463                 {
464                         private readonly IGenericContext context;
465                         private bool used;
466
467                         internal TrackingGenericContext(IGenericContext context)
468                         {
469                                 this.context = context;
470                         }
471
472                         internal bool IsUsed
473                         {
474                                 get { return used; }
475                         }
476
477                         public Type GetGenericTypeArgument(int index)
478                         {
479                                 used = true;
480                                 return context.GetGenericTypeArgument(index);
481                         }
482
483                         public Type GetGenericMethodArgument(int index)
484                         {
485                                 used = true;
486                                 return context.GetGenericMethodArgument(index);
487                         }
488                 }
489
490                 private TypeName GetTypeName(int typeNamespace, int typeName)
491                 {
492                         return new TypeName(GetString(typeNamespace), GetString(typeName));
493                 }
494
495                 internal Assembly ResolveAssemblyRef(int index)
496                 {
497                         if (assemblyRefs == null)
498                         {
499                                 assemblyRefs = new Assembly[AssemblyRef.RowCount];
500                         }
501                         if (assemblyRefs[index] == null)
502                         {
503                                 assemblyRefs[index] = ResolveAssemblyRefImpl(ref AssemblyRef.records[index]);
504                         }
505                         return assemblyRefs[index];
506                 }
507
508                 private Assembly ResolveAssemblyRefImpl(ref AssemblyRefTable.Record rec)
509                 {
510                         const int PublicKey = 0x0001;
511                         string name = String.Format("{0}, Version={1}.{2}.{3}.{4}, Culture={5}, {6}={7}",
512                                 GetString(rec.Name),
513                                 rec.MajorVersion,
514                                 rec.MinorVersion,
515                                 rec.BuildNumber,
516                                 rec.RevisionNumber,
517                                 rec.Culture == 0 ? "neutral" : GetString(rec.Culture),
518                                 (rec.Flags & PublicKey) == 0 ? "PublicKeyToken" : "PublicKey",
519                                 PublicKeyOrTokenToString(rec.PublicKeyOrToken));
520                         return universe.Load(name, this.Assembly, true);
521                 }
522
523                 private string PublicKeyOrTokenToString(int publicKeyOrToken)
524                 {
525                         if (publicKeyOrToken == 0)
526                         {
527                                 return "null";
528                         }
529                         ByteReader br = GetBlob(publicKeyOrToken);
530                         if (br.Length == 0)
531                         {
532                                 return "null";
533                         }
534                         StringBuilder sb = new StringBuilder(br.Length * 2);
535                         while (br.Length > 0)
536                         {
537                                 sb.AppendFormat("{0:x2}", br.ReadByte());
538                         }
539                         return sb.ToString();
540                 }
541
542                 public override Guid ModuleVersionId
543                 {
544                         get
545                         {
546                                 byte[] buf = new byte[16];
547                                 Buffer.BlockCopy(guidHeap, 16 * (ModuleTable.records[0].Mvid - 1), buf, 0, 16);
548                                 return new Guid(buf);
549                         }
550                 }
551
552                 public override string FullyQualifiedName
553                 {
554                         get { return location ?? "<Unknown>"; }
555                 }
556
557                 public override string Name
558                 {
559                         get { return location == null ? "<Unknown>" : System.IO.Path.GetFileName(location); }
560                 }
561
562                 public override Assembly Assembly
563                 {
564                         get { return assembly; }
565                 }
566
567                 internal override Type FindType(TypeName typeName)
568                 {
569                         PopulateTypeDef();
570                         Type type;
571                         if (!types.TryGetValue(typeName, out type))
572                         {
573                                 LazyForwardedType fw;
574                                 if (forwardedTypes.TryGetValue(typeName, out fw))
575                                 {
576                                         return fw.GetType(this, typeName);
577                                 }
578                         }
579                         return type;
580                 }
581
582                 internal override Type FindTypeIgnoreCase(TypeName lowerCaseName)
583                 {
584                         PopulateTypeDef();
585                         foreach (Type type in types.Values)
586                         {
587                                 if (new TypeName(type.__Namespace, type.__Name).ToLowerInvariant() == lowerCaseName)
588                                 {
589                                         return type;
590                                 }
591                         }
592                         foreach (TypeName name in forwardedTypes.Keys)
593                         {
594                                 if (name.ToLowerInvariant() == lowerCaseName)
595                                 {
596                                         return forwardedTypes[name].GetType(this, name);
597                                 }
598                         }
599                         return null;
600                 }
601
602                 private Exception TokenOutOfRangeException(int metadataToken)
603                 {
604                         return new ArgumentOutOfRangeException("metadataToken", String.Format("Token 0x{0:x8} is not valid in the scope of module {1}.", metadataToken, this.Name));
605                 }
606
607                 public override MemberInfo ResolveMember(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
608                 {
609                         switch (metadataToken >> 24)
610                         {
611                                 case FieldTable.Index:
612                                         return ResolveField(metadataToken, genericTypeArguments, genericMethodArguments);
613                                 case MemberRefTable.Index:
614                                         int index = (metadataToken & 0xFFFFFF) - 1;
615                                         if (index < 0 || index >= MemberRef.RowCount)
616                                         {
617                                                 goto default;
618                                         }
619                                         return GetMemberRef(index, genericTypeArguments, genericMethodArguments);
620                                 case MethodDefTable.Index:
621                                 case MethodSpecTable.Index:
622                                         return ResolveMethod(metadataToken, genericTypeArguments, genericMethodArguments);
623                                 default:
624                                         throw TokenOutOfRangeException(metadataToken);
625                         }
626                 }
627
628                 internal FieldInfo GetFieldAt(TypeDefImpl owner, int index)
629                 {
630                         if (fields == null)
631                         {
632                                 fields = new FieldInfo[Field.records.Length];
633                         }
634                         if (fields[index] == null)
635                         {
636                                 fields[index] = new FieldDefImpl(this, owner ?? FindFieldOwner(index), index);
637                         }
638                         return fields[index];
639                 }
640
641                 public override FieldInfo ResolveField(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
642                 {
643                         int index = (metadataToken & 0xFFFFFF) - 1;
644                         if (index < 0)
645                         {
646                                 throw TokenOutOfRangeException(metadataToken);
647                         }
648                         else if ((metadataToken >> 24) == FieldTable.Index && index < Field.RowCount)
649                         {
650                                 return GetFieldAt(null, index);
651                         }
652                         else if ((metadataToken >> 24) == MemberRefTable.Index && index < MemberRef.RowCount)
653                         {
654                                 FieldInfo field = GetMemberRef(index, genericTypeArguments, genericMethodArguments) as FieldInfo;
655                                 if (field != null)
656                                 {
657                                         return field;
658                                 }
659                                 throw new ArgumentException(String.Format("Token 0x{0:x8} is not a valid FieldInfo token in the scope of module {1}.", metadataToken, this.Name), "metadataToken");
660                         }
661                         else
662                         {
663                                 throw TokenOutOfRangeException(metadataToken);
664                         }
665                 }
666
667                 private TypeDefImpl FindFieldOwner(int fieldIndex)
668                 {
669                         // TODO use binary search?
670                         for (int i = 0; i < TypeDef.records.Length; i++)
671                         {
672                                 int field = TypeDef.records[i].FieldList - 1;
673                                 int end = TypeDef.records.Length > i + 1 ? TypeDef.records[i + 1].FieldList - 1 : Field.records.Length;
674                                 if (field <= fieldIndex && fieldIndex < end)
675                                 {
676                                         PopulateTypeDef();
677                                         return typeDefs[i];
678                                 }
679                         }
680                         throw new InvalidOperationException();
681                 }
682
683                 internal MethodBase GetMethodAt(TypeDefImpl owner, int index)
684                 {
685                         if (methods == null)
686                         {
687                                 methods = new MethodBase[MethodDef.records.Length];
688                         }
689                         if (methods[index] == null)
690                         {
691                                 MethodDefImpl method = new MethodDefImpl(this, owner ?? FindMethodOwner(index), index);
692                                 methods[index] = method.IsConstructor ? new ConstructorInfoImpl(method) : (MethodBase)method;
693                         }
694                         return methods[index];
695                 }
696
697                 public override MethodBase ResolveMethod(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
698                 {
699                         int index = (metadataToken & 0xFFFFFF) - 1;
700                         if (index < 0)
701                         {
702                                 throw TokenOutOfRangeException(metadataToken);
703                         }
704                         else if ((metadataToken >> 24) == MethodDefTable.Index && index < MethodDef.RowCount)
705                         {
706                                 return GetMethodAt(null, index);
707                         }
708                         else if ((metadataToken >> 24) == MemberRefTable.Index && index < MemberRef.RowCount)
709                         {
710                                 MethodBase method = GetMemberRef(index, genericTypeArguments, genericMethodArguments) as MethodBase;
711                                 if (method != null)
712                                 {
713                                         return method;
714                                 }
715                                 throw new ArgumentException(String.Format("Token 0x{0:x8} is not a valid MethodBase token in the scope of module {1}.", metadataToken, this.Name), "metadataToken");
716                         }
717                         else if ((metadataToken >> 24) == MethodSpecTable.Index && index < MethodSpec.RowCount)
718                         {
719                                 MethodInfo method = (MethodInfo)ResolveMethod(MethodSpec.records[index].Method, genericTypeArguments, genericMethodArguments);
720                                 ByteReader instantiation = ByteReader.FromBlob(blobHeap, MethodSpec.records[index].Instantiation);
721                                 return method.MakeGenericMethod(Signature.ReadMethodSpec(this, instantiation, new GenericContext(genericTypeArguments, genericMethodArguments)));
722                         }
723                         else
724                         {
725                                 throw TokenOutOfRangeException(metadataToken);
726                         }
727                 }
728
729                 public override Type[] __ResolveOptionalParameterTypes(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments, out CustomModifiers[] customModifiers)
730                 {
731                         int index = (metadataToken & 0xFFFFFF) - 1;
732                         if (index < 0)
733                         {
734                                 throw TokenOutOfRangeException(metadataToken);
735                         }
736                         else if ((metadataToken >> 24) == MemberRefTable.Index && index < MemberRef.RowCount)
737                         {
738                                 int sig = MemberRef.records[index].Signature;
739                                 return Signature.ReadOptionalParameterTypes(this, GetBlob(sig), new GenericContext(genericTypeArguments, genericMethodArguments), out customModifiers);
740                         }
741                         else if ((metadataToken >> 24) == MethodDefTable.Index && index < MethodDef.RowCount)
742                         {
743                                 // for convenience, we support passing a MethodDef token as well, because in some places
744                                 // it makes sense to have a vararg method that is referred to by its methoddef (e.g. ldftn).
745                                 // Note that MethodSpec doesn't make sense, because generic methods cannot be vararg.
746                                 customModifiers = Empty<CustomModifiers>.Array;
747                                 return Type.EmptyTypes;
748                         }
749                         else
750                         {
751                                 throw TokenOutOfRangeException(metadataToken);
752                         }
753                 }
754
755                 public override string ScopeName
756                 {
757                         get { return GetString(ModuleTable.records[0].Name); }
758                 }
759
760                 private TypeDefImpl FindMethodOwner(int methodIndex)
761                 {
762                         // TODO use binary search?
763                         for (int i = 0; i < TypeDef.records.Length; i++)
764                         {
765                                 int method = TypeDef.records[i].MethodList - 1;
766                                 int end = TypeDef.records.Length > i + 1 ? TypeDef.records[i + 1].MethodList - 1 : MethodDef.records.Length;
767                                 if (method <= methodIndex && methodIndex < end)
768                                 {
769                                         PopulateTypeDef();
770                                         return typeDefs[i];
771                                 }
772                         }
773                         throw new InvalidOperationException();
774                 }
775
776                 private MemberInfo GetMemberRef(int index, Type[] genericTypeArguments, Type[] genericMethodArguments)
777                 {
778                         if (memberRefs == null)
779                         {
780                                 memberRefs = new MemberInfo[MemberRef.records.Length];
781                         }
782                         if (memberRefs[index] == null)
783                         {
784                                 int owner = MemberRef.records[index].Class;
785                                 int sig = MemberRef.records[index].Signature;
786                                 string name = GetString(MemberRef.records[index].Name);
787                                 switch (owner >> 24)
788                                 {
789                                         case MethodDefTable.Index:
790                                                 return GetMethodAt(null, (owner & 0xFFFFFF) - 1);
791                                         case ModuleRefTable.Index:
792                                                 memberRefs[index] = ResolveTypeMemberRef(ResolveModuleType(owner), name, ByteReader.FromBlob(blobHeap, sig));
793                                                 break;
794                                         case TypeDefTable.Index:
795                                         case TypeRefTable.Index:
796                                                 memberRefs[index] = ResolveTypeMemberRef(ResolveType(owner), name, ByteReader.FromBlob(blobHeap, sig));
797                                                 break;
798                                         case TypeSpecTable.Index:
799                                         {
800                                                 Type type = ResolveType(owner, genericTypeArguments, genericMethodArguments);
801                                                 if (type.IsArray)
802                                                 {
803                                                         MethodSignature methodSig = MethodSignature.ReadSig(this, ByteReader.FromBlob(blobHeap, sig), new GenericContext(genericTypeArguments, genericMethodArguments));
804                                                         return type.FindMethod(name, methodSig)
805                                                                 ?? universe.GetMissingMethodOrThrow(type, name, methodSig);
806                                                 }
807                                                 else if (type.IsConstructedGenericType)
808                                                 {
809                                                         MemberInfo member = ResolveTypeMemberRef(type.GetGenericTypeDefinition(), name, ByteReader.FromBlob(blobHeap, sig));
810                                                         MethodBase mb = member as MethodBase;
811                                                         if (mb != null)
812                                                         {
813                                                                 member = mb.BindTypeParameters(type);
814                                                         }
815                                                         FieldInfo fi = member as FieldInfo;
816                                                         if (fi != null)
817                                                         {
818                                                                 member = fi.BindTypeParameters(type);
819                                                         }
820                                                         return member;
821                                                 }
822                                                 else
823                                                 {
824                                                         return ResolveTypeMemberRef(type, name, ByteReader.FromBlob(blobHeap, sig));
825                                                 }
826                                         }
827                                         default:
828                                                 throw new BadImageFormatException();
829                                 }
830                         }
831                         return memberRefs[index];
832                 }
833
834                 private Type ResolveModuleType(int token)
835                 {
836                         int index = (token & 0xFFFFFF) - 1;
837                         string name = GetString(ModuleRef.records[index]);
838                         Module module = assembly.GetModule(name);
839                         if (module == null || module.IsResource())
840                         {
841                                 throw new BadImageFormatException();
842                         }
843                         return module.GetModuleType();
844                 }
845
846                 private MemberInfo ResolveTypeMemberRef(Type type, string name, ByteReader sig)
847                 {
848                         if (sig.PeekByte() == Signature.FIELD)
849                         {
850                                 Type org = type;
851                                 FieldSignature fieldSig = FieldSignature.ReadSig(this, sig, type);
852                                 FieldInfo field = type.FindField(name, fieldSig);
853                                 if (field == null && universe.MissingMemberResolution)
854                                 {
855                                         return universe.GetMissingFieldOrThrow(type, name, fieldSig);
856                                 }
857                                 while (field == null && (type = type.BaseType) != null)
858                                 {
859                                         field = type.FindField(name, fieldSig);
860                                 }
861                                 if (field != null)
862                                 {
863                                         return field;
864                                 }
865                                 throw new MissingFieldException(org.ToString(), name);
866                         }
867                         else
868                         {
869                                 Type org = type;
870                                 MethodSignature methodSig = MethodSignature.ReadSig(this, sig, type);
871                                 MethodBase method = type.FindMethod(name, methodSig);
872                                 if (method == null && universe.MissingMemberResolution)
873                                 {
874                                         return universe.GetMissingMethodOrThrow(type, name, methodSig);
875                                 }
876                                 while (method == null && (type = type.BaseType) != null)
877                                 {
878                                         method = type.FindMethod(name, methodSig);
879                                 }
880                                 if (method != null)
881                                 {
882                                         return method;
883                                 }
884                                 throw new MissingMethodException(org.ToString(), name);
885                         }
886                 }
887
888                 internal ByteReader GetStandAloneSig(int index)
889                 {
890                         return ByteReader.FromBlob(blobHeap, StandAloneSig.records[index]);
891                 }
892
893                 public override byte[] ResolveSignature(int metadataToken)
894                 {
895                         int index = (metadataToken & 0xFFFFFF) - 1;
896                         if ((metadataToken >> 24) == StandAloneSigTable.Index && index >= 0 && index < StandAloneSig.RowCount)
897                         {
898                                 ByteReader br = GetStandAloneSig(index);
899                                 return br.ReadBytes(br.Length);
900                         }
901                         else
902                         {
903                                 throw TokenOutOfRangeException(metadataToken);
904                         }
905                 }
906
907                 public override __StandAloneMethodSig __ResolveStandAloneMethodSig(int metadataToken, Type[] genericTypeArguments, Type[] genericMethodArguments)
908                 {
909                         int index = (metadataToken & 0xFFFFFF) - 1;
910                         if ((metadataToken >> 24) == StandAloneSigTable.Index && index >= 0 && index < StandAloneSig.RowCount)
911                         {
912                                 return MethodSignature.ReadStandAloneMethodSig(this, GetStandAloneSig(index), new GenericContext(genericTypeArguments, genericMethodArguments));
913                         }
914                         else
915                         {
916                                 throw TokenOutOfRangeException(metadataToken);
917                         }
918                 }
919
920                 internal MethodInfo GetEntryPoint()
921                 {
922                         if (cliHeader.EntryPointToken != 0 && (cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0)
923                         {
924                                 return (MethodInfo)ResolveMethod((int)cliHeader.EntryPointToken);
925                         }
926                         return null;
927                 }
928
929                 internal string[] GetManifestResourceNames()
930                 {
931                         string[] names = new string[ManifestResource.records.Length];
932                         for (int i = 0; i < ManifestResource.records.Length; i++)
933                         {
934                                 names[i] = GetString(ManifestResource.records[i].Name);
935                         }
936                         return names;
937                 }
938
939                 internal ManifestResourceInfo GetManifestResourceInfo(string resourceName)
940                 {
941                         for (int i = 0; i < ManifestResource.records.Length; i++)
942                         {
943                                 if (resourceName == GetString(ManifestResource.records[i].Name))
944                                 {
945                                         ManifestResourceInfo info = new ManifestResourceInfo(this, i);
946                                         Assembly asm = info.ReferencedAssembly;
947                                         if (asm != null && !asm.__IsMissing && asm.GetManifestResourceInfo(resourceName) == null)
948                                         {
949                                                 return null;
950                                         }
951                                         return info;
952                                 }
953                         }
954                         return null;
955                 }
956
957                 internal Stream GetManifestResourceStream(string resourceName)
958                 {
959                         for (int i = 0; i < ManifestResource.records.Length; i++)
960                         {
961                                 if (resourceName == GetString(ManifestResource.records[i].Name))
962                                 {
963                                         if (ManifestResource.records[i].Implementation != 0x26000000)
964                                         {
965                                                 ManifestResourceInfo info = new ManifestResourceInfo(this, i);
966                                                 switch (ManifestResource.records[i].Implementation >> 24)
967                                                 {
968                                                         case FileTable.Index:
969                                                                 string fileName = Path.Combine(Path.GetDirectoryName(location), info.FileName);
970                                                                 if (System.IO.File.Exists(fileName))
971                                                                 {
972                                                                         // note that, like System.Reflection, we return null for zero length files and
973                                                                         // ManifestResource.Offset is ignored
974                                                                         FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);
975                                                                         if (fs.Length == 0)
976                                                                         {
977                                                                                 fs.Close();
978                                                                                 return null;
979                                                                         }
980                                                                         return fs;
981                                                                 }
982                                                                 return null;
983                                                         case AssemblyRefTable.Index:
984                                                                 Assembly asm = info.ReferencedAssembly;
985                                                                 if (asm.__IsMissing)
986                                                                 {
987                                                                         return null;
988                                                                 }
989                                                                 return asm.GetManifestResourceStream(resourceName);
990                                                         default:
991                                                                 throw new BadImageFormatException();
992                                                 }
993                                         }
994                                         SeekRVA((int)cliHeader.Resources.VirtualAddress + ManifestResource.records[i].Offset);
995                                         BinaryReader br = new BinaryReader(stream);
996                                         int length = br.ReadInt32();
997                                         return new MemoryStream(br.ReadBytes(length));
998                                 }
999                         }
1000                         return null;
1001                 }
1002
1003                 public override AssemblyName[] __GetReferencedAssemblies()
1004                 {
1005                         List<AssemblyName> list = new List<AssemblyName>();
1006                         for (int i = 0; i < AssemblyRef.records.Length; i++)
1007                         {
1008                                 AssemblyName name = new AssemblyName();
1009                                 name.Name = GetString(AssemblyRef.records[i].Name);
1010                                 name.Version = new Version(
1011                                         AssemblyRef.records[i].MajorVersion,
1012                                         AssemblyRef.records[i].MinorVersion,
1013                                         AssemblyRef.records[i].BuildNumber,
1014                                         AssemblyRef.records[i].RevisionNumber);
1015                                 if (AssemblyRef.records[i].PublicKeyOrToken != 0)
1016                                 {
1017                                         byte[] keyOrToken = GetBlobCopy(AssemblyRef.records[i].PublicKeyOrToken);
1018                                         const int PublicKey = 0x0001;
1019                                         if ((AssemblyRef.records[i].Flags & PublicKey) != 0)
1020                                         {
1021                                                 name.SetPublicKey(keyOrToken);
1022                                         }
1023                                         else
1024                                         {
1025                                                 name.SetPublicKeyToken(keyOrToken);
1026                                         }
1027                                 }
1028                                 else
1029                                 {
1030                                         name.SetPublicKeyToken(Empty<byte>.Array);
1031                                 }
1032                                 if (AssemblyRef.records[i].Culture != 0)
1033                                 {
1034                                         name.Culture = GetString(AssemblyRef.records[i].Culture);
1035                                 }
1036                                 else
1037                                 {
1038                                         name.Culture = "";
1039                                 }
1040                                 if (AssemblyRef.records[i].HashValue != 0)
1041                                 {
1042                                         name.hash = GetBlobCopy(AssemblyRef.records[i].HashValue);
1043                                 }
1044                                 name.Flags = (AssemblyNameFlags)AssemblyRef.records[i].Flags;
1045                                 list.Add(name);
1046                         }
1047                         return list.ToArray();
1048                 }
1049
1050                 public override void __ResolveReferencedAssemblies(Assembly[] assemblies)
1051                 {
1052                         if (assemblyRefs == null)
1053                         {
1054                                 assemblyRefs = new Assembly[AssemblyRef.RowCount];
1055                         }
1056                         for (int i = 0; i < assemblies.Length; i++)
1057                         {
1058                                 if (assemblyRefs[i] == null)
1059                                 {
1060                                         assemblyRefs[i] = assemblies[i];
1061                                 }
1062                         }
1063                 }
1064
1065                 public override string[] __GetReferencedModules()
1066                 {
1067                         string[] arr = new string[this.ModuleRef.RowCount];
1068                         for (int i = 0; i < arr.Length; i++)
1069                         {
1070                                 arr[i] = GetString(this.ModuleRef.records[i]);
1071                         }
1072                         return arr;
1073                 }
1074
1075                 public override Type[] __GetReferencedTypes()
1076                 {
1077                         Type[] arr = new Type[this.TypeRef.RowCount];
1078                         for (int i = 0; i < arr.Length; i++)
1079                         {
1080                                 arr[i] = ResolveType((TypeRefTable.Index << 24) + i + 1);
1081                         }
1082                         return arr;
1083                 }
1084
1085                 public override Type[] __GetExportedTypes()
1086                 {
1087                         Type[] arr = new Type[this.ExportedType.RowCount];
1088                         for (int i = 0; i < arr.Length; i++)
1089                         {
1090                                 arr[i] = ResolveExportedType(i);
1091                         }
1092                         return arr;
1093                 }
1094
1095                 private Type ResolveExportedType(int index)
1096                 {
1097                         TypeName typeName = GetTypeName(ExportedType.records[index].TypeNamespace, ExportedType.records[index].TypeName);
1098                         int implementation = ExportedType.records[index].Implementation;
1099                         int token = ExportedType.records[index].TypeDefId;
1100                         switch (implementation >> 24)
1101                         {
1102                                 case AssemblyRefTable.Index:
1103                                         return ResolveAssemblyRef((implementation & 0xFFFFFF) - 1).ResolveType(typeName).SetMetadataTokenForMissing(token);
1104                                 case ExportedTypeTable.Index:
1105                                         return ResolveExportedType((implementation & 0xFFFFFF) - 1).ResolveNestedType(typeName).SetMetadataTokenForMissing(token);
1106                                 default:
1107                                         throw new NotImplementedException();
1108                         }
1109                 }
1110
1111                 internal override Type GetModuleType()
1112                 {
1113                         PopulateTypeDef();
1114                         return moduleType;
1115                 }
1116
1117                 public override string __ImageRuntimeVersion
1118                 {
1119                         get { return imageRuntimeVersion; }
1120                 }
1121
1122                 public override int MDStreamVersion
1123                 {
1124                         get { return metadataStreamVersion; }
1125                 }
1126
1127                 public override void __GetDataDirectoryEntry(int index, out int rva, out int length)
1128                 {
1129                         peFile.GetDataDirectoryEntry(index, out rva, out length);
1130                 }
1131
1132                 public override long __RelativeVirtualAddressToFileOffset(int rva)
1133                 {
1134                         return peFile.RvaToFileOffset((uint)rva);
1135                 }
1136
1137                 public override bool __GetSectionInfo(int rva, out string name, out int characteristics)
1138                 {
1139                         return peFile.GetSectionInfo(rva, out name, out characteristics);
1140                 }
1141
1142                 public override int __ReadDataFromRVA(int rva, byte[] data, int offset, int length)
1143                 {
1144                         SeekRVA(rva);
1145                         int totalBytesRead = 0;
1146                         while (length > 0)
1147                         {
1148                                 int read = stream.Read(data, offset, length);
1149                                 if (read == 0)
1150                                 {
1151                                         // C++ assemblies can have fields that have an RVA that lies outside of the file
1152                                         break;
1153                                 }
1154                                 offset += read;
1155                                 length -= read;
1156                                 totalBytesRead += read;
1157                         }
1158                         return totalBytesRead;
1159                 }
1160
1161                 public override void GetPEKind(out PortableExecutableKinds peKind, out ImageFileMachine machine)
1162                 {
1163                         peKind = 0;
1164                         if ((cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_ILONLY) != 0)
1165                         {
1166                                 peKind |= PortableExecutableKinds.ILOnly;
1167                         }
1168                         switch (cliHeader.Flags & (CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED))
1169                         {
1170                                 case CliHeader.COMIMAGE_FLAGS_32BITREQUIRED:
1171                                         peKind |= PortableExecutableKinds.Required32Bit;
1172                                         break;
1173                                 case CliHeader.COMIMAGE_FLAGS_32BITREQUIRED | CliHeader.COMIMAGE_FLAGS_32BITPREFERRED:
1174                                         peKind |= PortableExecutableKinds.Preferred32Bit;
1175                                         break;
1176                                 default:
1177                                         // COMIMAGE_FLAGS_32BITPREFERRED by itself is illegal, so we ignore it
1178                                         // (not setting any flag is ok)
1179                                         break;
1180                         }
1181                         if (peFile.OptionalHeader.Magic == IMAGE_OPTIONAL_HEADER.IMAGE_NT_OPTIONAL_HDR64_MAGIC)
1182                         {
1183                                 peKind |= PortableExecutableKinds.PE32Plus;
1184                         }
1185
1186                         machine = (ImageFileMachine)peFile.FileHeader.Machine;
1187                 }
1188
1189                 public override int __Subsystem
1190                 {
1191                         get { return peFile.OptionalHeader.Subsystem; }
1192                 }
1193
1194                 public override IList<CustomAttributeData> __GetPlaceholderAssemblyCustomAttributes(bool multiple, bool security)
1195                 {
1196                         TypeName typeName;
1197                         switch ((multiple ? 1 : 0) + (security ? 2 : 0))
1198                         {
1199                                 case 0:
1200                                         typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHere");
1201                                         break;
1202                                 case 1:
1203                                         typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHereM");
1204                                         break;
1205                                 case 2:
1206                                         typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHereS");
1207                                         break;
1208                                 case 3:
1209                                 default:
1210                                         typeName = new TypeName("System.Runtime.CompilerServices", "AssemblyAttributesGoHereSM");
1211                                         break;
1212                         }
1213                         List<CustomAttributeData> list = new List<CustomAttributeData>();
1214                         for (int i = 0; i < CustomAttribute.records.Length; i++)
1215                         {
1216                                 if ((CustomAttribute.records[i].Parent >> 24) == TypeRefTable.Index)
1217                                 {
1218                                         int index = (CustomAttribute.records[i].Parent & 0xFFFFFF) - 1;
1219                                         if (typeName == GetTypeName(TypeRef.records[index].TypeNameSpace, TypeRef.records[index].TypeName))
1220                                         {
1221                                                 list.Add(new CustomAttributeData(this, i));
1222                                         }
1223                                 }
1224                         }
1225                         return list;
1226                 }
1227
1228                 internal override void Dispose()
1229                 {
1230                         stream.Close();
1231                 }
1232
1233                 internal override void ExportTypes(int fileToken, IKVM.Reflection.Emit.ModuleBuilder manifestModule)
1234                 {
1235                         PopulateTypeDef();
1236                         manifestModule.ExportTypes(typeDefs, fileToken);
1237                 }
1238
1239                 protected override long GetImageBaseImpl()
1240                 {
1241                         return (long)peFile.OptionalHeader.ImageBase;
1242                 }
1243
1244                 protected override long GetStackReserveImpl()
1245                 {
1246                         return (long)peFile.OptionalHeader.SizeOfStackReserve;
1247                 }
1248
1249                 protected override int GetFileAlignmentImpl()
1250                 {
1251                         return (int)peFile.OptionalHeader.FileAlignment;
1252                 }
1253
1254                 protected override DllCharacteristics GetDllCharacteristicsImpl()
1255                 {
1256                         return (DllCharacteristics)peFile.OptionalHeader.DllCharacteristics;
1257                 }
1258
1259                 public override int __EntryPointRVA
1260                 {
1261                         get { return (cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) != 0 ? (int)cliHeader.EntryPointToken : 0; }
1262                 }
1263
1264                 public override int __EntryPointToken
1265                 {
1266                         get { return (cliHeader.Flags & CliHeader.COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) == 0 ? (int)cliHeader.EntryPointToken : 0; }
1267                 }
1268         }
1269 }