context: /*
    Copyright (C) 2009-2011 Jeroen Frijters
  
    This software is provided 'as-is', without any express or implied
    warranty.  In no event will the authors be held liable for any damages
    arising from the use of this software.
  
    Permission is granted to anyone to use this software for any purpose,
    including commercial applications, and to alter it and redistribute it
    freely, subject to the following restrictions:
  
    1. The origin of this software must not be misrepresented; you must not
       claim that you wrote the original software. If you use this software
       in a product, an acknowledgment in the product documentation would be
       appreciated but is not required.
    2. Altered source versions must be plainly marked as such, and must not be
       misrepresented as being the original software.
    3. This notice may not be removed or altered from any source distribution.
  
    Jeroen Frijters
    jeroen@frijters.net
    
  */
  using System;
  using System.IO;
  using System.Text;
  using System.Collections.Generic;
  using IKVM.Reflection.Metadata;
  
  namespace IKVM.Reflection.Reader
  {
  	sealed class StreamHeader
  	{
  		internal uint Offset;
  		internal uint Size;
  		internal string Name;
  
  		internal void Read(BinaryReader br)
  		{
  			Offset = br.ReadUInt32();
  			Size = br.ReadUInt32();
  			byte[] buf = new byte[32];
  			byte b;
  			int len = 0;
  			while ((b = br.ReadByte()) != 0)
  			{
  				buf[len++] = b;
  			}
  			Name = Encoding.UTF8.GetString(buf, 0, len); ;
  			int padding = -1 + ((len + 4) & ~3) - len;
  			br.BaseStream.Seek(padding, SeekOrigin.Current);
  		}
  	}
  
  	sealed class ModuleReader : Module
  	{
  		internal readonly Stream stream;
  		private readonly string location;
  		private Assembly assembly;
  		private readonly PEReader peFile = new PEReader();
  		private readonly CliHeader cliHeader = new CliHeader();
  		private string imageRuntimeVersion;
  		private int metadataStreamVersion;
  		private byte[] stringHeap;
  		private byte[] blobHeap;
  		private byte[] userStringHeap;
  		private byte[] guidHeap;
  		private TypeDefImpl[] typeDefs;
  		private TypeDefImpl moduleType;
  		private Assembly[] assemblyRefs;
  		private Type[] typeRefs;
  		private Type[] typeSpecs;
  		private FieldInfo[] fields;
  		private MethodBase[] methods;
  		private MemberInfo[] memberRefs;
  		private Dictionary<int, string> strings = new Dictionary<int, string>();
  		private Dictionary<TypeName, Type> types = new Dictionary<TypeName, Type>();
  		private Dictionary<TypeName, LazyForwardedType> forwardedTypes = new Dictionary<TypeName, LazyForwardedType>();
  
  		private sealed class LazyForwardedType
  		{
  			private readonly int assemblyRef;
  			private Type type;
  
  			internal LazyForwardedType(int assemblyRef)
  			{
  				this.assemblyRef = assemblyRef;
  			}
  
  			internal Type GetType(ModuleReader module, TypeName typeName)
  			{
  				if (type == null)
  				{
  					Assembly asm = module.ResolveAssemblyRef(assemblyRef);
  					type = asm.ResolveType(typeName);
  					if (type == null)
  					{
  						throw new TypeLoadException(typeName.ToString());
  					}
  				}
  				return type;
  			}
  		}
  
  		internal ModuleReader(AssemblyReader assembly, Universe universe, Stream stream, string location)
  			: base(universe)
  		{
  			this.stream = stream;
  			this.location = location;
  			Read();
  			if (assembly == null && AssemblyTable.records.Length != 0)
  			{
  				assembly = new AssemblyReader(location, this);
  			}
  			this.assembly = assembly;
  		}
  
  		private void Read()
  		{
  			BinaryReader br = new BinaryReader(stream);
  			peFile.Read(br);
  			stream.Seek(peFile.RvaToFileOffset(peFile.GetComDescriptorVirtualAddress()), SeekOrigin.Begin);
  			cliHeader.Read(br);
  			stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaDataRVA), SeekOrigin.Begin);
  			foreach (StreamHeader sh in ReadStreamHeaders(br, out imageRuntimeVersion))
  			{
  				switch (sh.Name)
  				{
  					case "#Strings":
  						stringHeap = ReadHeap(stream, sh);
  						break;
  					case "#Blob":
  						blobHeap = ReadHeap(stream, sh);
  						break;
  					case "#US":
  						userStringHeap = ReadHeap(stream, sh);
  						break;
  					case "#GUID":
  						guidHeap = ReadHeap(stream, sh);
  						break;
  					case "#~":
  					case "#-":
  						stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaDataRVA + sh.Offset), SeekOrigin.Begin);
  						ReadTables(br);
  						break;
  					default:
  						throw new BadImageFormatException("Unsupported stream: " + sh.Name);
  				}
  			}
  		}
  
  		internal void SetAssembly(Assembly assembly)
  		{
  			this.assembly = assembly;
  		}
  
  		private static StreamHeader[] ReadStreamHeaders(BinaryReader br, out string Version)
  		{
  			uint Signature = br.ReadUInt32();
  			if (Signature != 0x424A5342)
  			{
  				throw new BadImageFormatException("Invalid metadata signature");
  			}
  			/*ushort MajorVersion =*/ br.ReadUInt16();
  			/*ushort MinorVersion =*/ br.ReadUInt16();
  			/*uint Reserved =*/ br.ReadUInt32();
  			uint Length = br.ReadUInt32();
  			byte[] buf = br.ReadBytes((int)Length);
  			Version = Encoding.UTF8.GetString(buf).TrimEnd('\u0000');
  			/*ushort Flags =*/ br.ReadUInt16();
  			ushort Streams = br.ReadUInt16();
  			StreamHeader[] streamHeaders = new StreamHeader[Streams];
  			for (int i = 0; i < streamHeaders.Length; i++)
  			{
  				streamHeaders[i] = new StreamHeader();
  				streamHeaders[i].Read(br);
  			}
  			return streamHeaders;
  		}
  
  		private void ReadTables(BinaryReader br)
  		{
  			Table[] tables = GetTables();
  			/*uint Reserved0 =*/ br.ReadUInt32();
  			byte MajorVersion = br.ReadByte();
  			byte MinorVersion = br.ReadByte();
  			metadataStreamVersion = MajorVersion << 16 | MinorVersion;
  			byte HeapSizes = br.ReadByte();
  			/*byte Reserved7 =*/ br.ReadByte();
  			ulong Valid = br.ReadUInt64();
  			/*ulong Sorted =*/ br.ReadUInt64();
  			for (int i = 0; i < 64; i++)
  			{
  				if ((Valid & (1UL << i)) != 0)
  				{
  					tables[i].RowCount = br.ReadInt32();
  				}
  				else if (tables[i] != null)
  				{
  					tables[i].RowCount = 0;
  				}
  			}
  			MetadataReader mr = new MetadataReader(this, br.BaseStream, HeapSizes);
  			for (int i = 0; i < 64; i++)
  			{
  				if ((Valid & (1UL << i)) != 0)
  				{
  					tables[i].Read(mr);
  				}
  			}
  			if (ParamPtr.RowCount != 0)
  			{
  				throw new NotImplementedException("ParamPtr table support has not yet been implemented.");
  			}
  		}
  
  		private byte[] ReadHeap(Stream stream, StreamHeader sh)
  		{
  			byte[] buf = new byte[sh.Size];
  			stream.Seek(peFile.RvaToFileOffset(cliHeader.MetaDataRVA + sh.Offset), SeekOrigin.Begin);
  			for (int pos = 0; pos < buf.Length; )
  			{
  				int read = stream.Read(buf, pos, buf.Length - pos);
  				if (read == 0)
  				{
  					throw new BadImageFormatException();
  				}
  				pos += read;
  			}
  			return buf;
  		}
  
  		internal void SeekRVA(int rva)
  		{
  			stream.Seek(peFile.RvaToFileOffset((uint)rva), SeekOrigin.Begin);
  		}
  
  		internal override void GetTypesImpl(List<Type> list)
  		{
  			PopulateTypeDef();
  			foreach (TypeDefImpl type in typeDefs)
  			{
  				if (type != moduleType)
  				{
  					list.Add(type);
  				}
  			}
  		}
  
  		private void PopulateTypeDef()
  		{
  			if (typeDefs == null)
  			{
  				typeDefs = new TypeDefImpl[TypeDef.records.Length];
  				for (int i = 0; i < typeDefs.Length; i++)
  				{
  					TypeDefImpl type = new TypeDefImpl(this, i);
  					typeDefs[i] = type;
  					if (type.IsModulePseudoType)
  					{
  						moduleType = type;
  					}
  					else if (!type.IsNestedByFlags)
  					{
  						types.Add(new TypeName(type.__Namespace, type.__Name), type);
  					}
  				}
  				// add forwarded types to forwardedTypes dictionary (because Module.GetType(string) should return them)
  				for (int i = 0; i < ExportedType.records.Length; i++)
  				{
  					int implementation = ExportedType.records[i].Implementation;
  					if (implementation >> 24 == AssemblyRefTable.Index)
  					{
  						TypeName typeName = GetTypeName(ExportedType.records[i].TypeNamespace, ExportedType.records[i].TypeName);
  						forwardedTypes.Add(typeName, new LazyForwardedType((implementation & 0xFFFFFF) - 1));
  					}
  				}
  			}
  		}
  
  		internal string GetString(int index)
  		{
  			if (index == 0)
  			{
  				return null;
  			}
  			string str;
  			if (!strings.TryGetValue(index, out str))
  			{
  				int len = 0;
  				while (stringHeap[index + len] != 0)
  				{
  					len++;
  				}
  				str = Encoding.UTF8.GetString(stringHeap, index, len);
  				strings.Add(index, str);
  			}
  			return str;
  		}
  
  		private static int ReadCompressedInt(byte[] buffer, ref int offset)
  		{
  			byte b1 = buffer[offset++];
  			if (b1 <= 0x7F)
  			{
  				return b1;
  			}
  			else if ((b1 & 0xC0) == 0x80)
  			{
  				byte b2 = buffer[offset++];
  				return ((b1 & 0x3F) << 8) | b2;
  			}
  			else
  			{
  				byte b2 = buffer[offset++];
  				byte b3 = buffer[offset++];
  				byte b4 = buffer[offset++];
  				return ((b1 & 0x3F) << 24) + (b2 << 16) + (b3 << 8) + b4;
  			}
  		}
  
  		internal byte[] GetBlobCopy(int blobIndex)
  		{
  			int len = ReadCompressedInt(blobHeap, ref blobIndex);
  			byte[] buf = new byte[len];
  			Buffer.BlockCopy(blobHeap, blobIndex, buf, 0, len);
  			return buf;
  		}
  
  		internal override ByteReader GetBlob(int blobIndex)
  		{
  			return ByteReader.FromBlob(blobHeap, blobIndex);
  		}
  
  		public override string ResolveString(int metadataToken)
  		{
  			string str;
  			if (!strings.TryGetValue(metadataToken, out str))
  			{
  				if ((metadataToken >> 24) != 0x70)
  				{
  					throw new ArgumentOutOfRangeException();
  				}
  				int index = metadataToken & 0xFFFFFF;
  				int len = ReadCompressedInt(userStringHeap, ref index) & ~1;
  				StringBuilder sb = new StringBuilder(len / 2);
  				for (int i = 0; i < len; i += 2)
  				{
  					char ch = (char)(userStringHeap[index + i] | userStringHeap[index + i + 1] << 8);
  					sb.Append(ch);
  				}
  				str = sb.ToString();
  				strings.Add(metadataToken, str);
  			}
  			return str;
  		}
  
  		internal Type ResolveType(int metadataToken, IGenericContext context)
  		{
  			switch (metadataToken >> 24)
  			{
  				case TypeDefTable.Index:
  					PopulateTypeDef();
  					return typeDefs[(metadataToken & 0xFFFFFF) - 1];
  				case TypeRefTable.Index:
  					{
  						if (typeRefs == null)
  						{
  							typeRefs = new Type[TypeRef.records.Length];
  						}
  
gt: 						int index = (metadataToken & 0xFFFFFF) - 1;