/* Copyright (C) 2009-2010 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.Collections.Generic; using System.Diagnostics; using System.IO; using System.Text; using CallingConvention = System.Runtime.InteropServices.CallingConvention; using IKVM.Reflection.Reader; using IKVM.Reflection.Emit; using IKVM.Reflection.Writer; using IKVM.Reflection.Metadata; namespace IKVM.Reflection { abstract class Signature { internal const byte DEFAULT = 0x00; internal const byte VARARG = 0x05; internal const byte GENERIC = 0x10; internal const byte HASTHIS = 0x20; internal const byte EXPLICITTHIS = 0x40; internal const byte FIELD = 0x06; internal const byte LOCAL_SIG = 0x07; internal const byte PROPERTY = 0x08; internal const byte GENERICINST = 0x0A; internal const byte SENTINEL = 0x41; internal const byte ELEMENT_TYPE_VOID = 0x01; internal const byte ELEMENT_TYPE_BOOLEAN = 0x02; internal const byte ELEMENT_TYPE_CHAR = 0x03; internal const byte ELEMENT_TYPE_I1 = 0x04; internal const byte ELEMENT_TYPE_U1 = 0x05; internal const byte ELEMENT_TYPE_I2 = 0x06; internal const byte ELEMENT_TYPE_U2 = 0x07; internal const byte ELEMENT_TYPE_I4 = 0x08; internal const byte ELEMENT_TYPE_U4 = 0x09; internal const byte ELEMENT_TYPE_I8 = 0x0a; internal const byte ELEMENT_TYPE_U8 = 0x0b; internal const byte ELEMENT_TYPE_R4 = 0x0c; internal const byte ELEMENT_TYPE_R8 = 0x0d; internal const byte ELEMENT_TYPE_STRING = 0x0e; internal const byte ELEMENT_TYPE_PTR = 0x0f; internal const byte ELEMENT_TYPE_BYREF = 0x10; internal const byte ELEMENT_TYPE_VALUETYPE = 0x11; internal const byte ELEMENT_TYPE_CLASS = 0x12; internal const byte ELEMENT_TYPE_VAR = 0x13; internal const byte ELEMENT_TYPE_ARRAY = 0x14; internal const byte ELEMENT_TYPE_GENERICINST = 0x15; internal const byte ELEMENT_TYPE_TYPEDBYREF = 0x16; internal const byte ELEMENT_TYPE_I = 0x18; internal const byte ELEMENT_TYPE_U = 0x19; internal const byte ELEMENT_TYPE_FNPTR = 0x1b; internal const byte ELEMENT_TYPE_OBJECT = 0x1c; internal const byte ELEMENT_TYPE_SZARRAY = 0x1d; internal const byte ELEMENT_TYPE_MVAR = 0x1e; internal const byte ELEMENT_TYPE_CMOD_REQD = 0x1f; internal const byte ELEMENT_TYPE_CMOD_OPT = 0x20; internal const byte ELEMENT_TYPE_PINNED = 0x45; internal abstract void WriteSig(ModuleBuilder module, ByteBuffer bb); private static Type ReadGenericInst(ModuleReader module, ByteReader br, IGenericContext context) { switch (br.ReadByte()) { case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_VALUETYPE: break; default: throw new BadImageFormatException(); } Type type = ReadTypeDefOrRefEncoded(module, br, context); if (!type.IsGenericTypeDefinition) { throw new BadImageFormatException(); } int genArgCount = br.ReadCompressedInt(); Type[] args = new Type[genArgCount]; Type[][] reqmod = null; Type[][] optmod = null; for (int i = 0; i < genArgCount; i++) { // LAMESPEC the Type production (23.2.12) doesn't include CustomMod* for genericinst, but C++ uses it, the verifier allows it and ildasm also supports it CustomModifiers mods = ReadCustomModifiers(module, br, context); if (mods.required != null || mods.optional != null) { if (reqmod == null) { reqmod = new Type[genArgCount][]; optmod = new Type[genArgCount][]; } reqmod[i] = mods.required; optmod[i] = mods.optional; } args[i] = ReadType(module, br, context); } return GenericTypeInstance.Make(type, args, reqmod, optmod); } internal static Type ReadTypeSpec(ModuleReader module, ByteReader br, IGenericContext context) { // LAMESPEC a TypeSpec can contain custom modifiers (C++/CLI generates "newarr (TypeSpec with custom modifiers)") SkipCustomModifiers(br); // LAMESPEC anything can be adorned by (useless) custom modifiers // also, VAR and MVAR are also used in TypeSpec (contrary to what the spec says) return ReadType(module, br, context); } private static Type ReadFunctionPointer(ModuleReader module, ByteReader br, IGenericContext context) { // TODO like .NET we return System.IntPtr here, but ideally we should fire an event in Universe that // the user can hook to provide a custom type (or we simply should build in full support for function pointer types) MethodSignature.ReadStandAloneMethodSig(module, br, context); return module.universe.System_IntPtr; } internal static Type[] ReadMethodSpec(ModuleReader module, ByteReader br, IGenericContext context) { if (br.ReadByte() != GENERICINST) { throw new BadImageFormatException(); } Type[] args = new Type[br.ReadCompressedInt()]; for (int i = 0; i < args.Length; i++) { args[i] = ReadType(module, br, context); } return args; } private static int ReadArrayShape(ByteReader br) { int rank = br.ReadCompressedInt(); int numSizes = br.ReadCompressedInt(); for (int i = 0; i < numSizes; i++) { br.ReadCompressedInt(); } int numLoBounds = br.ReadCompressedInt(); for (int i = 0; i < numLoBounds; i++) { br.ReadCompressedInt(); } return rank; } private static Type ReadTypeOrVoid(ModuleReader module, ByteReader br, IGenericContext context) { if (br.PeekByte() == ELEMENT_TYPE_VOID) { br.ReadByte(); return module.universe.System_Void; } else { return ReadType(module, br, context); } } // see ECMA 335 CLI spec June 2006 section 23.2.12 for this production protected static Type ReadType(ModuleReader module, ByteReader br, IGenericContext context) { CustomModifiers mods; switch (br.ReadByte()) { case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_VALUETYPE: return ReadTypeDefOrRefEncoded(module, br, context); case ELEMENT_TYPE_BOOLEAN: return module.universe.System_Boolean; case ELEMENT_TYPE_CHAR: return module.universe.System_Char; case ELEMENT_TYPE_I1: return module.universe.System_SByte; case ELEMENT_TYPE_U1: return module.universe.System_Byte; case ELEMENT_TYPE_I2: return module.universe.System_Int16; case ELEMENT_TYPE_U2: return module.universe.System_UInt16; case ELEMENT_TYPE_I4: return module.universe.System_Int32; case ELEMENT_TYPE_U4: return module.universe.System_UInt32; case ELEMENT_TYPE_I8: return module.universe.System_Int64; case ELEMENT_TYPE_U8: return module.universe.System_UInt64; case ELEMENT_TYPE_R4: return module.universe.System_Single; case ELEMENT_TYPE_R8: return module.universe.System_Double; case ELEMENT_TYPE_I: return module.universe.System_IntPtr; case ELEMENT_TYPE_U: return module.universe.System_UIntPtr; case ELEMENT_TYPE_STRING: return module.universe.System_String; case ELEMENT_TYPE_OBJECT: return module.universe.System_Object; case ELEMENT_TYPE_VAR: return context.GetGenericTypeArgument(br.ReadCompressedInt()); case ELEMENT_TYPE_MVAR: return context.GetGenericMethodArgument(br.ReadCompressedInt()); case ELEMENT_TYPE_GENERICINST: return ReadGenericInst(module, br, context); case ELEMENT_TYPE_SZARRAY: mods = ReadCustomModifiers(module, br, context); return ReadType(module, br, context).__MakeArrayType(mods.required, mods.optional); case ELEMENT_TYPE_ARRAY: mods = ReadCustomModifiers(module, br, context); return ReadType(module, br, context).__MakeArrayType(ReadArrayShape(br), mods.required, mods.optional); case ELEMENT_TYPE_PTR: mods = ReadCustomModifiers(module, br, context); return ReadTypeOrVoid(module, br, context).__MakePointerType(mods.required, mods.optional); case ELEMENT_TYPE_FNPTR: return ReadFunctionPointer(module, br, context); default: throw new BadImageFormatException(); } } internal static void ReadLocalVarSig(ModuleReader module, ByteReader br, IGenericContext context, List list) { if (br.Length < 2 || br.ReadByte() != LOCAL_SIG) { throw new BadImageFormatException("Invalid local variable signature"); } int count = br.ReadCompressedInt(); for (int i = 0; i < count; i++) { if (br.PeekByte() == ELEMENT_TYPE_TYPEDBYREF) { br.ReadByte(); list.Add(new LocalVariableInfo(i, module.universe.System_TypedReference, false)); } else { SkipCustomModifiers(br); bool pinned = false; if (br.PeekByte() == ELEMENT_TYPE_PINNED) { br.ReadByte(); pinned = true; } SkipCustomModifiers(br); Type type = ReadTypeOrByRef(module, br, context); list.Add(new LocalVariableInfo(i, type, pinned)); } } } private static Type ReadTypeOrByRef(ModuleReader module, ByteReader br, IGenericContext context) { if (br.PeekByte() == ELEMENT_TYPE_BYREF) { br.ReadByte(); // LAMESPEC it is allowed (by C++/CLI, ilasm and peverify) to have custom modifiers after the BYREF // (which makes sense, as it is analogous to pointers) CustomModifiers mods = ReadCustomModifiers(module, br, context); // C++/CLI generates void& local variables, so we need to use ReadTypeOrVoid here return ReadTypeOrVoid(module, br, context).__MakeByRefType(mods.required, mods.optional); } else { return ReadType(module, br, context); } } protected static Type ReadRetType(ModuleReader module, ByteReader br, IGenericContext context) { switch (br.PeekByte()) { case ELEMENT_TYPE_VOID: br.ReadByte(); return module.universe.System_Void; case ELEMENT_TYPE_TYPEDBYREF: br.ReadByte(); return module.universe.System_TypedReference; default: return ReadTypeOrByRef(module, br, context); } } protected static Type ReadParam(ModuleReader module, ByteReader br, IGenericContext context) { switch (br.PeekByte()) { case ELEMENT_TYPE_TYPEDBYREF: br.ReadByte(); return module.universe.System_TypedReference; default: return ReadTypeOrByRef(module, br, context); } } protected static void WriteType(ModuleBuilder module, ByteBuffer bb, Type type) { while (type.HasElementType) { if (type.__IsVector) { bb.Write(ELEMENT_TYPE_SZARRAY); } else if (type.IsArray) { int rank = type.GetArrayRank(); bb.Write(ELEMENT_TYPE_ARRAY); // LAMESPEC the Type production (23.2.12) doesn't include CustomMod* for arrays, but the verifier allows it and ildasm also supports it WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_REQD, type.__GetRequiredCustomModifiers()); WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_OPT, type.__GetOptionalCustomModifiers()); WriteType(module, bb, type.GetElementType()); bb.WriteCompressedInt(rank); // since a Type doesn't contain the lower/upper bounds // (they act like a custom modifier, so they are part of the signature, but not of the Type), // we set them to the C# compatible values and hope for the best bb.WriteCompressedInt(0); // boundsCount bb.WriteCompressedInt(rank); // loCount for (int i = 0; i < rank; i++) { bb.WriteCompressedInt(0); } return; } else if (type.IsByRef) { bb.Write(ELEMENT_TYPE_BYREF); } else if (type.IsPointer) { bb.Write(ELEMENT_TYPE_PTR); } WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_REQD, type.__GetRequiredCustomModifiers()); WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_OPT, type.__GetOptionalCustomModifiers()); type = type.GetElementType(); } Universe u = module.universe; if (type == u.System_Void) { bb.Write(ELEMENT_TYPE_VOID); } else if (type == u.System_Boolean) { bb.Write(ELEMENT_TYPE_BOOLEAN); } else if (type == u.System_Char) { bb.Write(ELEMENT_TYPE_CHAR); } else if (type == u.System_SByte) { bb.Write(ELEMENT_TYPE_I1); } else if (type == u.System_Byte) { bb.Write(ELEMENT_TYPE_U1); } else if (type == u.System_Int16) { bb.Write(ELEMENT_TYPE_I2); } else if (type == u.System_UInt16) { bb.Write(ELEMENT_TYPE_U2); } else if (type == u.System_Int32) { bb.Write(ELEMENT_TYPE_I4); } else if (type == u.System_UInt32) { bb.Write(ELEMENT_TYPE_U4); } else if (type == u.System_Int64) { bb.Write(ELEMENT_TYPE_I8); } else if (type == u.System_UInt64) { bb.Write(ELEMENT_TYPE_U8); } else if (type == u.System_Single) { bb.Write(ELEMENT_TYPE_R4); } else if (type == u.System_Double) { bb.Write(ELEMENT_TYPE_R8); } else if (type == u.System_String) { bb.Write(ELEMENT_TYPE_STRING); } else if (type == u.System_IntPtr) { bb.Write(ELEMENT_TYPE_I); } else if (type == u.System_UIntPtr) { bb.Write(ELEMENT_TYPE_U); } else if (type == u.System_TypedReference) { bb.Write(ELEMENT_TYPE_TYPEDBYREF); } else if (type == u.System_Object) { bb.Write(ELEMENT_TYPE_OBJECT); } else if (type.IsGenericParameter) { if (type is UnboundGenericMethodParameter || type.DeclaringMethod != null) { bb.Write(ELEMENT_TYPE_MVAR); } else { bb.Write(ELEMENT_TYPE_VAR); } bb.WriteCompressedInt(type.GenericParameterPosition); } else if (type.IsGenericType) { WriteGenericSignature(module, bb, type); } else { if (type.IsValueType) { bb.Write(ELEMENT_TYPE_VALUETYPE); } else { bb.Write(ELEMENT_TYPE_CLASS); } bb.WriteTypeDefOrRefEncoded(module.GetTypeToken(type).Token); } } private static void WriteGenericSignature(ModuleBuilder module, ByteBuffer bb, Type type) { Type[] typeArguments = type.GetGenericArguments(); Type[][] requiredCustomModifiers = type.__GetGenericArgumentsRequiredCustomModifiers(); Type[][] optionalCustomModifiers = type.__GetGenericArgumentsOptionalCustomModifiers(); if (!type.IsGenericTypeDefinition) { type = type.GetGenericTypeDefinition(); } bb.Write(ELEMENT_TYPE_GENERICINST); if (type.IsValueType) { bb.Write(ELEMENT_TYPE_VALUETYPE); } else { bb.Write(ELEMENT_TYPE_CLASS); } bb.WriteTypeDefOrRefEncoded(module.GetTypeToken(type).Token); bb.WriteCompressedInt(typeArguments.Length); for (int i = 0; i < typeArguments.Length; i++) { WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_REQD, requiredCustomModifiers[i]); WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_OPT, optionalCustomModifiers[i]); WriteType(module, bb, typeArguments[i]); } } protected static void WriteCustomModifiers(ModuleBuilder module, ByteBuffer bb, byte mod, Type[] modifiers) { if (modifiers != null) { foreach (Type type in modifiers) { bb.Write(mod); bb.WriteTypeDefOrRefEncoded(module.GetTypeTokenForMemberRef(type)); } } } protected static bool IsCustomModifier(byte b) { return b == ELEMENT_TYPE_CMOD_OPT || b == ELEMENT_TYPE_CMOD_REQD; } struct CustomModifiers { internal Type[] required; internal Type[] optional; } private static CustomModifiers ReadCustomModifiers(ModuleReader module, ByteReader br, IGenericContext context) { CustomModifiers mods = new CustomModifiers(); byte b = br.PeekByte(); if (IsCustomModifier(b)) { List required = new List(); List optional = new List(); while (IsCustomModifier(b)) { bool req = br.ReadByte() == ELEMENT_TYPE_CMOD_REQD; Type type = ReadTypeDefOrRefEncoded(module, br, context); (req ? required : optional).Add(type); b = br.PeekByte(); } mods.required = required.ToArray(); mods.optional = optional.ToArray(); } return mods; } protected static void SkipCustomModifiers(ByteReader br) { byte b = br.PeekByte(); while (IsCustomModifier(b)) { br.ReadByte(); br.ReadCompressedInt(); b = br.PeekByte(); } } private static Type ReadTypeDefOrRefEncoded(ModuleReader module, ByteReader br, IGenericContext context) { int encoded = br.ReadCompressedInt(); switch (encoded & 3) { case 0: return module.ResolveType((TypeDefTable.Index << 24) + (encoded >> 2), null, null); case 1: return module.ResolveType((TypeRefTable.Index << 24) + (encoded >> 2), null, null); case 2: return module.ResolveType((TypeSpecTable.Index << 24) + (encoded >> 2), context); default: throw new BadImageFormatException(); } } protected static void ReadCustomModifiers(ModuleReader module, ByteReader br, IGenericContext context, out Type[] requiredCustomModifiers, out Type[] optionalCustomModifiers) { byte b = br.PeekByte(); if (IsCustomModifier(b)) { List required = new List(); List optional = new List(); while (IsCustomModifier(b)) { br.ReadByte(); Type type = ReadTypeDefOrRefEncoded(module, br, context); if (b == ELEMENT_TYPE_CMOD_REQD) { required.Add(type); } else { optional.Add(type); } b = br.PeekByte(); } requiredCustomModifiers = required.ToArray(); optionalCustomModifiers = optional.ToArray(); } else { requiredCustomModifiers = null; optionalCustomModifiers = null; } } // unmanaged calling convention internal static void WriteStandAloneMethodSig(ModuleBuilder module, ByteBuffer bb, CallingConvention callingConvention, Type returnType, Type[] parameterTypes) { switch (callingConvention) { case CallingConvention.Cdecl: bb.Write((byte)0x01); // C break; case CallingConvention.StdCall: case CallingConvention.Winapi: bb.Write((byte)0x02); // STDCALL break; case CallingConvention.ThisCall: bb.Write((byte)0x03); // THISCALL break; case CallingConvention.FastCall: bb.Write((byte)0x04); // FASTCALL break; default: throw new ArgumentOutOfRangeException("callingConvention"); } bb.WriteCompressedInt(parameterTypes.Length); WriteType(module, bb, returnType); foreach (Type t in parameterTypes) { WriteType(module, bb, t); } } // managed calling convention internal static void WriteStandAloneMethodSig(ModuleBuilder module, ByteBuffer bb, CallingConventions callingConvention, Type returnType, Type[] parameterTypes, Type[] optionalParameterTypes) { byte flags = 0; if ((callingConvention & CallingConventions.HasThis) != 0) { flags |= HASTHIS; } if ((callingConvention & CallingConventions.ExplicitThis) != 0) { flags |= EXPLICITTHIS; } if ((callingConvention & CallingConventions.VarArgs) != 0) { flags |= VARARG; } bb.Write(flags); bb.WriteCompressedInt(parameterTypes.Length + optionalParameterTypes.Length); WriteType(module, bb, returnType); foreach (Type t in parameterTypes) { WriteType(module, bb, t); } if (optionalParameterTypes.Length > 0) { bb.Write(SENTINEL); foreach (Type t in optionalParameterTypes) { WriteType(module, bb, t); } } } internal static void WriteLocalVarSig(ModuleBuilder module, ByteBuffer bb, IList locals) { bb.Write(LOCAL_SIG); bb.WriteCompressedInt(locals.Count); foreach (LocalBuilder local in locals) { if (local.IsPinned) { bb.Write(ELEMENT_TYPE_PINNED); } WriteType(module, bb, local.LocalType); } } internal static void WritePropertySig(ModuleBuilder module, ByteBuffer bb, CallingConventions callingConvention, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers) { byte flags = PROPERTY; if ((callingConvention & CallingConventions.HasThis) != 0) { flags |= HASTHIS; } if ((callingConvention & CallingConventions.ExplicitThis) != 0) { flags |= EXPLICITTHIS; } if ((callingConvention & CallingConventions.VarArgs) != 0) { flags |= VARARG; } bb.Write(flags); bb.WriteCompressedInt(parameterTypes == null ? 0 : parameterTypes.Length); WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_REQD, returnTypeRequiredCustomModifiers); WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_OPT, returnTypeOptionalCustomModifiers); WriteType(module, bb, returnType); if (parameterTypes != null) { for (int i = 0; i < parameterTypes.Length; i++) { if (parameterTypeRequiredCustomModifiers != null) { WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_REQD, parameterTypeRequiredCustomModifiers[i]); } if (parameterTypeOptionalCustomModifiers != null) { WriteCustomModifiers(module, bb, ELEMENT_TYPE_CMOD_OPT, parameterTypeOptionalCustomModifiers[i]); } WriteType(module, bb, parameterTypes[i]); } } } internal static void WriteTypeSpec(ModuleBuilder module, ByteBuffer bb, Type type) { WriteType(module, bb, type); } internal static void WriteMethodSpec(ModuleBuilder module, ByteBuffer bb, Type[] genArgs) { bb.Write(GENERICINST); bb.WriteCompressedInt(genArgs.Length); foreach (Type arg in genArgs) { WriteType(module, bb, arg); } } // this reads just the optional parameter types, from a MethodRefSig internal static Type[] ReadOptionalParameterTypes(ModuleReader module, ByteReader br) { br.ReadByte(); int paramCount = br.ReadCompressedInt(); SkipCustomModifiers(br); ReadRetType(module, br, null); for (int i = 0; i < paramCount; i++) { if (br.PeekByte() == SENTINEL) { br.ReadByte(); Type[] types = new Type[paramCount - i]; for (int j = 0; j < types.Length; j++) { SkipCustomModifiers(br); types[j] = ReadType(module, br, null); } return types; } SkipCustomModifiers(br); ReadType(module, br, null); } return Type.EmptyTypes; } protected static Type[] BindTypeParameters(IGenericBinder binder, Type[] types) { if (types == null || types.Length == 0) { return Type.EmptyTypes; } Type[] expanded = new Type[types.Length]; for (int i = 0; i < types.Length; i++) { expanded[i] = types[i].BindTypeParameters(binder); } return expanded; } protected static Type[][] BindTypeParameters(IGenericBinder binder, Type[][] types) { if (types == null) { return null; } Type[][] expanded = new Type[types.Length][]; for (int i = 0; i < types.Length; i++) { expanded[i] = BindTypeParameters(binder, types[i]); } return expanded; } protected static Type[][][] BindTypeParameters(IGenericBinder binder, Type[][][] types) { if (types == null) { return null; } Type[][][] expanded = new Type[types.Length][][]; for (int i = 0; i < types.Length; i++) { expanded[i] = BindTypeParameters(binder, types[i]); } return expanded; } protected static Type[] BindTypeParameters(IGenericBinder binder, Type[][][] types, int index, int optOrReq) { if (types == null || types[index] == null) { return null; } return BindTypeParameters(binder, types[index][optOrReq]); } } }