mirror of
https://github.com/stride3d/stride.git
synced 2026-10-12 02:23:23 +00:00
779 lines
31 KiB
C#
779 lines
31 KiB
C#
// Copyright (c) .NET Foundation and Contributors (https://dotnetfoundation.org/ & https://stride3d.net) and Silicon Studio Corp. (https://www.siliconstudio.co.jp)
|
|
// Distributed under the MIT license. See the LICENSE.md file in the project root for more information.
|
|
// Source: http://stackoverflow.com/questions/4968755/mono-cecil-call-generic-base-class-method-from-other-assembly
|
|
|
|
using System.Reflection;
|
|
using System.Text;
|
|
using Mono.Cecil;
|
|
using Mono.Cecil.Cil;
|
|
using Mono.Cecil.Rocks;
|
|
using Stride.Core.Storage;
|
|
|
|
namespace Stride.Core.AssemblyProcessor;
|
|
|
|
public static class CecilExtensions
|
|
{
|
|
// Not sure why Cecil made ContainsGenericParameter internal, but let's work around it by reflection.
|
|
private static readonly MethodInfo containsGenericParameterGetMethod = typeof(MemberReference).GetProperty("ContainsGenericParameter", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).GetMethod;
|
|
|
|
public static bool IsResolvedValueType(this TypeReference type)
|
|
{
|
|
if (type.GetType() == typeof(TypeReference))
|
|
type = type.Resolve();
|
|
|
|
return type.IsValueType;
|
|
}
|
|
|
|
public static MethodDefinition GetEmptyConstructor(this TypeDefinition type, bool allowPrivate = false)
|
|
{
|
|
return type.Methods.FirstOrDefault(x => x.IsConstructor && (x.IsPublic || allowPrivate) && !x.IsStatic && x.Parameters.Count == 0);
|
|
}
|
|
|
|
public static ModuleDefinition GetStrideCoreModule(this AssemblyDefinition assembly)
|
|
{
|
|
var strideCoreAssembly = assembly.Name.Name == "Stride.Core"
|
|
? assembly
|
|
: assembly.MainModule.AssemblyResolver.Resolve(new AssemblyNameReference("Stride.Core", null));
|
|
return strideCoreAssembly.MainModule;
|
|
}
|
|
|
|
public static TypeReference MakeGenericType(this TypeReference self, params TypeReference[] arguments)
|
|
{
|
|
if (self.GenericParameters.Count != arguments.Length)
|
|
throw new ArgumentException();
|
|
|
|
if (arguments.Length == 0)
|
|
return self;
|
|
|
|
var instance = new GenericInstanceType(self);
|
|
foreach (var argument in arguments)
|
|
instance.GenericArguments.Add(argument);
|
|
|
|
return instance;
|
|
}
|
|
|
|
public static FieldReference MakeGeneric(this FieldReference self, params TypeReference[] arguments)
|
|
{
|
|
if (arguments.Length == 0)
|
|
return self;
|
|
|
|
return new FieldReference(self.Name, self.FieldType, self.DeclaringType.MakeGenericType(arguments));
|
|
}
|
|
|
|
public static MethodReference MakeGeneric(this MethodReference self, params TypeReference[] arguments)
|
|
{
|
|
if (arguments.Length == 0)
|
|
return self;
|
|
|
|
var reference = new MethodReference(self.Name, self.ReturnType, self.DeclaringType.MakeGenericType(arguments))
|
|
{
|
|
HasThis = self.HasThis,
|
|
ExplicitThis = self.ExplicitThis,
|
|
CallingConvention = self.CallingConvention,
|
|
};
|
|
|
|
foreach (var parameter in self.Parameters)
|
|
reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
|
|
|
|
CopyGenericParameters(self, reference);
|
|
|
|
return reference;
|
|
}
|
|
|
|
private static void CopyGenericParameters(MethodReference self, MethodReference reference)
|
|
{
|
|
foreach (var genericParameter in self.GenericParameters)
|
|
{
|
|
var genericParameterCopy = new GenericParameter(genericParameter.Name, reference)
|
|
{
|
|
Attributes = genericParameter.Attributes,
|
|
};
|
|
reference.GenericParameters.Add(genericParameterCopy);
|
|
foreach (var constraint in genericParameter.Constraints)
|
|
genericParameterCopy.Constraints.Add(constraint);
|
|
}
|
|
}
|
|
|
|
public static MethodReference MakeGenericMethod(this MethodReference self, params TypeReference[] arguments)
|
|
{
|
|
if (self.GenericParameters.Count != arguments.Length)
|
|
throw new ArgumentException();
|
|
|
|
var method = new GenericInstanceMethod(self);
|
|
foreach(var argument in arguments)
|
|
method.GenericArguments.Add(argument);
|
|
return method;
|
|
}
|
|
|
|
public static MethodDefinition OpenModuleConstructor(this AssemblyDefinition assembly, out Instruction returnInstruction)
|
|
{
|
|
// Get or create module static constructor
|
|
var voidType = assembly.MainModule.TypeSystem.Void;
|
|
var moduleClass = assembly.MainModule.Types.First(t => t.Name == "<Module>");
|
|
var staticConstructor = moduleClass.GetStaticConstructor();
|
|
if (staticConstructor == null)
|
|
{
|
|
staticConstructor = new MethodDefinition(".cctor",
|
|
Mono.Cecil.MethodAttributes.Private | Mono.Cecil.MethodAttributes.HideBySig | Mono.Cecil.MethodAttributes.Static | Mono.Cecil.MethodAttributes.SpecialName | Mono.Cecil.MethodAttributes.RTSpecialName,
|
|
voidType);
|
|
staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret));
|
|
|
|
moduleClass.Methods.Add(staticConstructor);
|
|
}
|
|
returnInstruction = staticConstructor.Body.Instructions.Last();
|
|
|
|
return staticConstructor;
|
|
}
|
|
|
|
public static TypeDefinition GetTypeResolved(this ModuleDefinition moduleDefinition, string typeName)
|
|
{
|
|
foreach (var exportedType in moduleDefinition.ExportedTypes)
|
|
{
|
|
if (exportedType.FullName == typeName)
|
|
{
|
|
var typeDefinition = exportedType.Resolve();
|
|
return typeDefinition;
|
|
}
|
|
}
|
|
|
|
return moduleDefinition.GetType(typeName);
|
|
}
|
|
|
|
public static TypeDefinition GetTypeResolved(this ModuleDefinition moduleDefinition, string @namespace, string typeName)
|
|
{
|
|
foreach (var exportedType in moduleDefinition.ExportedTypes)
|
|
{
|
|
if (exportedType.Namespace == @namespace && exportedType.Name == typeName)
|
|
{
|
|
return exportedType.Resolve();
|
|
}
|
|
}
|
|
|
|
return moduleDefinition.GetType(@namespace, typeName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the corlib assembly which can be either mscorlib.dll or System.Runtime.dll depending on the .NET runtime environment.
|
|
/// </summary>
|
|
/// <param name="assembly">Assembly where System.Object is found.</param>
|
|
/// <returns></returns>
|
|
public static AssemblyDefinition FindCorlibAssembly(AssemblyDefinition assembly)
|
|
{
|
|
// Ask Cecil for the core library which will be either mscorlib or System.Runtime.
|
|
AssemblyNameReference corlibReference = assembly.MainModule.TypeSystem.CoreLibrary as AssemblyNameReference;
|
|
return assembly.MainModule.AssemblyResolver.Resolve(corlibReference);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the assembly in which the generic collections are defined. This can be either in mscorlib.dll or in System.Collections.dll depending on the .NET runtime environment.
|
|
/// </summary>
|
|
/// <param name="assembly">Assembly where the generic collections are defined.</param>
|
|
/// <returns></returns>
|
|
public static AssemblyDefinition FindCollectionsAssembly(AssemblyDefinition assembly)
|
|
{
|
|
// Ask Cecil for the core library which will be either mscorlib or System.Runtime.
|
|
var corlibReference = FindCorlibAssembly(assembly);
|
|
|
|
if (string.Equals(corlibReference.Name.Name, "system.runtime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// The core library is System.Runtime, so the collections assemblies are in System.Collections.dll.
|
|
// First we look if it is not already referenced by `assembly' and if not, we made an explicit reference
|
|
// to System.Collections.
|
|
var collectionsAssembly = assembly.MainModule.AssemblyReferences.FirstOrDefault(ass => string.Equals(ass.Name, "system.collections", StringComparison.OrdinalIgnoreCase));
|
|
collectionsAssembly ??= new AssemblyNameReference("System.Collections", new Version(4,0,0,0));
|
|
return assembly.MainModule.AssemblyResolver.Resolve(collectionsAssembly);
|
|
}
|
|
else
|
|
{
|
|
return corlibReference;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds the assembly in which the generic collections are defined. This can be either in mscorlib.dll or in System.Collections.dll depending on the .NET runtime environment.
|
|
/// </summary>
|
|
/// <param name="assembly">Assembly where the generic collections are defined.</param>
|
|
/// <returns></returns>
|
|
public static AssemblyDefinition FindReflectionAssembly(AssemblyDefinition assembly)
|
|
{
|
|
// Ask Cecil for the core library which will be either mscorlib or System.Runtime.
|
|
var corlibReference = FindCorlibAssembly(assembly);
|
|
|
|
if (string.Equals(corlibReference.Name.Name, "system.runtime", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// The core library is System.Runtime, so the collections assemblies are in System.Collections.dll.
|
|
// First we look if it is not already referenced by `assembly' and if not, we made an explicit reference
|
|
// to System.Collections.
|
|
var collectionsAssembly = assembly.MainModule.AssemblyReferences.FirstOrDefault(ass => string.Equals(ass.Name, "system.reflection", StringComparison.OrdinalIgnoreCase));
|
|
collectionsAssembly ??= new AssemblyNameReference("System.Reflection", new Version(4, 0, 0, 0));
|
|
return assembly.MainModule.AssemblyResolver.Resolve(collectionsAssembly);
|
|
}
|
|
else
|
|
{
|
|
return corlibReference;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get AssemblyProcessorProgram Files x86
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static string ProgramFilesx86()
|
|
{
|
|
if (8 == IntPtr.Size
|
|
|| (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432"))))
|
|
{
|
|
return Environment.GetEnvironmentVariable("ProgramFiles(x86)");
|
|
}
|
|
|
|
return Environment.GetEnvironmentVariable("ProgramFiles");
|
|
}
|
|
|
|
public static GenericInstanceType ChangeGenericInstanceType(this GenericInstanceType type, TypeReference elementType, IEnumerable<TypeReference> genericArguments)
|
|
{
|
|
if (elementType != type.ElementType || genericArguments != type.GenericArguments)
|
|
{
|
|
var result = new GenericInstanceType(elementType);
|
|
foreach (var genericArgument in genericArguments)
|
|
result.GenericArguments.Add(genericArgument);
|
|
if (type.HasGenericParameters)
|
|
SetGenericParameters(result, type.GenericParameters);
|
|
return result;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
public static ArrayType ChangeArrayType(this ArrayType type, TypeReference elementType, int rank)
|
|
{
|
|
if (elementType != type.ElementType || rank != type.Rank)
|
|
{
|
|
var result = new ArrayType(elementType, rank);
|
|
if (type.HasGenericParameters)
|
|
SetGenericParameters(result, type.GenericParameters);
|
|
return result;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
public static PointerType ChangePointerType(this PointerType type, TypeReference elementType)
|
|
{
|
|
if (elementType != type.ElementType)
|
|
{
|
|
var result = new PointerType(elementType);
|
|
if (type.HasGenericParameters)
|
|
SetGenericParameters(result, type.GenericParameters);
|
|
return result;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
public static PinnedType ChangePinnedType(this PinnedType type, TypeReference elementType)
|
|
{
|
|
if (elementType != type.ElementType)
|
|
{
|
|
var result = new PinnedType(elementType);
|
|
if (type.HasGenericParameters)
|
|
SetGenericParameters(result, type.GenericParameters);
|
|
return result;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
public static TypeReference ChangeGenericParameters(this TypeReference type, IEnumerable<GenericParameter> genericParameters)
|
|
{
|
|
if (type.GenericParameters == genericParameters)
|
|
return type;
|
|
|
|
TypeReference result;
|
|
if (type is ArrayType arrayType)
|
|
{
|
|
result = new ArrayType(arrayType.ElementType, arrayType.Rank);
|
|
}
|
|
else
|
|
{
|
|
if (type is GenericInstanceType genericInstanceType)
|
|
{
|
|
result = new GenericInstanceType(genericInstanceType.ElementType);
|
|
}
|
|
else if (type.GetType() == typeof(TypeReference).GetType())
|
|
{
|
|
result = new TypeReference(type.Namespace, type.Name, type.Module, type.Scope, type.IsValueType);
|
|
}
|
|
else
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
SetGenericParameters(result, genericParameters);
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sometimes, TypeReference.IsValueType is not properly set (since it needs to load dependent assembly).
|
|
/// THis do so when necessary.
|
|
/// </summary>
|
|
/// <param name="typeReference"></param>
|
|
/// <returns></returns>
|
|
public static TypeReference FixupValueType(this TypeReference typeReference)
|
|
{
|
|
return FixupValueTypeVisitor.Default.VisitDynamic(typeReference);
|
|
}
|
|
|
|
private static void SetGenericParameters(TypeReference result, IEnumerable<GenericParameter> genericParameters)
|
|
{
|
|
foreach (var genericParameter in genericParameters)
|
|
result.GenericParameters.Add(genericParameter);
|
|
}
|
|
|
|
public static string GenerateGenerics(this TypeReference type, bool empty = false)
|
|
{
|
|
var genericInstanceType = type as GenericInstanceType;
|
|
if (!type.HasGenericParameters && genericInstanceType == null)
|
|
return string.Empty;
|
|
|
|
var result = new StringBuilder();
|
|
|
|
// Try to process generic instantiations
|
|
if (genericInstanceType != null)
|
|
{
|
|
result.Append("<");
|
|
|
|
bool first = true;
|
|
foreach (var genericArgument in genericInstanceType.GenericArguments)
|
|
{
|
|
if (!first)
|
|
result.Append(",");
|
|
first = false;
|
|
if (!empty)
|
|
result.Append(ConvertCSharp(genericArgument, empty));
|
|
}
|
|
|
|
result.Append(">");
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
if (type.HasGenericParameters)
|
|
{
|
|
result.Append("<");
|
|
|
|
bool first = true;
|
|
foreach (var genericParameter in type.GenericParameters)
|
|
{
|
|
if (!first)
|
|
result.Append(",");
|
|
first = false;
|
|
if (!empty)
|
|
result.Append(ConvertCSharp(genericParameter, empty));
|
|
}
|
|
|
|
result.Append(">");
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
public static unsafe string ConvertTypeId(this TypeReference type)
|
|
{
|
|
var typeName = type.ConvertCSharp(false);
|
|
var typeId = ObjectId.FromBytes(Encoding.UTF8.GetBytes(typeName));
|
|
|
|
var typeIdHash = (uint*)&typeId;
|
|
return string.Format("new {0}(0x{1:x8}, 0x{2:x8}, 0x{3:x8}, 0x{4:x8})", typeof(ObjectId).FullName, typeIdHash[0], typeIdHash[1], typeIdHash[2], typeIdHash[3]);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates type name valid to use from C# source file.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <param name="empty"></param>
|
|
/// <returns></returns>
|
|
public static string ConvertCSharp(this TypeReference type, bool empty = false)
|
|
{
|
|
// Try to process arrays
|
|
if (type is ArrayType arrayType)
|
|
{
|
|
return ConvertCSharp(arrayType.ElementType, empty) + "[]";
|
|
}
|
|
|
|
// Remove the `X at end of generic definition.
|
|
var typeName = type.GetElementType().FullName;
|
|
var genericSeparatorIndex = typeName.LastIndexOf('`');
|
|
if (genericSeparatorIndex != -1)
|
|
typeName = typeName[..genericSeparatorIndex];
|
|
|
|
// Replace / into . (nested types)
|
|
typeName = typeName.Replace('/', '.');
|
|
|
|
// Try to process generic instantiations
|
|
if (type is GenericInstanceType genericInstanceType)
|
|
{
|
|
var result = new StringBuilder();
|
|
|
|
// Use ElementType so that we have only the name without the <> part.
|
|
result.Append(typeName);
|
|
result.Append("<");
|
|
|
|
bool first = true;
|
|
foreach (var genericArgument in genericInstanceType.GenericArguments)
|
|
{
|
|
if (!first)
|
|
result.Append(",");
|
|
first = false;
|
|
if (!empty)
|
|
result.Append(ConvertCSharp(genericArgument, empty));
|
|
}
|
|
|
|
result.Append(">");
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
if (type.HasGenericParameters)
|
|
{
|
|
var result = new StringBuilder();
|
|
|
|
// Use ElementType so that we have only the name without the <> part.
|
|
result.Append(typeName);
|
|
result.Append("<");
|
|
|
|
bool first = true;
|
|
foreach (var genericParameter in type.GenericParameters)
|
|
{
|
|
if (!first)
|
|
result.Append(",");
|
|
first = false;
|
|
if (!empty)
|
|
result.Append(ConvertCSharp(genericParameter, empty));
|
|
}
|
|
|
|
result.Append(">");
|
|
|
|
return result.ToString();
|
|
}
|
|
|
|
return typeName;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates the Mono.Cecil TypeReference from its .NET <see cref="Type"/> counterpart.
|
|
/// </summary>
|
|
/// <param name="type">The type.</param>
|
|
/// <param name="assemblyResolver">The assembly resolver.</param>
|
|
/// <returns></returns>
|
|
public static TypeReference GenerateTypeCecil(this Type type, BaseAssemblyResolver assemblyResolver)
|
|
{
|
|
var assemblyDefinition = assemblyResolver.Resolve(AssemblyNameReference.Parse(type.Assembly.FullName));
|
|
TypeReference typeReference;
|
|
|
|
if (type.IsNested)
|
|
{
|
|
var declaringType = GenerateTypeCecil(type.DeclaringType, assemblyResolver);
|
|
typeReference = declaringType.Resolve().NestedTypes.FirstOrDefault(x => x.Name == type.Name);
|
|
}
|
|
else if (type.IsArray)
|
|
{
|
|
var elementType = GenerateTypeCecil(type.GetElementType(), assemblyResolver);
|
|
typeReference = new ArrayType(elementType, type.GetArrayRank());
|
|
}
|
|
else
|
|
{
|
|
typeReference = assemblyDefinition.MainModule.GetTypeResolved(type.IsGenericType ? type.GetGenericTypeDefinition().FullName : type.FullName);
|
|
}
|
|
|
|
if (typeReference == null)
|
|
throw new InvalidOperationException("Could not resolve cecil type.");
|
|
|
|
if (type.IsGenericType)
|
|
{
|
|
var genericInstanceType = new GenericInstanceType(typeReference);
|
|
foreach (var argType in type.GetGenericArguments())
|
|
{
|
|
TypeReference argTypeReference;
|
|
if (argType.IsGenericParameter)
|
|
{
|
|
argTypeReference = new GenericParameter(argType.Name, typeReference);
|
|
}
|
|
else
|
|
{
|
|
argTypeReference = GenerateTypeCecil(argType, assemblyResolver);
|
|
}
|
|
genericInstanceType.GenericArguments.Add(argTypeReference);
|
|
}
|
|
|
|
typeReference = genericInstanceType;
|
|
}
|
|
|
|
return typeReference;
|
|
}
|
|
|
|
public static bool ContainsGenericParameter(this MemberReference memberReference)
|
|
{
|
|
return (bool)containsGenericParameterGetMethod.Invoke(memberReference, null);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generates type name similar to Type.AssemblyQualifiedName.
|
|
/// </summary>
|
|
/// <param name="type"></param>
|
|
/// <returns></returns>
|
|
public static string ConvertAssemblyQualifiedName(this TypeReference type)
|
|
{
|
|
var result = new StringBuilder(256);
|
|
ConvertAssemblyQualifiedName(type, result);
|
|
return result.ToString();
|
|
}
|
|
|
|
private static void ConvertAssemblyQualifiedName(this TypeReference type, StringBuilder result)
|
|
{
|
|
int start, end;
|
|
|
|
var arrayType = type as ArrayType;
|
|
if (arrayType != null)
|
|
{
|
|
// If it's an array, process element type, and add [] after
|
|
type = arrayType.ElementType;
|
|
}
|
|
|
|
// Add FUllName from GetElementType() (remove generics etc...)
|
|
start = result.Length;
|
|
result.Append(type.GetElementType().FullName);
|
|
end = result.Length;
|
|
|
|
// Replace / into + (nested types)
|
|
result = result.Replace('/', '+', start, end);
|
|
|
|
// Try to process generic instantiations
|
|
if (type is GenericInstanceType genericInstanceType)
|
|
{
|
|
// Ideally we would like to have access to Mono.Cecil TypeReference.ContainsGenericParameter, but it's internal.
|
|
// This doesn't cover every case but hopefully this should be enough for serialization
|
|
bool containsGenericParameter = false;
|
|
foreach (var genericArgument in genericInstanceType.GenericArguments)
|
|
{
|
|
if (genericArgument.IsGenericParameter)
|
|
containsGenericParameter = true;
|
|
}
|
|
|
|
if (!containsGenericParameter)
|
|
{
|
|
// Use ElementType so that we have only the name without the <> part.
|
|
result.Append('[');
|
|
|
|
bool first = true;
|
|
foreach (var genericArgument in genericInstanceType.GenericArguments)
|
|
{
|
|
if (!first)
|
|
result.Append(",");
|
|
result.Append('[');
|
|
first = false;
|
|
result.Append(ConvertAssemblyQualifiedName(genericArgument));
|
|
result.Append(']');
|
|
}
|
|
|
|
result.Append(']');
|
|
}
|
|
}
|
|
|
|
// Try to process arrays
|
|
if (arrayType != null)
|
|
{
|
|
result.Append('[');
|
|
if (arrayType.Rank > 1)
|
|
result.Append(',', arrayType.Rank - 1);
|
|
result.Append(']');
|
|
}
|
|
|
|
result.Append(", ");
|
|
start = result.Length;
|
|
result.Append(type.Module.Assembly.FullName);
|
|
end = result.Length;
|
|
|
|
#if STRIDE_PLATFORM_MONO_MOBILE
|
|
// Xamarin iOS and Android remap some assemblies
|
|
const string oldTypeEnding = "2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e";
|
|
const string newTypeEnding = "4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";
|
|
result = result.Replace(oldTypeEnding, newTypeEnding, start, end);
|
|
#endif
|
|
}
|
|
|
|
public static void AddRange<T>(this ICollection<T> list, IEnumerable<T> items)
|
|
{
|
|
if (list is List<T> l)
|
|
{
|
|
l.AddRange(items);
|
|
}
|
|
else
|
|
{
|
|
foreach (var item in items)
|
|
{
|
|
list.Add(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void InflateGenericType(TypeDefinition genericType, TypeDefinition inflatedType, params TypeReference[] genericTypes)
|
|
{
|
|
// Base type
|
|
var genericMapping = new Dictionary<TypeReference, TypeReference>();
|
|
for (int i = 0; i < genericTypes.Length; ++i)
|
|
{
|
|
genericMapping.Add(genericType.GenericParameters[i], genericTypes[i]);
|
|
}
|
|
|
|
var resolveGenericsVisitor = new ResolveGenericsVisitor(genericMapping);
|
|
inflatedType.BaseType = inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(genericType.BaseType));
|
|
|
|
// Some stuff are not handled yet
|
|
if (genericType.HasNestedTypes)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
foreach (var field in genericType.Fields)
|
|
{
|
|
var clonedField = new FieldDefinition(field.Name, field.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(field.FieldType)));
|
|
inflatedType.Fields.Add(clonedField);
|
|
}
|
|
|
|
foreach (var property in genericType.Properties)
|
|
{
|
|
if (property.HasParameters)
|
|
throw new NotImplementedException();
|
|
|
|
var clonedProperty = new PropertyDefinition(property.Name, property.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(property.PropertyType)))
|
|
{
|
|
HasThis = property.HasThis,
|
|
GetMethod = property.GetMethod != null ? InflateMethod(inflatedType, property.GetMethod, resolveGenericsVisitor) : null,
|
|
SetMethod = property.SetMethod != null ? InflateMethod(inflatedType, property.SetMethod, resolveGenericsVisitor) : null,
|
|
};
|
|
|
|
inflatedType.Properties.Add(clonedProperty);
|
|
}
|
|
|
|
// Clone methods
|
|
foreach (var method in genericType.Methods)
|
|
{
|
|
var clonedMethod = InflateMethod(inflatedType, method, resolveGenericsVisitor);
|
|
inflatedType.Methods.Add(clonedMethod);
|
|
}
|
|
}
|
|
|
|
private static MethodDefinition InflateMethod(TypeDefinition inflatedType, MethodDefinition method, ResolveGenericsVisitor resolveGenericsVisitor)
|
|
{
|
|
var clonedMethod = new MethodDefinition(method.Name, method.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(method.ReturnType)));
|
|
clonedMethod.Parameters.AddRange(
|
|
method.Parameters.Select(x => new ParameterDefinition(x.Name, x.Attributes, inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(x.ParameterType)))));
|
|
|
|
if (method.Body != null)
|
|
{
|
|
clonedMethod.Body.Variables.AddRange(
|
|
method.Body.Variables.Select(x => new VariableDefinition(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(x.VariableType)))));
|
|
|
|
clonedMethod.Body.InitLocals = method.Body.InitLocals;
|
|
|
|
var mappedInstructions = new Dictionary<Instruction, Instruction>();
|
|
foreach (var instruction in method.Body.Instructions)
|
|
{
|
|
// Create nop instructions to start with (if we use actual opcode, it would do an operand check)
|
|
var mappedInstruction = Instruction.Create(OpCodes.Nop);
|
|
mappedInstruction.OpCode = instruction.OpCode;
|
|
mappedInstruction.Operand = instruction.Operand;
|
|
mappedInstructions[instruction] = mappedInstruction;
|
|
}
|
|
|
|
foreach (var instruction in method.Body.Instructions)
|
|
{
|
|
// Fix operand
|
|
var mappedInstruction = mappedInstructions[instruction];
|
|
if (mappedInstruction.Operand is Instruction)
|
|
{
|
|
mappedInstruction.Operand = mappedInstructions[(Instruction)instruction.Operand];
|
|
}
|
|
else if (mappedInstruction.Operand is ParameterDefinition)
|
|
{
|
|
var parameterIndex = method.Parameters.IndexOf((ParameterDefinition)instruction.Operand);
|
|
mappedInstruction.Operand = clonedMethod.Parameters[parameterIndex];
|
|
}
|
|
else if (mappedInstruction.Operand is VariableDefinition)
|
|
{
|
|
var variableIndex = method.Body.Variables.IndexOf((VariableDefinition)instruction.Operand);
|
|
mappedInstruction.Operand = clonedMethod.Body.Variables[variableIndex];
|
|
}
|
|
else if (mappedInstruction.Operand is TypeReference reference)
|
|
{
|
|
var newTypeReference = resolveGenericsVisitor.VisitDynamic(reference);
|
|
newTypeReference = inflatedType.Module.ImportReference(newTypeReference);
|
|
mappedInstruction.Operand = newTypeReference;
|
|
}
|
|
else if (mappedInstruction.Operand is FieldReference fieldReference)
|
|
{
|
|
var newFieldReference = new FieldReference(fieldReference.Name,
|
|
inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(fieldReference.FieldType)),
|
|
inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(fieldReference.DeclaringType)));
|
|
mappedInstruction.Operand = newFieldReference;
|
|
}
|
|
else if (mappedInstruction.Operand is MethodReference methodReference)
|
|
{
|
|
var genericInstanceMethod = methodReference as GenericInstanceMethod;
|
|
if (genericInstanceMethod != null)
|
|
{
|
|
methodReference = genericInstanceMethod.ElementMethod;
|
|
}
|
|
|
|
methodReference = methodReference.GetElementMethod();
|
|
var newMethodReference = new MethodReference(methodReference.Name,
|
|
inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(methodReference.ReturnType)),
|
|
inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(methodReference.DeclaringType)))
|
|
{
|
|
HasThis = methodReference.HasThis,
|
|
ExplicitThis = methodReference.ExplicitThis,
|
|
CallingConvention = methodReference.CallingConvention,
|
|
};
|
|
|
|
foreach (var parameter in methodReference.Parameters)
|
|
newMethodReference.Parameters.Add(new ParameterDefinition(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(parameter.ParameterType))));
|
|
|
|
if (methodReference.HasGenericParameters)
|
|
{
|
|
CopyGenericParameters(methodReference, newMethodReference);
|
|
}
|
|
|
|
if (genericInstanceMethod != null)
|
|
{
|
|
newMethodReference = newMethodReference.MakeGenericMethod(genericInstanceMethod.GenericArguments.Select(x => inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(x))).ToArray());
|
|
}
|
|
|
|
mappedInstruction.Operand = newMethodReference;
|
|
}
|
|
else if (mappedInstruction.Operand is CallSite callSite)
|
|
{
|
|
var newCallSite = new CallSite(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(callSite.ReturnType)))
|
|
{
|
|
HasThis = callSite.HasThis,
|
|
ExplicitThis = callSite.ExplicitThis,
|
|
CallingConvention = callSite.CallingConvention,
|
|
};
|
|
|
|
foreach (var parameter in callSite.Parameters)
|
|
newCallSite.Parameters.Add(new ParameterDefinition(inflatedType.Module.ImportReference(resolveGenericsVisitor.VisitDynamic(parameter.ParameterType))));
|
|
|
|
mappedInstruction.Operand = newCallSite;
|
|
}
|
|
else if (mappedInstruction.Operand is Instruction[])
|
|
{
|
|
// Not used in UpdatableProperty<T>
|
|
throw new NotImplementedException();
|
|
}
|
|
}
|
|
|
|
clonedMethod.Body.Instructions.AddRange(method.Body.Instructions.Select(x => mappedInstructions[x]));
|
|
}
|
|
return clonedMethod;
|
|
}
|
|
}
|