2014-08-13 10:39:27 +01:00
/ *
Copyright ( C ) 2009 - 2013 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
{
private 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 [ ] guidHeap ;
private uint userStringHeapOffset ;
private uint userStringHeapSize ;
private byte [ ] lazyUserStringHeap ;
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 index ;
private Type type ;
internal LazyForwardedType ( int index )
{
this . index = index ;
}
internal Type GetType ( ModuleReader module )
{
// guard against circular type forwarding
if ( type = = MarkerType . Pinned )
{
TypeName typeName = module . GetTypeName ( module . ExportedType . records [ index ] . TypeNamespace , module . ExportedType . records [ index ] . TypeName ) ;
return module . universe . GetMissingTypeOrThrow ( module , module , null , typeName ) . SetCyclicTypeForwarder ( ) ;
}
else if ( type = = null )
{
type = MarkerType . Pinned ;
type = module . ResolveExportedType ( index ) ;
}
return type ;
}
}
internal ModuleReader ( AssemblyReader assembly , Universe universe , Stream stream , string location , bool mapped )
: base ( universe )
{
this . stream = universe ! = null & & universe . MetadataOnly ? null : stream ;
this . location = location ;
Read ( stream , mapped ) ;
if ( universe ! = null & & universe . WindowsRuntimeProjection & & imageRuntimeVersion . StartsWith ( "WindowsRuntime " , StringComparison . Ordinal ) )
{
WindowsRuntimeProjection . Patch ( this , strings , ref imageRuntimeVersion , ref blobHeap ) ;
}
if ( assembly = = null & & AssemblyTable . records . Length ! = 0 )
{
assembly = new AssemblyReader ( location , this ) ;
}
this . assembly = assembly ;
}
private void Read ( Stream stream , bool mapped )
{
BinaryReader br = new BinaryReader ( stream ) ;
peFile . Read ( br , mapped ) ;
stream . Seek ( peFile . RvaToFileOffset ( peFile . GetComDescriptorVirtualAddress ( ) ) , SeekOrigin . Begin ) ;
cliHeader . Read ( br ) ;
stream . Seek ( peFile . RvaToFileOffset ( cliHeader . MetaData . VirtualAddress ) , SeekOrigin . Begin ) ;
foreach ( StreamHeader sh in ReadStreamHeaders ( br , out imageRuntimeVersion ) )
{
switch ( sh . Name )
{
case "#Strings" :
stringHeap = ReadHeap ( stream , sh . Offset , sh . Size ) ;
break ;
case "#Blob" :
blobHeap = ReadHeap ( stream , sh . Offset , sh . Size ) ;
break ;
case "#US" :
userStringHeapOffset = sh . Offset ;
userStringHeapSize = sh . Size ;
break ;
case "#GUID" :
guidHeap = ReadHeap ( stream , sh . Offset , sh . Size ) ;
break ;
case "#~" :
case "#-" :
stream . Seek ( peFile . RvaToFileOffset ( cliHeader . MetaData . VirtualAddress + sh . Offset ) , SeekOrigin . Begin ) ;
ReadTables ( br ) ;
break ;
default :
// we ignore unknown streams, because the CLR does so too
// (and some obfuscators add bogus streams)
break ;
}
}
}
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 & ( 1 UL < < i ) ) ! = 0 )
{
tables [ i ] . Sorted = ( Sorted & ( 1 UL < < i ) ) ! = 0 ;
tables [ i ] . RowCount = br . ReadInt32 ( ) ;
}
}
MetadataReader mr = new MetadataReader ( this , br . BaseStream , HeapSizes ) ;
for ( int i = 0 ; i < 64 ; i + + )
{
if ( ( Valid & ( 1 UL < < 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 , uint offset , uint size )
{
byte [ ] buf = new byte [ size ] ;
stream . Seek ( peFile . RvaToFileOffset ( cliHeader . MetaData . VirtualAddress + 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 )
{
GetStream ( ) . Seek ( peFile . RvaToFileOffset ( ( uint ) rva ) , SeekOrigin . Begin ) ;
}
internal Stream GetStream ( )
{
if ( stream = = null )
{
throw new InvalidOperationException ( "Operation not available when UniverseOptions.MetadataOnly is enabled." ) ;
}
return stream ;
}
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 )
{
2015-08-26 07:17:56 -04:00
types . Add ( type . TypeName , type ) ;
2014-08-13 10:39:27 +01:00
}
}
// 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 ( i ) ) ;
}
}
}
}
internal override 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 ReadCompressedUInt ( 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 = ReadCompressedUInt ( 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 TokenOutOfRangeException ( metadataToken ) ;
}
if ( lazyUserStringHeap = = null )
{
lazyUserStringHeap = ReadHeap ( GetStream ( ) , userStringHeapOffset , userStringHeapSize ) ;
}
int index = metadataToken & 0xFFFFFF ;
int len = ReadCompressedUInt ( lazyUserStringHeap , ref index ) & ~ 1 ;
StringBuilder sb = new StringBuilder ( len / 2 ) ;
for ( int i = 0 ; i < len ; i + = 2 )
{
char ch = ( char ) ( lazyUserStringHeap [ index + i ] | lazyUserStringHeap [ index + i + 1 ] < < 8 ) ;
sb . Append ( ch ) ;
}
str = sb . ToString ( ) ;
strings . Add ( metadataToken , str ) ;
}
return str ;
}
internal override Type ResolveType ( int metadataToken , IGenericContext context )
{
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( index < 0 )
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
else if ( ( metadataToken > > 24 ) = = TypeDefTable . Index & & index < TypeDef . RowCount )
{
PopulateTypeDef ( ) ;
return typeDefs [ index ] ;
}
else if ( ( metadataToken > > 24 ) = = TypeRefTable . Index & & index < TypeRef . RowCount )
{
if ( typeRefs = = null )
{
typeRefs = new Type [ TypeRef . records . Length ] ;
}
if ( typeRefs [ index ] = = null )
{
int scope = TypeRef . records [ index ] . ResolutionScope ;
switch ( scope > > 24 )
{
case AssemblyRefTable . Index :
{
Assembly assembly = ResolveAssemblyRef ( ( scope & 0xFFFFFF ) - 1 ) ;
2015-08-26 07:17:56 -04:00
TypeName typeName = GetTypeName ( TypeRef . records [ index ] . TypeNamespace , TypeRef . records [ index ] . TypeName ) ;
2014-08-13 10:39:27 +01:00
typeRefs [ index ] = assembly . ResolveType ( this , typeName ) ;
break ;
}
case TypeRefTable . Index :
{
Type outer = ResolveType ( scope , null ) ;
2015-08-26 07:17:56 -04:00
TypeName typeName = GetTypeName ( TypeRef . records [ index ] . TypeNamespace , TypeRef . records [ index ] . TypeName ) ;
2014-08-13 10:39:27 +01:00
typeRefs [ index ] = outer . ResolveNestedType ( this , typeName ) ;
break ;
}
case ModuleTable . Index :
case ModuleRefTable . Index :
{
Module module ;
if ( scope > > 24 = = ModuleTable . Index )
{
if ( scope = = 0 | | scope = = 1 )
{
module = this ;
}
else
{
throw new NotImplementedException ( "self reference scope?" ) ;
}
}
else
{
module = ResolveModuleRef ( ModuleRef . records [ ( scope & 0xFFFFFF ) - 1 ] ) ;
}
2015-08-26 07:17:56 -04:00
TypeName typeName = GetTypeName ( TypeRef . records [ index ] . TypeNamespace , TypeRef . records [ index ] . TypeName ) ;
2014-08-13 10:39:27 +01:00
typeRefs [ index ] = module . FindType ( typeName ) ? ? module . universe . GetMissingTypeOrThrow ( this , module , null , typeName ) ;
break ;
}
default :
throw new NotImplementedException ( "ResolutionScope = " + scope . ToString ( "X" ) ) ;
}
}
return typeRefs [ index ] ;
}
else if ( ( metadataToken > > 24 ) = = TypeSpecTable . Index & & index < TypeSpec . RowCount )
{
if ( typeSpecs = = null )
{
typeSpecs = new Type [ TypeSpec . records . Length ] ;
}
Type type = typeSpecs [ index ] ;
if ( type = = null )
{
TrackingGenericContext tc = context = = null ? null : new TrackingGenericContext ( context ) ;
type = Signature . ReadTypeSpec ( this , ByteReader . FromBlob ( blobHeap , TypeSpec . records [ index ] ) , tc ) ;
if ( tc = = null | | ! tc . IsUsed )
{
typeSpecs [ index ] = type ;
}
}
return type ;
}
else
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
private Module ResolveModuleRef ( int moduleNameIndex )
{
string moduleName = GetString ( moduleNameIndex ) ;
Module module = assembly . GetModule ( moduleName ) ;
if ( module = = null )
{
throw new FileNotFoundException ( moduleName ) ;
}
return module ;
}
private sealed class TrackingGenericContext : IGenericContext
{
private readonly IGenericContext context ;
private bool used ;
internal TrackingGenericContext ( IGenericContext context )
{
this . context = context ;
}
internal bool IsUsed
{
get { return used ; }
}
public Type GetGenericTypeArgument ( int index )
{
used = true ;
return context . GetGenericTypeArgument ( index ) ;
}
public Type GetGenericMethodArgument ( int index )
{
used = true ;
return context . GetGenericMethodArgument ( index ) ;
}
}
private TypeName GetTypeName ( int typeNamespace , int typeName )
{
return new TypeName ( GetString ( typeNamespace ) , GetString ( typeName ) ) ;
}
internal Assembly ResolveAssemblyRef ( int index )
{
if ( assemblyRefs = = null )
{
assemblyRefs = new Assembly [ AssemblyRef . RowCount ] ;
}
if ( assemblyRefs [ index ] = = null )
{
assemblyRefs [ index ] = ResolveAssemblyRefImpl ( ref AssemblyRef . records [ index ] ) ;
}
return assemblyRefs [ index ] ;
}
private Assembly ResolveAssemblyRefImpl ( ref AssemblyRefTable . Record rec )
{
const int PublicKey = 0x0001 ;
string name = AssemblyName . GetFullName (
GetString ( rec . Name ) ,
rec . MajorVersion ,
rec . MinorVersion ,
rec . BuildNumber ,
rec . RevisionNumber ,
rec . Culture = = 0 ? "neutral" : GetString ( rec . Culture ) ,
rec . PublicKeyOrToken = = 0 ? Empty < byte > . Array : ( rec . Flags & PublicKey ) = = 0 ? GetBlobCopy ( rec . PublicKeyOrToken ) : AssemblyName . ComputePublicKeyToken ( GetBlobCopy ( rec . PublicKeyOrToken ) ) ,
rec . Flags ) ;
return universe . Load ( name , this , true ) ;
}
public override Guid ModuleVersionId
{
get
{
byte [ ] buf = new byte [ 16 ] ;
Buffer . BlockCopy ( guidHeap , 16 * ( ModuleTable . records [ 0 ] . Mvid - 1 ) , buf , 0 , 16 ) ;
return new Guid ( buf ) ;
}
}
public override string FullyQualifiedName
{
get { return location ? ? "<Unknown>" ; }
}
public override string Name
{
get { return location = = null ? "<Unknown>" : System . IO . Path . GetFileName ( location ) ; }
}
public override Assembly Assembly
{
get { return assembly ; }
}
internal override Type FindType ( TypeName typeName )
{
PopulateTypeDef ( ) ;
Type type ;
if ( ! types . TryGetValue ( typeName , out type ) )
{
LazyForwardedType fw ;
if ( forwardedTypes . TryGetValue ( typeName , out fw ) )
{
return fw . GetType ( this ) ;
}
}
return type ;
}
internal override Type FindTypeIgnoreCase ( TypeName lowerCaseName )
{
PopulateTypeDef ( ) ;
foreach ( Type type in types . Values )
{
2015-08-26 07:17:56 -04:00
if ( type . TypeName . ToLowerInvariant ( ) = = lowerCaseName )
2014-08-13 10:39:27 +01:00
{
return type ;
}
}
foreach ( TypeName name in forwardedTypes . Keys )
{
if ( name . ToLowerInvariant ( ) = = lowerCaseName )
{
return forwardedTypes [ name ] . GetType ( this ) ;
}
}
return null ;
}
private Exception TokenOutOfRangeException ( int metadataToken )
{
return new ArgumentOutOfRangeException ( "metadataToken" , String . Format ( "Token 0x{0:x8} is not valid in the scope of module {1}." , metadataToken , this . Name ) ) ;
}
public override MemberInfo ResolveMember ( int metadataToken , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments )
{
switch ( metadataToken > > 24 )
{
case FieldTable . Index :
return ResolveField ( metadataToken , genericTypeArguments , genericMethodArguments ) ;
case MemberRefTable . Index :
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( index < 0 | | index > = MemberRef . RowCount )
{
goto default ;
}
return GetMemberRef ( index , genericTypeArguments , genericMethodArguments ) ;
case MethodDefTable . Index :
case MethodSpecTable . Index :
return ResolveMethod ( metadataToken , genericTypeArguments , genericMethodArguments ) ;
case TypeRefTable . Index :
case TypeDefTable . Index :
case TypeSpecTable . Index :
return ResolveType ( metadataToken , genericTypeArguments , genericMethodArguments ) ;
default :
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
internal FieldInfo GetFieldAt ( TypeDefImpl owner , int index )
{
if ( fields = = null )
{
fields = new FieldInfo [ Field . records . Length ] ;
}
if ( fields [ index ] = = null )
{
fields [ index ] = new FieldDefImpl ( this , owner ? ? FindFieldOwner ( index ) , index ) ;
}
return fields [ index ] ;
}
public override FieldInfo ResolveField ( int metadataToken , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments )
{
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( index < 0 )
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
else if ( ( metadataToken > > 24 ) = = FieldTable . Index & & index < Field . RowCount )
{
return GetFieldAt ( null , index ) ;
}
else if ( ( metadataToken > > 24 ) = = MemberRefTable . Index & & index < MemberRef . RowCount )
{
FieldInfo field = GetMemberRef ( index , genericTypeArguments , genericMethodArguments ) as FieldInfo ;
if ( field ! = null )
{
return field ;
}
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" ) ;
}
else
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
private TypeDefImpl FindFieldOwner ( int fieldIndex )
{
// TODO use binary search?
for ( int i = 0 ; i < TypeDef . records . Length ; i + + )
{
int field = TypeDef . records [ i ] . FieldList - 1 ;
int end = TypeDef . records . Length > i + 1 ? TypeDef . records [ i + 1 ] . FieldList - 1 : Field . records . Length ;
if ( field < = fieldIndex & & fieldIndex < end )
{
PopulateTypeDef ( ) ;
return typeDefs [ i ] ;
}
}
throw new InvalidOperationException ( ) ;
}
internal MethodBase GetMethodAt ( TypeDefImpl owner , int index )
{
if ( methods = = null )
{
methods = new MethodBase [ MethodDef . records . Length ] ;
}
if ( methods [ index ] = = null )
{
MethodDefImpl method = new MethodDefImpl ( this , owner ? ? FindMethodOwner ( index ) , index ) ;
methods [ index ] = method . IsConstructor ? new ConstructorInfoImpl ( method ) : ( MethodBase ) method ;
}
return methods [ index ] ;
}
public override MethodBase ResolveMethod ( int metadataToken , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments )
{
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( index < 0 )
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
else if ( ( metadataToken > > 24 ) = = MethodDefTable . Index & & index < MethodDef . RowCount )
{
return GetMethodAt ( null , index ) ;
}
else if ( ( metadataToken > > 24 ) = = MemberRefTable . Index & & index < MemberRef . RowCount )
{
MethodBase method = GetMemberRef ( index , genericTypeArguments , genericMethodArguments ) as MethodBase ;
if ( method ! = null )
{
return method ;
}
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" ) ;
}
else if ( ( metadataToken > > 24 ) = = MethodSpecTable . Index & & index < MethodSpec . RowCount )
{
MethodInfo method = ( MethodInfo ) ResolveMethod ( MethodSpec . records [ index ] . Method , genericTypeArguments , genericMethodArguments ) ;
ByteReader instantiation = ByteReader . FromBlob ( blobHeap , MethodSpec . records [ index ] . Instantiation ) ;
return method . MakeGenericMethod ( Signature . ReadMethodSpec ( this , instantiation , new GenericContext ( genericTypeArguments , genericMethodArguments ) ) ) ;
}
else
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
public override Type [ ] __ResolveOptionalParameterTypes ( int metadataToken , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments , out CustomModifiers [ ] customModifiers )
{
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( index < 0 )
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
else if ( ( metadataToken > > 24 ) = = MemberRefTable . Index & & index < MemberRef . RowCount )
{
int sig = MemberRef . records [ index ] . Signature ;
return Signature . ReadOptionalParameterTypes ( this , GetBlob ( sig ) , new GenericContext ( genericTypeArguments , genericMethodArguments ) , out customModifiers ) ;
}
else if ( ( metadataToken > > 24 ) = = MethodDefTable . Index & & index < MethodDef . RowCount )
{
// for convenience, we support passing a MethodDef token as well, because in some places
// it makes sense to have a vararg method that is referred to by its methoddef (e.g. ldftn).
// Note that MethodSpec doesn't make sense, because generic methods cannot be vararg.
customModifiers = Empty < CustomModifiers > . Array ;
return Type . EmptyTypes ;
}
else
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
public override CustomModifiers __ResolveTypeSpecCustomModifiers ( int typeSpecToken , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments )
{
int index = ( typeSpecToken & 0xFFFFFF ) - 1 ;
if ( typeSpecToken > > 24 ! = TypeSpecTable . Index | | index < 0 | | index > = TypeSpec . RowCount )
{
throw TokenOutOfRangeException ( typeSpecToken ) ;
}
return CustomModifiers . Read ( this , ByteReader . FromBlob ( blobHeap , TypeSpec . records [ index ] ) , new GenericContext ( genericTypeArguments , genericMethodArguments ) ) ;
}
public override string ScopeName
{
get { return GetString ( ModuleTable . records [ 0 ] . Name ) ; }
}
private TypeDefImpl FindMethodOwner ( int methodIndex )
{
// TODO use binary search?
for ( int i = 0 ; i < TypeDef . records . Length ; i + + )
{
int method = TypeDef . records [ i ] . MethodList - 1 ;
int end = TypeDef . records . Length > i + 1 ? TypeDef . records [ i + 1 ] . MethodList - 1 : MethodDef . records . Length ;
if ( method < = methodIndex & & methodIndex < end )
{
PopulateTypeDef ( ) ;
return typeDefs [ i ] ;
}
}
throw new InvalidOperationException ( ) ;
}
private MemberInfo GetMemberRef ( int index , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments )
{
if ( memberRefs = = null )
{
memberRefs = new MemberInfo [ MemberRef . records . Length ] ;
}
if ( memberRefs [ index ] = = null )
{
int owner = MemberRef . records [ index ] . Class ;
int sig = MemberRef . records [ index ] . Signature ;
string name = GetString ( MemberRef . records [ index ] . Name ) ;
switch ( owner > > 24 )
{
case MethodDefTable . Index :
return GetMethodAt ( null , ( owner & 0xFFFFFF ) - 1 ) ;
case ModuleRefTable . Index :
memberRefs [ index ] = ResolveTypeMemberRef ( ResolveModuleType ( owner ) , name , ByteReader . FromBlob ( blobHeap , sig ) ) ;
break ;
case TypeDefTable . Index :
case TypeRefTable . Index :
memberRefs [ index ] = ResolveTypeMemberRef ( ResolveType ( owner ) , name , ByteReader . FromBlob ( blobHeap , sig ) ) ;
break ;
case TypeSpecTable . Index :
{
Type type = ResolveType ( owner , genericTypeArguments , genericMethodArguments ) ;
if ( type . IsArray )
{
MethodSignature methodSig = MethodSignature . ReadSig ( this , ByteReader . FromBlob ( blobHeap , sig ) , new GenericContext ( genericTypeArguments , genericMethodArguments ) ) ;
return type . FindMethod ( name , methodSig )
? ? universe . GetMissingMethodOrThrow ( this , type , name , methodSig ) ;
}
else if ( type . IsConstructedGenericType )
{
MemberInfo member = ResolveTypeMemberRef ( type . GetGenericTypeDefinition ( ) , name , ByteReader . FromBlob ( blobHeap , sig ) ) ;
MethodBase mb = member as MethodBase ;
if ( mb ! = null )
{
member = mb . BindTypeParameters ( type ) ;
}
FieldInfo fi = member as FieldInfo ;
if ( fi ! = null )
{
member = fi . BindTypeParameters ( type ) ;
}
return member ;
}
else
{
return ResolveTypeMemberRef ( type , name , ByteReader . FromBlob ( blobHeap , sig ) ) ;
}
}
default :
throw new BadImageFormatException ( ) ;
}
}
return memberRefs [ index ] ;
}
private Type ResolveModuleType ( int token )
{
int index = ( token & 0xFFFFFF ) - 1 ;
string name = GetString ( ModuleRef . records [ index ] ) ;
Module module = assembly . GetModule ( name ) ;
if ( module = = null | | module . IsResource ( ) )
{
throw new BadImageFormatException ( ) ;
}
return module . GetModuleType ( ) ;
}
private MemberInfo ResolveTypeMemberRef ( Type type , string name , ByteReader sig )
{
if ( sig . PeekByte ( ) = = Signature . FIELD )
{
Type org = type ;
FieldSignature fieldSig = FieldSignature . ReadSig ( this , sig , type ) ;
FieldInfo field = type . FindField ( name , fieldSig ) ;
if ( field = = null & & universe . MissingMemberResolution )
{
return universe . GetMissingFieldOrThrow ( this , type , name , fieldSig ) ;
}
while ( field = = null & & ( type = type . BaseType ) ! = null )
{
field = type . FindField ( name , fieldSig ) ;
}
if ( field ! = null )
{
return field ;
}
2015-04-07 09:35:12 +01:00
#if CORECLR
throw new MissingFieldException ( org . ToString ( ) + "." + name ) ;
#else
2014-08-13 10:39:27 +01:00
throw new MissingFieldException ( org . ToString ( ) , name ) ;
2015-04-07 09:35:12 +01:00
#endif
2014-08-13 10:39:27 +01:00
}
else
{
Type org = type ;
MethodSignature methodSig = MethodSignature . ReadSig ( this , sig , type ) ;
MethodBase method = type . FindMethod ( name , methodSig ) ;
if ( method = = null & & universe . MissingMemberResolution )
{
return universe . GetMissingMethodOrThrow ( this , type , name , methodSig ) ;
}
while ( method = = null & & ( type = type . BaseType ) ! = null )
{
method = type . FindMethod ( name , methodSig ) ;
}
if ( method ! = null )
{
return method ;
}
2015-04-07 09:35:12 +01:00
#if CORECLR
throw new MissingMethodException ( org . ToString ( ) + "." + name ) ;
#else
2014-08-13 10:39:27 +01:00
throw new MissingMethodException ( org . ToString ( ) , name ) ;
2015-04-07 09:35:12 +01:00
#endif
2014-08-13 10:39:27 +01:00
}
}
internal ByteReader GetStandAloneSig ( int index )
{
return ByteReader . FromBlob ( blobHeap , StandAloneSig . records [ index ] ) ;
}
public override byte [ ] ResolveSignature ( int metadataToken )
{
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( ( metadataToken > > 24 ) = = StandAloneSigTable . Index & & index > = 0 & & index < StandAloneSig . RowCount )
{
ByteReader br = GetStandAloneSig ( index ) ;
return br . ReadBytes ( br . Length ) ;
}
else
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
public override __StandAloneMethodSig __ResolveStandAloneMethodSig ( int metadataToken , Type [ ] genericTypeArguments , Type [ ] genericMethodArguments )
{
int index = ( metadataToken & 0xFFFFFF ) - 1 ;
if ( ( metadataToken > > 24 ) = = StandAloneSigTable . Index & & index > = 0 & & index < StandAloneSig . RowCount )
{
return MethodSignature . ReadStandAloneMethodSig ( this , GetStandAloneSig ( index ) , new GenericContext ( genericTypeArguments , genericMethodArguments ) ) ;
}
else
{
throw TokenOutOfRangeException ( metadataToken ) ;
}
}
internal MethodInfo GetEntryPoint ( )
{
if ( cliHeader . EntryPointToken ! = 0 & & ( cliHeader . Flags & CliHeader . COMIMAGE_FLAGS_NATIVE_ENTRYPOINT ) = = 0 )
{
return ( MethodInfo ) ResolveMethod ( ( int ) cliHeader . EntryPointToken ) ;
}
return null ;
}
internal string [ ] GetManifestResourceNames ( )
{
string [ ] names = new string [ ManifestResource . records . Length ] ;
for ( int i = 0 ; i < ManifestResource . records . Length ; i + + )
{
names [ i ] = GetString ( ManifestResource . records [ i ] . Name ) ;
}
return names ;
}
internal ManifestResourceInfo GetManifestResourceInfo ( string resourceName )
{
for ( int i = 0 ; i < ManifestResource . records . Length ; i + + )
{
if ( resourceName = = GetString ( ManifestResource . records [ i ] . Name ) )
{
ManifestResourceInfo info = new ManifestResourceInfo ( this , i ) ;
Assembly asm = info . ReferencedAssembly ;
if ( asm ! = null & & ! asm . __IsMissing & & asm . GetManifestResourceInfo ( resourceName ) = = null )
{
return null ;
}
return info ;
}
}
return null ;
}
internal Stream GetManifestResourceStream ( string resourceName )
{
for ( int i = 0 ; i < ManifestResource . records . Length ; i + + )
{
if ( resourceName = = GetString ( ManifestResource . records [ i ] . Name ) )
{
if ( ManifestResource . records [ i ] . Implementation ! = 0x26000000 )
{
ManifestResourceInfo info = new ManifestResourceInfo ( this , i ) ;
switch ( ManifestResource . records [ i ] . Implementation > > 24 )
{
case FileTable . Index :
string fileName = Path . Combine ( Path . GetDirectoryName ( location ) , info . FileName ) ;
if ( System . IO . File . Exists ( fileName ) )
{
// note that, like System.Reflection, we return null for zero length files and
// ManifestResource.Offset is ignored
FileStream fs = new FileStream ( fileName , FileMode . Open , FileAccess . Read , FileShare . Read | FileShare . Delete ) ;
if ( fs . Length = = 0 )
{
fs . Close ( ) ;
return null ;
}
return fs ;
}
return null ;
case AssemblyRefTable . Index :
Assembly asm = info . ReferencedAssembly ;
if ( asm . __IsMissing )
{
return null ;
}
return asm . GetManifestResourceStream ( resourceName ) ;
default :
throw new BadImageFormatException ( ) ;
}
}
SeekRVA ( ( int ) cliHeader . Resources . VirtualAddress + ManifestResource . records [ i ] . Offset ) ;
BinaryReader br = new BinaryReader ( stream ) ;
int length = br . ReadInt32 ( ) ;
return new MemoryStream ( br . ReadBytes ( length ) ) ;
}
}
return null ;
}
public override AssemblyName [ ] __GetReferencedAssemblies ( )
{
List < AssemblyName > list = new List < AssemblyName > ( ) ;
for ( int i = 0 ; i < AssemblyRef . records . Length ; i + + )
{
AssemblyName name = new AssemblyName ( ) ;
name . Name = GetString ( AssemblyRef . records [ i ] . Name ) ;
name . Version = new Version (
AssemblyRef . records [ i ] . MajorVersion ,
AssemblyRef . records [ i ] . MinorVersion ,
AssemblyRef . records [ i ] . BuildNumber ,
AssemblyRef . records [ i ] . RevisionNumber ) ;
if ( AssemblyRef . records [ i ] . PublicKeyOrToken ! = 0 )
{
byte [ ] keyOrToken = GetBlobCopy ( AssemblyRef . records [ i ] . PublicKeyOrToken ) ;
const int PublicKey = 0x0001 ;
if ( ( AssemblyRef . records [ i ] . Flags & PublicKey ) ! = 0 )
{
name . SetPublicKey ( keyOrToken ) ;
}
else
{
name . SetPublicKeyToken ( keyOrToken ) ;
}
}
else
{
name . SetPublicKeyToken ( Empty < byte > . Array ) ;
}
if ( AssemblyRef . records [ i ] . Culture ! = 0 )
{
name . Culture = GetString ( AssemblyRef . records [ i ] . Culture ) ;
}
else
{
name . Culture = "" ;
}
if ( AssemblyRef . records [ i ] . HashValue ! = 0 )
{
name . hash = GetBlobCopy ( AssemblyRef . records [ i ] . HashValue ) ;
}
name . RawFlags = ( AssemblyNameFlags ) AssemblyRef . records [ i ] . Flags ;
list . Add ( name ) ;
}
return list . ToArray ( ) ;
}
public override void __ResolveReferencedAssemblies ( Assembly [ ] assemblies )
{
if ( assemblyRefs = = null )
{
assemblyRefs = new Assembly [ AssemblyRef . RowCount ] ;
}
for ( int i = 0 ; i < assemblies . Length ; i + + )
{
if ( assemblyRefs [ i ] = = null )
{
assemblyRefs [ i ] = assemblies [ i ] ;
}
}
}
public override string [ ] __GetReferencedModules ( )
{
string [ ] arr = new string [ this . ModuleRef . RowCount ] ;
for ( int i = 0 ; i < arr . Length ; i + + )
{
arr [ i ] = GetString ( this . ModuleRef . records [ i ] ) ;
}
return arr ;
}
public override Type [ ] __GetReferencedTypes ( )
{
Type [ ] arr = new Type [ this . TypeRef . RowCount ] ;
for ( int i = 0 ; i < arr . Length ; i + + )
{
arr [ i ] = ResolveType ( ( TypeRefTable . Index < < 24 ) + i + 1 ) ;
}
return arr ;
}
public override Type [ ] __GetExportedTypes ( )
{
Type [ ] arr = new Type [ this . ExportedType . RowCount ] ;
for ( int i = 0 ; i < arr . Length ; i + + )
{
arr [ i ] = ResolveExportedType ( i ) ;
}
return arr ;
}
private Type ResolveExportedType ( int index )
{
TypeName typeName = GetTypeName ( ExportedType . records [ index ] . TypeNamespace , ExportedType . records [ index ] . TypeName ) ;
int implementation = ExportedType . records [ index ] . Implementation ;
int token = ExportedType . records [ index ] . TypeDefId ;
int flags = ExportedType . records [ index ] . Flags ;
switch ( implementation > > 24 )
{
case AssemblyRefTable . Index :
return ResolveAssemblyRef ( ( implementation & 0xFFFFFF ) - 1 ) . ResolveType ( this , typeName ) . SetMetadataTokenForMissing ( token , flags ) ;
case ExportedTypeTable . Index :
return ResolveExportedType ( ( implementation & 0xFFFFFF ) - 1 ) . ResolveNestedType ( this , typeName ) . SetMetadataTokenForMissing ( token , flags ) ;
case FileTable . Index :
Module module = assembly . GetModule ( GetString ( File . records [ ( implementation & 0xFFFFFF ) - 1 ] . Name ) ) ;
return module . FindType ( typeName ) ? ? module . universe . GetMissingTypeOrThrow ( this , module , null , typeName ) . SetMetadataTokenForMissing ( token , flags ) ;
default :
throw new BadImageFormatException ( ) ;
}
}
internal override Type GetModuleType ( )
{
PopulateTypeDef ( ) ;
return moduleType ;
}
public override string __ImageRuntimeVersion
{
get { return imageRuntimeVersion ; }
}
public override int MDStreamVersion
{
get { return metadataStreamVersion ; }
}
public override void __GetDataDirectoryEntry ( int index , out int rva , out int length )
{
peFile . GetDataDirectoryEntry ( index , out rva , out length ) ;
}
public override long __RelativeVirtualAddressToFileOffset ( int rva )
{
return peFile . RvaToFileOffset ( ( uint ) rva ) ;
}
public override bool __GetSectionInfo ( int rva , out string name , out int characteristics , out int virtualAddress , out int virtualSize , out int pointerToRawData , out int sizeOfRawData )
{
return peFile . GetSectionInfo ( rva , out name , out characteristics , out virtualAddress , out virtualSize , out pointerToRawData , out sizeOfRawData ) ;
}
public override int __ReadDataFromRVA ( int rva , byte [ ] data , int offset , int length )
{
SeekRVA ( rva ) ;
int totalBytesRead = 0 ;
while ( length > 0 )
{
int read = stream . Read ( data , offset , length ) ;
if ( read = = 0 )
{
// C++ assemblies can have fields that have an RVA that lies outside of the file
break ;
}
offset + = read ;
length - = read ;
totalBytesRead + = read ;
}
return totalBytesRead ;
}
public override void GetPEKind ( out PortableExecutableKinds peKind , out ImageFileMachine machine )
{
peKind = 0 ;
if ( ( cliHeader . Flags & CliHeader . COMIMAGE_FLAGS_ILONLY ) ! = 0 )
{
peKind | = PortableExecutableKinds . ILOnly ;
}
switch ( cliHeader . Flags & ( CliHeader . COMIMAGE_FLAGS_32BITREQUIRED | CliHeader . COMIMAGE_FLAGS_32BITPREFERRED ) )
{
case CliHeader . COMIMAGE_FLAGS_32BITREQUIRED :
peKind | = PortableExecutableKinds . Required32Bit ;
break ;
case CliHeader . COMIMAGE_FLAGS_32BITREQUIRED | CliHeader . COMIMAGE_FLAGS_32BITPREFERRED :
peKind | = PortableExecutableKinds . Preferred32Bit ;
break ;
default :
// COMIMAGE_FLAGS_32BITPREFERRED by itself is illegal, so we ignore it
// (not setting any flag is ok)
break ;
}
if ( peFile . OptionalHeader . Magic = = IMAGE_OPTIONAL_HEADER . IMAGE_NT_OPTIONAL_HDR64_MAGIC )
{
peKind | = PortableExecutableKinds . PE32Plus ;
}
machine = ( ImageFileMachine ) peFile . FileHeader . Machine ;
}
public override int __Subsystem
{
get { return peFile . OptionalHeader . Subsystem ; }
}
public override IList < CustomAttributeData > __GetPlaceholderAssemblyCustomAttributes ( bool multiple , bool security )
{
TypeName typeName ;
switch ( ( multiple ? 1 : 0 ) + ( security ? 2 : 0 ) )
{
case 0 :
typeName = new TypeName ( "System.Runtime.CompilerServices" , "AssemblyAttributesGoHere" ) ;
break ;
case 1 :
typeName = new TypeName ( "System.Runtime.CompilerServices" , "AssemblyAttributesGoHereM" ) ;
break ;
case 2 :
typeName = new TypeName ( "System.Runtime.CompilerServices" , "AssemblyAttributesGoHereS" ) ;
break ;
case 3 :
default :
typeName = new TypeName ( "System.Runtime.CompilerServices" , "AssemblyAttributesGoHereSM" ) ;
break ;
}
List < CustomAttributeData > list = new List < CustomAttributeData > ( ) ;
for ( int i = 0 ; i < CustomAttribute . records . Length ; i + + )
{
if ( ( CustomAttribute . records [ i ] . Parent > > 24 ) = = TypeRefTable . Index )
{
int index = ( CustomAttribute . records [ i ] . Parent & 0xFFFFFF ) - 1 ;
2015-08-26 07:17:56 -04:00
if ( typeName = = GetTypeName ( TypeRef . records [ index ] . TypeNamespace , TypeRef . records [ index ] . TypeName ) )
2014-08-13 10:39:27 +01:00
{
list . Add ( new CustomAttributeData ( this , i ) ) ;
}
}
}
return list ;
}
internal override void Dispose ( )
{
if ( stream ! = null )
{
stream . Close ( ) ;
}
}
internal override void ExportTypes ( int fileToken , IKVM . Reflection . Emit . ModuleBuilder manifestModule )
{
PopulateTypeDef ( ) ;
manifestModule . ExportTypes ( typeDefs , fileToken ) ;
}
protected override long GetImageBaseImpl ( )
{
return ( long ) peFile . OptionalHeader . ImageBase ;
}
protected override long GetStackReserveImpl ( )
{
return ( long ) peFile . OptionalHeader . SizeOfStackReserve ;
}
protected override int GetFileAlignmentImpl ( )
{
return ( int ) peFile . OptionalHeader . FileAlignment ;
}
protected override DllCharacteristics GetDllCharacteristicsImpl ( )
{
return ( DllCharacteristics ) peFile . OptionalHeader . DllCharacteristics ;
}
public override int __EntryPointRVA
{
get { return ( cliHeader . Flags & CliHeader . COMIMAGE_FLAGS_NATIVE_ENTRYPOINT ) ! = 0 ? ( int ) cliHeader . EntryPointToken : 0 ; }
}
public override int __EntryPointToken
{
get { return ( cliHeader . Flags & CliHeader . COMIMAGE_FLAGS_NATIVE_ENTRYPOINT ) = = 0 ? ( int ) cliHeader . EntryPointToken : 0 ; }
}
#if ! NO_AUTHENTICODE
public override System . Security . Cryptography . X509Certificates . X509Certificate GetSignerCertificate ( )
{
return Authenticode . GetSignerCertificate ( GetStream ( ) ) ;
}
#endif // !NO_AUTHENTICODE
}
}