Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

432 lines
13 KiB
C#

//
// ReflectionHelper.cs
//
// Author:
// Jb Evain (jbevain@gmail.com)
//
// (C) 2005 Jb Evain
// (C) 2006 Evaluant RC S.A.
// (C) 2007 Jb Evain
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
namespace Mono.Cecil {
using System;
using System.Collections;
using SR = System.Reflection;
using System.Text;
internal sealed class ReflectionHelper {
ModuleDefinition m_module;
public ReflectionHelper (ModuleDefinition module)
{
m_module = module;
}
public AssemblyNameReference ImportAssembly (SR.Assembly asm)
{
AssemblyNameReference asmRef = GetAssemblyNameReference (asm.GetName ());
if (asmRef != null)
return asmRef;
SR.AssemblyName asmName = asm.GetName ();
asmRef = new AssemblyNameReference (
asmName.Name, asmName.CultureInfo.Name, asmName.Version);
asmRef.PublicKeyToken = asmName.GetPublicKeyToken ();
asmRef.HashAlgorithm = (AssemblyHashAlgorithm) asmName.HashAlgorithm;
asmRef.Culture = asmName.CultureInfo.ToString ();
m_module.AssemblyReferences.Add (asmRef);
return asmRef;
}
AssemblyNameReference GetAssemblyNameReference (SR.AssemblyName name)
{
foreach (AssemblyNameReference reference in m_module.AssemblyReferences)
if (reference.FullName == name.FullName)
return reference;
return null;
}
public static string GetTypeSignature (Type t)
{
if (t.HasElementType) {
if (t.IsPointer)
return string.Concat (GetTypeSignature (t.GetElementType ()), "*");
else if (t.IsArray) {
int rank = t.GetArrayRank ();
if (rank == 1)
return string.Concat (GetTypeSignature (t.GetElementType ()), "[]");
StringBuilder sb = new StringBuilder ();
sb.Append ('[');
for (int i = 1; i < rank; i++)
sb.Append (',');
sb.Append (']');
return string.Concat (GetTypeSignature (t.GetElementType ()), sb.ToString ());
} else if (t.IsByRef)
return string.Concat(GetTypeSignature(t.GetElementType()), "&");
}
if (IsGenericTypeSpec (t)) {
StringBuilder sb = new StringBuilder ();
sb.Append (GetTypeSignature (GetGenericTypeDefinition (t)));
sb.Append ("<");
Type [] genArgs = GetGenericArguments (t);
for (int i = 0; i < genArgs.Length; i++) {
if (i > 0)
sb.Append (",");
sb.Append (GetTypeSignature (genArgs [i]));
}
sb.Append (">");
return sb.ToString ();
}
if (IsGenericParameter (t))
return t.Name;
if (t.DeclaringType != null)
return string.Concat (t.DeclaringType.FullName, "/", t.Name);
if (t.Namespace == null || t.Namespace.Length == 0)
return t.Name;
return string.Concat (t.Namespace, ".", t.Name);
}
static bool GetProperty (object o, string prop)
{
SR.PropertyInfo pi = o.GetType ().GetProperty (prop);
if (pi == null)
return false;
return (bool) pi.GetValue (o, null);
}
public static bool IsGenericType (Type t)
{
return GetProperty (t, "IsGenericType");
}
static bool IsGenericParameter (Type t)
{
return GetProperty (t, "IsGenericParameter");
}
static bool IsGenericTypeDefinition (Type t)
{
return GetProperty (t, "IsGenericTypeDefinition");
}
static bool IsGenericTypeSpec (Type t)
{
return IsGenericType (t) && !IsGenericTypeDefinition (t);
}
static Type GetGenericTypeDefinition (Type t)
{
return (Type) t.GetType ().GetMethod ("GetGenericTypeDefinition").Invoke (t, null);
}
static Type [] GetGenericArguments (Type t)
{
return (Type []) t.GetType ().GetMethod ("GetGenericArguments").Invoke (t, null);
}
GenericInstanceType GetGenericType (Type t, TypeReference element, ImportContext context)
{
GenericInstanceType git = new GenericInstanceType (element);
foreach (Type genArg in GetGenericArguments (t))
git.GenericArguments.Add (ImportSystemType (genArg, context));
return git;
}
static bool GenericParameterOfMethod (Type t)
{
return t.GetType ().GetProperty ("DeclaringMethod").GetValue (t, null) != null;
}
static GenericParameter GetGenericParameter (Type t, ImportContext context)
{
int pos = (int) t.GetType ().GetProperty ("GenericParameterPosition").GetValue (t, null);
IGenericParameterProvider provider;
if (GenericParameterOfMethod (t))
provider = context.GenericContext.Method;
else
provider = context.GenericContext.Type;
if (provider == null)
throw new InvalidOperationException ("Invalid context");
return provider.GenericParameters [pos];
}
TypeReference GetTypeSpec (Type t, ImportContext context)
{
Stack s = new Stack ();
while (t.HasElementType || IsGenericTypeSpec (t)) {
s.Push (t);
if (t.HasElementType)
t = t.GetElementType ();
else if (IsGenericTypeSpec (t)) {
t = (Type) t.GetType ().GetMethod ("GetGenericTypeDefinition").Invoke (t, null);
break;
}
}
TypeReference elementType = ImportSystemType (t, context);
while (s.Count > 0) {
t = (Type) s.Pop ();
if (t.IsPointer)
elementType = new PointerType (elementType);
else if (t.IsArray)
elementType = new ArrayType (elementType, t.GetArrayRank ());
else if (t.IsByRef)
elementType = new ReferenceType (elementType);
else if (IsGenericTypeSpec (t))
elementType = GetGenericType (t, elementType, context);
else
throw new ReflectionException ("Unknown element type");
}
return elementType;
}
TypeReference AdjustReference (Type type, TypeReference reference)
{
if (type.IsValueType && !reference.IsValueType)
reference.IsValueType = true;
if (IsGenericTypeDefinition (type)) {
Type [] parameters = GetGenericArguments (type);
for (int i = reference.GenericParameters.Count; i < parameters.Length; i++)
reference.GenericParameters.Add (new GenericParameter (i, reference));
}
return reference;
}
public TypeReference ImportSystemType (Type t, ImportContext context)
{
if (t.HasElementType || IsGenericTypeSpec (t))
return GetTypeSpec (t, context);
if (IsGenericParameter (t))
return GetGenericParameter (t, context);
TypeReference type = m_module.TypeReferences [GetTypeSignature (t)];
if (type != null)
return AdjustReference (t, type);
AssemblyNameReference asm = ImportAssembly (t.Assembly);
if (t.DeclaringType != null) {
type = new TypeReference (t.Name, string.Empty, asm, t.IsValueType);
type.DeclaringType = ImportSystemType (t.DeclaringType, context);
} else
type = new TypeReference (t.Name, t.Namespace, asm, t.IsValueType);
if (IsGenericTypeDefinition (t))
foreach (Type genParam in GetGenericArguments (t))
type.GenericParameters.Add (new GenericParameter (genParam.Name, type));
context.GenericContext.Type = type;
m_module.TypeReferences.Add (type);
return type;
}
static string GetMethodBaseSignature (SR.MethodBase meth, Type declaringType, Type retType)
{
StringBuilder sb = new StringBuilder ();
sb.Append (GetTypeSignature (retType));
sb.Append (' ');
sb.Append (GetTypeSignature (declaringType));
sb.Append ("::");
sb.Append (meth.Name);
if (IsGenericMethodSpec (meth)) {
sb.Append ("<");
Type [] genArgs = GetGenericArguments (meth as SR.MethodInfo);
for (int i = 0; i < genArgs.Length; i++) {
if (i > 0)
sb.Append (",");
sb.Append (GetTypeSignature (genArgs [i]));
}
sb.Append (">");
}
sb.Append ("(");
SR.ParameterInfo [] parameters = meth.GetParameters ();
for (int i = 0; i < parameters.Length; i++) {
if (i > 0)
sb.Append (",");
sb.Append (GetTypeSignature (parameters [i].ParameterType));
}
sb.Append (")");
return sb.ToString ();
}
static bool IsGenericMethod (SR.MethodBase mb)
{
return GetProperty (mb, "IsGenericMethod");
}
static bool IsGenericMethodDefinition (SR.MethodBase mb)
{
return GetProperty (mb, "IsGenericMethodDefinition");
}
static bool IsGenericMethodSpec (SR.MethodBase mb)
{
return IsGenericMethod (mb) && !IsGenericMethodDefinition (mb);
}
static Type [] GetGenericArguments (SR.MethodInfo mi)
{
return (Type []) mi.GetType ().GetMethod ("GetGenericArguments").Invoke (mi, null);
}
static int GetMetadataToken (SR.MethodInfo mi)
{
return (int) mi.GetType ().GetProperty ("MetadataToken").GetValue (mi, null);
}
MethodReference ImportGenericInstanceMethod (SR.MethodInfo mi, ImportContext context)
{
SR.MethodInfo gmd = (SR.MethodInfo) mi.GetType ().GetMethod ("GetGenericMethodDefinition").Invoke (mi, null);
GenericInstanceMethod gim = new GenericInstanceMethod (
ImportMethodBase (gmd, gmd.ReturnType, context));
foreach (Type genArg in GetGenericArguments (mi))
gim.GenericArguments.Add (ImportSystemType (genArg, context));
return gim;
}
MethodReference ImportMethodBase (SR.MethodBase mb, Type retType, ImportContext context)
{
if (IsGenericMethod (mb) && !IsGenericMethodDefinition (mb))
return ImportGenericInstanceMethod ((SR.MethodInfo) mb, context);
Type originalDecType = mb.DeclaringType;
Type declaringTypeDef = originalDecType;
while (IsGenericTypeSpec (declaringTypeDef))
declaringTypeDef = GetGenericTypeDefinition (declaringTypeDef);
if (mb.DeclaringType != declaringTypeDef && mb is SR.MethodInfo) {
int mt = GetMetadataToken (mb as SR.MethodInfo);
// hack to get the generic method definition from the constructed method
foreach (SR.MethodInfo mi in declaringTypeDef.GetMethods ()) {
if (GetMetadataToken (mi) == mt) {
mb = mi;
retType = mi.ReturnType;
break;
}
}
}
string sig = GetMethodBaseSignature (mb, originalDecType, retType);
MethodReference meth = (MethodReference) GetMemberReference (sig);
if (meth != null)
return meth;
meth = new MethodReference (
mb.Name,
(mb.CallingConvention & SR.CallingConventions.HasThis) > 0,
(mb.CallingConvention & SR.CallingConventions.ExplicitThis) > 0,
MethodCallingConvention.Default); // TODO: get the real callconv
meth.DeclaringType = ImportSystemType (originalDecType, context);
if (IsGenericMethod (mb))
foreach (Type genParam in GetGenericArguments (mb as SR.MethodInfo))
meth.GenericParameters.Add (new GenericParameter (genParam.Name, meth));
TypeReference contextType = context.GenericContext.Type;
MethodReference contextMethod = context.GenericContext.Method;
context.GenericContext.Method = meth;
context.GenericContext.Type = ImportSystemType (declaringTypeDef, context);
meth.ReturnType.ReturnType = ImportSystemType (retType, context);
SR.ParameterInfo [] parameters = mb.GetParameters ();
for (int i = 0; i < parameters.Length; i++)
meth.Parameters.Add (new ParameterDefinition (
ImportSystemType (parameters [i].ParameterType, context)));
context.GenericContext.Type = contextType;
context.GenericContext.Method = contextMethod;
m_module.MemberReferences.Add (meth);
return meth;
}
public MethodReference ImportConstructorInfo (SR.ConstructorInfo ci, ImportContext context)
{
return ImportMethodBase (ci, typeof (void), context);
}
public MethodReference ImportMethodInfo (SR.MethodInfo mi, ImportContext context)
{
return ImportMethodBase (mi, mi.ReturnType, context);
}
static string GetFieldSignature (SR.FieldInfo field)
{
StringBuilder sb = new StringBuilder ();
sb.Append (GetTypeSignature (field.FieldType));
sb.Append (' ');
sb.Append (GetTypeSignature (field.DeclaringType));
sb.Append ("::");
sb.Append (field.Name);
return sb.ToString ();
}
public FieldReference ImportFieldInfo (SR.FieldInfo fi, ImportContext context)
{
string sig = GetFieldSignature (fi);
FieldReference f = (FieldReference) GetMemberReference (sig);
if (f != null)
return f;
f = new FieldReference (
fi.Name,
ImportSystemType (fi.DeclaringType, context),
ImportSystemType (fi.FieldType, context));
m_module.MemberReferences.Add (f);
return f;
}
MemberReference GetMemberReference (string signature)
{
foreach (MemberReference reference in m_module.MemberReferences)
if (reference.ToString () == signature)
return reference;
return null;
}
}
}