ikvm-fork/refemit/SignatureHelper.cs

378 строки
12 KiB
C#
Исходник Обычный вид История

2008-11-14 10:40:49 +03:00
/*
Copyright (C) 2008 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.Text;
using System.Reflection;
using System.Diagnostics;
using System.Runtime.InteropServices;
using IKVM.Reflection.Emit.Writer;
namespace IKVM.Reflection.Emit
{
public class SignatureHelper
{
internal const byte DEFAULT = 0x00;
internal const byte GENERIC = 0x10;
internal const byte HASTHIS = 0x20;
internal const byte FIELD = 0x06;
internal const byte PROPERTY = 0x08;
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;
2008-11-14 10:40:49 +03:00
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;
2008-11-14 10:40:49 +03:00
internal const byte ELEMENT_TYPE_GENERICINST = 0x15;
internal const byte ELEMENT_TYPE_I = 0x18;
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 = 0x020;
private enum GenericParameterType
{
None,
Method,
Type
}
internal static void WriteType(ModuleBuilder moduleBuilder, ByteBuffer bb, Type type)
{
WriteType(moduleBuilder, bb, type, GenericParameterType.None);
}
private static void WriteType(ModuleBuilder moduleBuilder, ByteBuffer bb, Type type, GenericParameterType genericParameter)
{
while (type.HasElementType)
2008-11-14 10:40:49 +03:00
{
if (type.IsArray)
2008-11-14 10:40:49 +03:00
{
// we look at the Type.Name property, because for arrays of generic parameters (T[]) the FullName returns null
if (type.Name.EndsWith("[]", StringComparison.Ordinal))
{
bb.Write(ELEMENT_TYPE_SZARRAY);
}
else
{
int rank = type.GetArrayRank();
bb.Write(ELEMENT_TYPE_ARRAY);
WriteType(moduleBuilder, bb, type.GetElementType(), genericParameter);
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);
2008-11-14 10:40:49 +03:00
}
type = type.GetElementType();
}
if (type == typeof(void))
{
bb.Write(ELEMENT_TYPE_VOID);
}
else if (type == typeof(bool))
{
bb.Write(ELEMENT_TYPE_BOOLEAN);
}
else if (type == typeof(char))
{
bb.Write(ELEMENT_TYPE_CHAR);
}
else if (type == typeof(sbyte))
{
bb.Write(ELEMENT_TYPE_I1);
}
else if (type == typeof(byte))
{
bb.Write(ELEMENT_TYPE_U1);
}
else if (type == typeof(short))
{
bb.Write(ELEMENT_TYPE_I2);
}
else if (type == typeof(ushort))
{
bb.Write(ELEMENT_TYPE_U2);
}
else if (type == typeof(int))
{
bb.Write(ELEMENT_TYPE_I4);
}
else if (type == typeof(uint))
{
bb.Write(ELEMENT_TYPE_U4);
}
else if (type == typeof(long))
{
bb.Write(ELEMENT_TYPE_I8);
}
else if (type == typeof(ulong))
{
bb.Write(ELEMENT_TYPE_U8);
}
else if (type == typeof(float))
{
bb.Write(ELEMENT_TYPE_R4);
}
else if (type == typeof(double))
{
bb.Write(ELEMENT_TYPE_R8);
}
else if (type == typeof(string))
{
bb.Write(ELEMENT_TYPE_STRING);
}
else if (type == typeof(IntPtr))
{
bb.Write(ELEMENT_TYPE_I);
}
else if (type == typeof(object))
{
bb.Write(ELEMENT_TYPE_OBJECT);
}
else if (type.IsGenericParameter)
{
switch (genericParameter)
{
case GenericParameterType.Type:
bb.Write(ELEMENT_TYPE_VAR);
bb.WriteCompressedInt(type.GenericParameterPosition);
break;
case GenericParameterType.Method:
bb.Write(ELEMENT_TYPE_MVAR);
bb.WriteCompressedInt(type.GenericParameterPosition);
break;
default:
throw new InvalidOperationException();
}
}
else if (type.IsGenericType && !type.IsGenericTypeDefinition)
{
WriteGenericSignature(moduleBuilder, bb, type.GetGenericTypeDefinition(), type.GetGenericArguments());
}
else if (!type.IsPrimitive)
{
if (type.IsValueType)
{
bb.Write(ELEMENT_TYPE_VALUETYPE);
}
else
{
bb.Write(ELEMENT_TYPE_CLASS);
}
bb.WriteTypeDefOrRefEncoded(moduleBuilder.GetTypeToken(type).Token);
}
else
{
throw new NotImplementedException(type.FullName);
}
}
internal static void WriteCustomModifiers(ModuleBuilder moduleBuilder, ByteBuffer bb, byte mod, Type[] modifiers)
{
if (modifiers != null)
{
foreach (Type type in modifiers)
{
bb.Write(mod);
bb.WriteTypeDefOrRefEncoded(moduleBuilder.GetTypeToken(type).Token);
}
}
}
internal static void WriteFieldSig(ModuleBuilder moduleBuilder, ByteBuffer bb, Type fieldType, Type[] requiredCustomModifiers, Type[] optionalCustomModifiers)
{
bb.Write(FIELD);
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_REQD, requiredCustomModifiers);
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_OPT, optionalCustomModifiers);
WriteType(moduleBuilder, bb, fieldType);
}
internal static void WriteMethodSig(ModuleBuilder moduleBuilder, ByteBuffer bb, CallingConventions callingConvention, Type returnType, Type[] returnTypeRequiredCustomModifiers, Type[] returnTypeOptionalCustomModifiers, Type[] parameterTypes, Type[][] parameterTypeRequiredCustomModifiers, Type[][] parameterTypeOptionalCustomModifiers)
{
if ((callingConvention & ~CallingConventions.HasThis) != CallingConventions.Standard)
{
throw new NotImplementedException();
}
byte first = DEFAULT;
if ((callingConvention & CallingConventions.HasThis) != 0)
{
first |= HASTHIS;
}
parameterTypes = parameterTypes ?? Type.EmptyTypes;
bb.Write(first);
bb.WriteCompressedInt(parameterTypes.Length);
// RetType
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_REQD, returnTypeRequiredCustomModifiers);
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_OPT, returnTypeOptionalCustomModifiers);
WriteType(moduleBuilder, bb, returnType ?? typeof(void));
// Param
for (int i = 0; i < parameterTypes.Length; i++)
{
if (parameterTypeRequiredCustomModifiers != null && parameterTypeRequiredCustomModifiers.Length > i)
{
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_REQD, parameterTypeRequiredCustomModifiers[i]);
}
if (parameterTypeOptionalCustomModifiers != null && parameterTypeOptionalCustomModifiers.Length > i)
{
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_OPT, parameterTypeOptionalCustomModifiers[i]);
}
WriteType(moduleBuilder, bb, parameterTypes[i]);
}
}
private static MethodBase GetMethodOnTypeDefinition(MethodBase method)
{
Type type = method.DeclaringType;
if (type != null && type.IsGenericType && !type.IsGenericTypeDefinition)
{
// this trick allows us to go from the method on the generic type instance, to the equivalent method on the generic type definition
method = method.Module.ResolveMethod(method.MetadataToken);
}
return method;
}
internal static void WriteMethodSig(ModuleBuilder moduleBuilder, ByteBuffer bb, MethodBase methodOnTypeInstance)
{
Debug.Assert(!methodOnTypeInstance.IsGenericMethod || methodOnTypeInstance.IsGenericMethodDefinition);
MethodBase method = GetMethodOnTypeDefinition(methodOnTypeInstance);
ParameterInfo returnParameter = null;
ParameterInfo[] parameters = method.GetParameters();
if (method is MethodInfo)
{
returnParameter = ((MethodInfo)method).ReturnParameter;
}
bool methodIsGeneric = false;
ParameterInfo methodOnTypeInstanceReturnParameter = returnParameter;
ParameterInfo[] methodOnTypeInstanceParameters = parameters;
if (methodOnTypeInstance.IsGenericMethodDefinition)
{
methodIsGeneric = true;
methodOnTypeInstanceReturnParameter = ((MethodInfo)methodOnTypeInstance).ReturnParameter;
methodOnTypeInstanceParameters = methodOnTypeInstance.GetParameters();
}
byte first = DEFAULT;
if (!method.IsStatic)
{
first |= HASTHIS;
}
if (method.IsGenericMethod)
{
first |= GENERIC;
}
bb.Write(first);
if (method.IsGenericMethod)
{
bb.WriteCompressedInt(method.GetGenericArguments().Length);
}
bb.WriteCompressedInt(parameters.Length);
// RetType
if (returnParameter == null)
{
WriteType(moduleBuilder, bb, typeof(void));
}
else
{
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_REQD, returnParameter.GetRequiredCustomModifiers());
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_OPT, returnParameter.GetOptionalCustomModifiers());
WriteMethodParameterType(moduleBuilder, bb, returnParameter.ParameterType, methodOnTypeInstanceReturnParameter.ParameterType, methodIsGeneric);
}
// Param
for (int i = 0; i < parameters.Length; i++)
{
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_REQD, parameters[i].GetRequiredCustomModifiers());
WriteCustomModifiers(moduleBuilder, bb, ELEMENT_TYPE_CMOD_OPT, parameters[i].GetOptionalCustomModifiers());
WriteMethodParameterType(moduleBuilder, bb, parameters[i].ParameterType, methodOnTypeInstanceParameters[i].ParameterType, methodIsGeneric);
}
}
private static bool IsGenericParameter(Type type)
{
while (type.HasElementType)
{
type = type.GetElementType();
}
return type.IsGenericParameter;
}
private static void WriteMethodParameterType(ModuleBuilder moduleBuilder, ByteBuffer bb, Type type1, Type type2, bool methodIsGeneric)
{
if (methodIsGeneric && IsGenericParameter(type2))
{
WriteType(moduleBuilder, bb, type1, GenericParameterType.Method);
}
else if (IsGenericParameter(type1))
{
WriteType(moduleBuilder, bb, type1, GenericParameterType.Type);
}
else
{
WriteType(moduleBuilder, bb, type1);
}
}
internal static void WriteGenericSignature(ModuleBuilder moduleBuilder, ByteBuffer bb, Type type, Type[] typeArguments)
{
Debug.Assert(type.IsGenericTypeDefinition);
bb.Write(ELEMENT_TYPE_GENERICINST);
WriteType(moduleBuilder, bb, type);
bb.WriteCompressedInt(typeArguments.Length);
foreach (Type t in typeArguments)
{
WriteType(moduleBuilder, bb, t);
}
}
}
}