753 lines
28 KiB
C#
753 lines
28 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using ModestTree;
|
|
using Zenject.Internal;
|
|
using Zenject.ReflectionBaking.Mono.Cecil;
|
|
using Zenject.ReflectionBaking.Mono.Cecil.Cil;
|
|
using Zenject.ReflectionBaking.Mono.Collections.Generic;
|
|
using MethodAttributes = Zenject.ReflectionBaking.Mono.Cecil.MethodAttributes;
|
|
|
|
namespace Zenject.ReflectionBaking
|
|
{
|
|
public class ReflectionBakingModuleEditor
|
|
{
|
|
readonly Assembly _assembly;
|
|
readonly ModuleDefinition _module;
|
|
readonly List<Regex> _namespaceRegexes;
|
|
|
|
MethodReference _zenjectTypeInfoConstructor;
|
|
MethodReference _injectableInfoConstructor;
|
|
MethodReference _injectMethodInfoConstructor;
|
|
MethodReference _injectMemberInfoConstructor;
|
|
MethodReference _constructorInfoConstructor;
|
|
MethodReference _getTypeFromHandleMethod;
|
|
MethodReference _funcConstructor;
|
|
MethodReference _funcPostInject;
|
|
MethodReference _funcMemberSetter;
|
|
MethodReference _preserveConstructor;
|
|
|
|
TypeReference _injectMethodInfoType;
|
|
TypeReference _injectMemberInfoType;
|
|
TypeReference _injectableInfoType;
|
|
TypeReference _objectArrayType;
|
|
TypeReference _zenjectTypeInfoType;
|
|
|
|
ReflectionBakingModuleEditor(
|
|
ModuleDefinition module, Assembly assembly, List<string> namespacePatterns)
|
|
{
|
|
_module = module;
|
|
_assembly = assembly;
|
|
_namespaceRegexes = namespacePatterns.Select(CreateRegex).ToList();
|
|
_namespaceRegexes.Add(CreateRegex("^Zenject"));
|
|
}
|
|
|
|
public static int WeaveAssembly(
|
|
ModuleDefinition module, Assembly assembly)
|
|
{
|
|
return WeaveAssembly(module, assembly, new List<string>());
|
|
}
|
|
|
|
public static int WeaveAssembly(
|
|
ModuleDefinition module, Assembly assembly, List<string> namespacePatterns)
|
|
{
|
|
return new ReflectionBakingModuleEditor(module, assembly, namespacePatterns).Run();
|
|
}
|
|
|
|
int Run()
|
|
{
|
|
SaveImports();
|
|
|
|
int numTypesEditted = 0;
|
|
|
|
var allTypes = _module.LookupAllTypes();
|
|
|
|
foreach (var typeDef in allTypes)
|
|
{
|
|
if (_namespaceRegexes.Any() && !_namespaceRegexes.Any(x => x.IsMatch(typeDef.FullName)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var actualType = typeDef.TryGetActualType(_assembly);
|
|
|
|
if (actualType == null)
|
|
{
|
|
Log.Warn("Could not find actual type for type '{0}', skipping", typeDef.FullName);
|
|
continue;
|
|
}
|
|
|
|
if (TryEditType(typeDef, actualType))
|
|
{
|
|
numTypesEditted++;
|
|
}
|
|
}
|
|
|
|
return numTypesEditted;
|
|
}
|
|
|
|
Regex CreateRegex(string regexStr)
|
|
{
|
|
return new Regex(regexStr, RegexOptions.Compiled);
|
|
}
|
|
|
|
void SaveImports()
|
|
{
|
|
_zenjectTypeInfoType = _module.ImportType<InjectTypeInfo>();
|
|
_zenjectTypeInfoConstructor = _module.ImportMethod<InjectTypeInfo>(".ctor");
|
|
|
|
_injectableInfoConstructor = _module.ImportMethod<InjectableInfo>(".ctor");
|
|
|
|
_getTypeFromHandleMethod = _module.ImportMethod<Type>("GetTypeFromHandle", 1);
|
|
|
|
_injectMethodInfoType = _module.ImportType<InjectTypeInfo.InjectMethodInfo>();
|
|
_injectMethodInfoConstructor = _module.ImportMethod<InjectTypeInfo.InjectMethodInfo>(".ctor");
|
|
|
|
_injectMemberInfoType = _module.ImportType<InjectTypeInfo.InjectMemberInfo>();
|
|
_injectMemberInfoConstructor = _module.ImportMethod<InjectTypeInfo.InjectMemberInfo>(".ctor");
|
|
|
|
_preserveConstructor = _module.ImportMethod<Zenject.Internal.PreserveAttribute>(".ctor");
|
|
_constructorInfoConstructor = _module.ImportMethod<InjectTypeInfo.InjectConstructorInfo>(".ctor");
|
|
|
|
_injectableInfoType = _module.ImportType<InjectableInfo>();
|
|
|
|
_objectArrayType = _module.Import(typeof(object[]));
|
|
|
|
_funcConstructor = _module.ImportMethod<ZenFactoryMethod>(".ctor", 2);
|
|
|
|
_funcPostInject = _module.ImportMethod<ZenInjectMethod>(".ctor", 2);
|
|
|
|
_funcMemberSetter = _module.ImportMethod<ZenMemberSetterMethod>(".ctor", 2);
|
|
}
|
|
|
|
public bool TryEditType(TypeDefinition typeDef, Type actualType)
|
|
{
|
|
if (actualType.IsEnum || actualType.IsValueType || actualType.IsInterface
|
|
|| actualType.HasAttribute<NoReflectionBakingAttribute>()
|
|
|| IsStaticClass(actualType) || actualType.DerivesFromOrEqual<Delegate>() || actualType.DerivesFromOrEqual<Attribute>())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Allow running on the same dll multiple times without causing problems
|
|
if (IsTypeProcessed(typeDef))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
try
|
|
{
|
|
var typeInfo = ReflectionTypeAnalyzer.GetReflectionInfo(actualType);
|
|
|
|
var factoryMethod = TryAddFactoryMethod(typeDef, typeInfo);
|
|
var genericTypeDef = CreateGenericInstanceWithParameters(typeDef);
|
|
var fieldSetMethods = AddFieldSetters(typeDef, genericTypeDef, typeInfo);
|
|
var propertySetMethods = AddPropertySetters(typeDef, genericTypeDef, typeInfo);
|
|
var postInjectMethods = AddPostInjectMethods(typeDef, genericTypeDef, typeInfo);
|
|
|
|
CreateGetInfoMethod(
|
|
typeDef, genericTypeDef, typeInfo,
|
|
factoryMethod, fieldSetMethods, propertySetMethods, postInjectMethods);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
Log.ErrorException("Error when modifying type '{0}'".Fmt(actualType), e);
|
|
throw;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool IsStaticClass(Type type)
|
|
{
|
|
// Apparently this is unique to static classes
|
|
return type.IsAbstract && type.IsSealed;
|
|
}
|
|
|
|
// We are already processed if our static constructor calls TypeAnalyzer
|
|
bool IsTypeProcessed(TypeDefinition typeDef)
|
|
{
|
|
return typeDef.GetMethod(TypeAnalyzer.ReflectionBakingGetInjectInfoMethodName) != null;
|
|
}
|
|
|
|
void EmitCastOperation(ILProcessor processor, Type type, Collection<GenericParameter> genericParams)
|
|
{
|
|
if (type.IsGenericParameter)
|
|
{
|
|
processor.Emit(OpCodes.Unbox_Any, genericParams[type.GenericParameterPosition]);
|
|
}
|
|
else if (type.IsEnum)
|
|
{
|
|
processor.Emit(OpCodes.Unbox_Any, _module.TypeSystem.Int32);
|
|
}
|
|
else if (type.IsValueType)
|
|
{
|
|
processor.Emit(OpCodes.Unbox_Any, _module.ImportType(type));
|
|
}
|
|
else
|
|
{
|
|
processor.Emit(OpCodes.Castclass, CreateGenericInstanceIfNecessary(type, genericParams));
|
|
}
|
|
}
|
|
|
|
TypeReference CreateGenericInstanceWithParameters(TypeDefinition typeDef)
|
|
{
|
|
if (typeDef.GenericParameters.Any())
|
|
{
|
|
var genericInstance = new GenericInstanceType(typeDef);
|
|
|
|
foreach (var parameter in typeDef.GenericParameters)
|
|
{
|
|
genericInstance.GenericArguments.Add(parameter);
|
|
}
|
|
|
|
return genericInstance;
|
|
}
|
|
|
|
return typeDef;
|
|
}
|
|
|
|
MethodDefinition TryAddFactoryMethod(
|
|
TypeDefinition typeDef, ReflectionTypeInfo typeInfo)
|
|
{
|
|
if (typeInfo.Type.GetParentTypes().Any(x => x.FullName == "UnityEngine.Component"))
|
|
{
|
|
Assert.That(typeInfo.InjectConstructor.Parameters.IsEmpty());
|
|
return null;
|
|
}
|
|
|
|
if (typeInfo.InjectConstructor.ConstructorInfo == null)
|
|
{
|
|
// static classes, abstract types
|
|
return null;
|
|
}
|
|
|
|
var factoryMethod = new MethodDefinition(
|
|
TypeAnalyzer.ReflectionBakingFactoryMethodName,
|
|
MethodAttributes.Private | MethodAttributes.HideBySig |
|
|
MethodAttributes.Static,
|
|
_module.TypeSystem.Object);
|
|
|
|
var p1 = new ParameterDefinition(_objectArrayType);
|
|
p1.Name = "P_0";
|
|
factoryMethod.Parameters.Add(p1);
|
|
|
|
var body = factoryMethod.Body;
|
|
body.InitLocals = true;
|
|
|
|
var processor = body.GetILProcessor();
|
|
|
|
var returnValueVar = new VariableDefinition(_module.TypeSystem.Object);
|
|
body.Variables.Add(returnValueVar);
|
|
|
|
processor.Emit(OpCodes.Nop);
|
|
|
|
Assert.IsNotNull(typeInfo.InjectConstructor);
|
|
|
|
var args = typeInfo.InjectConstructor.Parameters;
|
|
|
|
for (int i = 0; i < args.Count; i++)
|
|
{
|
|
var arg = args[i];
|
|
|
|
processor.Emit(OpCodes.Ldarg_0);
|
|
processor.Emit(OpCodes.Ldc_I4, i);
|
|
processor.Emit(OpCodes.Ldelem_Ref);
|
|
|
|
EmitCastOperation(
|
|
processor, arg.ParameterInfo.ParameterType, typeDef.GenericParameters);
|
|
}
|
|
|
|
processor.Emit(OpCodes.Newobj, _module.Import(typeInfo.InjectConstructor.ConstructorInfo));
|
|
|
|
processor.Emit(OpCodes.Stloc_0);
|
|
processor.Emit(OpCodes.Ldloc_S, returnValueVar);
|
|
processor.Emit(OpCodes.Ret);
|
|
|
|
typeDef.Methods.Add(factoryMethod);
|
|
|
|
return factoryMethod;
|
|
}
|
|
|
|
void AddPostInjectMethodBody(
|
|
ILProcessor processor, ReflectionTypeInfo.InjectMethodInfo postInjectInfo, TypeDefinition typeDef, TypeReference genericTypeDef)
|
|
{
|
|
processor.Emit(OpCodes.Nop);
|
|
|
|
TypeReference declaringTypeDef;
|
|
MethodReference actualMethodDef;
|
|
|
|
if (!TryFindLocalMethod(
|
|
genericTypeDef, postInjectInfo.MethodInfo.Name, out declaringTypeDef, out actualMethodDef))
|
|
{
|
|
throw Assert.CreateException();
|
|
}
|
|
|
|
processor.Emit(OpCodes.Ldarg_0);
|
|
processor.Emit(OpCodes.Castclass, declaringTypeDef);
|
|
|
|
for (int k = 0; k < postInjectInfo.Parameters.Count; k++)
|
|
{
|
|
var injectInfo = postInjectInfo.Parameters[k];
|
|
|
|
processor.Emit(OpCodes.Ldarg_1);
|
|
processor.Emit(OpCodes.Ldc_I4, k);
|
|
processor.Emit(OpCodes.Ldelem_Ref);
|
|
|
|
EmitCastOperation(processor, injectInfo.ParameterInfo.ParameterType, typeDef.GenericParameters);
|
|
}
|
|
|
|
processor.Emit(OpCodes.Callvirt, actualMethodDef);
|
|
processor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
MethodDefinition AddPostInjectMethod(
|
|
string name, ReflectionTypeInfo.InjectMethodInfo postInjectInfo, TypeDefinition typeDef, TypeReference genericTypeDef)
|
|
{
|
|
var methodDef = new MethodDefinition(
|
|
name,
|
|
MethodAttributes.Private | MethodAttributes.HideBySig |
|
|
MethodAttributes.Static,
|
|
_module.TypeSystem.Void);
|
|
|
|
var p1 = new ParameterDefinition(_module.TypeSystem.Object);
|
|
p1.Name = "P_0";
|
|
methodDef.Parameters.Add(p1);
|
|
|
|
var p2 = new ParameterDefinition(_objectArrayType);
|
|
p2.Name = "P_1";
|
|
methodDef.Parameters.Add(p2);
|
|
|
|
var body = methodDef.Body;
|
|
var processor = body.GetILProcessor();
|
|
|
|
AddPostInjectMethodBody(processor, postInjectInfo, typeDef, genericTypeDef);
|
|
|
|
typeDef.Methods.Add(methodDef);
|
|
|
|
return methodDef;
|
|
}
|
|
|
|
List<MethodDefinition> AddPostInjectMethods(
|
|
TypeDefinition typeDef, TypeReference genericTypeDef, ReflectionTypeInfo typeInfo)
|
|
{
|
|
var postInjectMethods = new List<MethodDefinition>();
|
|
|
|
for (int i = 0; i < typeInfo.InjectMethods.Count; i++)
|
|
{
|
|
postInjectMethods.Add(
|
|
AddPostInjectMethod(
|
|
TypeAnalyzer.ReflectionBakingInjectMethodPrefix + i, typeInfo.InjectMethods[i], typeDef, genericTypeDef));
|
|
}
|
|
|
|
return postInjectMethods;
|
|
}
|
|
|
|
void EmitSetterMethod(
|
|
ILProcessor processor, MemberInfo memberInfo, TypeDefinition typeDef, TypeReference genericTypeDef)
|
|
{
|
|
processor.Emit(OpCodes.Nop);
|
|
|
|
processor.Emit(OpCodes.Ldarg_0);
|
|
processor.Emit(OpCodes.Castclass, genericTypeDef);
|
|
|
|
processor.Emit(OpCodes.Ldarg_1);
|
|
|
|
if (memberInfo is FieldInfo)
|
|
{
|
|
var fieldInfo = (FieldInfo)memberInfo;
|
|
|
|
EmitCastOperation(processor, fieldInfo.FieldType, typeDef.GenericParameters);
|
|
|
|
processor.Emit(OpCodes.Stfld, FindLocalField(genericTypeDef, fieldInfo.Name));
|
|
}
|
|
else
|
|
{
|
|
var propertyInfo = (PropertyInfo)memberInfo;
|
|
|
|
EmitCastOperation(processor, propertyInfo.PropertyType, typeDef.GenericParameters);
|
|
|
|
processor.Emit(OpCodes.Callvirt, FindLocalPropertySetMethod(genericTypeDef, propertyInfo.Name));
|
|
}
|
|
|
|
processor.Emit(OpCodes.Ret);
|
|
}
|
|
|
|
MethodDefinition AddSetterMethod(
|
|
string name, MemberInfo memberInfo, TypeDefinition typeDef, TypeReference genericTypeDef)
|
|
{
|
|
var methodDef = new MethodDefinition(
|
|
name,
|
|
MethodAttributes.Private | MethodAttributes.HideBySig |
|
|
MethodAttributes.Static,
|
|
_module.TypeSystem.Void);
|
|
|
|
var p1 = new ParameterDefinition(_module.TypeSystem.Object);
|
|
p1.Name = "P_0";
|
|
methodDef.Parameters.Add(p1);
|
|
|
|
var p2 = new ParameterDefinition(_module.TypeSystem.Object);
|
|
p2.Name = "P_1";
|
|
methodDef.Parameters.Add(p2);
|
|
|
|
methodDef.Body.InitLocals = true;
|
|
|
|
EmitSetterMethod(
|
|
methodDef.Body.GetILProcessor(), memberInfo, typeDef, genericTypeDef);
|
|
|
|
typeDef.Methods.Add(methodDef);
|
|
|
|
return methodDef;
|
|
}
|
|
|
|
List<MethodDefinition> AddPropertySetters(
|
|
TypeDefinition typeDef, TypeReference genericTypeDef, ReflectionTypeInfo typeInfo)
|
|
{
|
|
var methodDefs = new List<MethodDefinition>();
|
|
|
|
for (int i = 0; i < typeInfo.InjectProperties.Count; i++)
|
|
{
|
|
methodDefs.Add(
|
|
AddSetterMethod(
|
|
TypeAnalyzer.ReflectionBakingPropertySetterPrefix + i,
|
|
typeInfo.InjectProperties[i].PropertyInfo, typeDef, genericTypeDef));
|
|
}
|
|
|
|
return methodDefs;
|
|
}
|
|
|
|
List<MethodDefinition> AddFieldSetters(
|
|
TypeDefinition typeDef, TypeReference genericTypeDef, ReflectionTypeInfo typeInfo)
|
|
{
|
|
var methodDefs = new List<MethodDefinition>();
|
|
|
|
for (int i = 0; i < typeInfo.InjectFields.Count; i++)
|
|
{
|
|
methodDefs.Add(
|
|
AddSetterMethod(
|
|
TypeAnalyzer.ReflectionBakingFieldSetterPrefix + i,
|
|
typeInfo.InjectFields[i].FieldInfo, typeDef, genericTypeDef));
|
|
}
|
|
|
|
return methodDefs;
|
|
}
|
|
|
|
void CreateGetInfoMethod(
|
|
TypeDefinition typeDef, TypeReference genericTypeDef, ReflectionTypeInfo typeInfo,
|
|
MethodDefinition factoryMethod, List<MethodDefinition> fieldSetMethods,
|
|
List<MethodDefinition> propertySetMethods, List<MethodDefinition> postInjectMethods)
|
|
{
|
|
var getInfoMethodDef = new MethodDefinition(
|
|
TypeAnalyzer.ReflectionBakingGetInjectInfoMethodName,
|
|
MethodAttributes.Private | MethodAttributes.HideBySig |
|
|
MethodAttributes.Static,
|
|
_zenjectTypeInfoType);
|
|
|
|
typeDef.Methods.Add(getInfoMethodDef);
|
|
|
|
getInfoMethodDef.CustomAttributes.Add(
|
|
new CustomAttribute(_preserveConstructor));
|
|
|
|
var returnValueVar = new VariableDefinition(_module.TypeSystem.Object);
|
|
|
|
var body = getInfoMethodDef.Body;
|
|
|
|
body.Variables.Add(returnValueVar);
|
|
body.InitLocals = true;
|
|
|
|
var instructions = new List<Instruction>();
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldtoken, genericTypeDef));
|
|
instructions.Add(Instruction.Create(OpCodes.Call, _getTypeFromHandleMethod));
|
|
|
|
if (factoryMethod == null)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
|
}
|
|
else
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldftn, factoryMethod.ChangeDeclaringType(genericTypeDef)));
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _funcConstructor));
|
|
}
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, typeInfo.InjectConstructor.Parameters.Count));
|
|
instructions.Add(Instruction.Create(OpCodes.Newarr, _injectableInfoType));
|
|
|
|
for (int i = 0; i < typeInfo.InjectConstructor.Parameters.Count; i++)
|
|
{
|
|
var injectableInfo = typeInfo.InjectConstructor.Parameters[i].InjectableInfo;
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Dup));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, i));
|
|
|
|
EmitNewInjectableInfoInstructions(
|
|
instructions, injectableInfo, typeDef);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
|
}
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _constructorInfoConstructor));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, typeInfo.InjectMethods.Count));
|
|
instructions.Add(Instruction.Create(OpCodes.Newarr, _injectMethodInfoType));
|
|
|
|
Assert.IsEqual(postInjectMethods.Count, typeInfo.InjectMethods.Count);
|
|
|
|
for (int i = 0; i < typeInfo.InjectMethods.Count; i++)
|
|
{
|
|
var injectMethodInfo = typeInfo.InjectMethods[i];
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Dup));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, i));
|
|
|
|
AddInjectableMethodInstructions(
|
|
instructions, injectMethodInfo, typeDef, genericTypeDef, postInjectMethods[i]);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
|
}
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, fieldSetMethods.Count + propertySetMethods.Count));
|
|
instructions.Add(Instruction.Create(OpCodes.Newarr, _injectMemberInfoType));
|
|
|
|
for (int i = 0; i < fieldSetMethods.Count; i++)
|
|
{
|
|
var injectField = typeInfo.InjectFields[i];
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Dup));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, i));
|
|
|
|
AddInjectableMemberInstructions(
|
|
instructions,
|
|
injectField.InjectableInfo, injectField.FieldInfo.Name,
|
|
typeDef, genericTypeDef, fieldSetMethods[i]);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
|
}
|
|
|
|
for (int i = 0; i < propertySetMethods.Count; i++)
|
|
{
|
|
var injectProperty = typeInfo.InjectProperties[i];
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Dup));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, fieldSetMethods.Count + i));
|
|
|
|
AddInjectableMemberInstructions(
|
|
instructions,
|
|
injectProperty.InjectableInfo,
|
|
injectProperty.PropertyInfo.Name, typeDef, genericTypeDef,
|
|
propertySetMethods[i]);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
|
}
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _zenjectTypeInfoConstructor));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Stloc_0));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldloc_S, returnValueVar));
|
|
instructions.Add(Instruction.Create(OpCodes.Ret));
|
|
|
|
var processor = body.GetILProcessor();
|
|
|
|
foreach (var instruction in instructions)
|
|
{
|
|
processor.Append(instruction);
|
|
}
|
|
}
|
|
|
|
MethodReference FindLocalPropertySetMethod(
|
|
TypeReference specificTypeRef, string memberName)
|
|
{
|
|
foreach (var typeRef in specificTypeRef.GetSpecificBaseTypesAndSelf())
|
|
{
|
|
var candidatePropertyDef = typeRef.Resolve().Properties
|
|
.Where(x => x.Name == memberName).SingleOrDefault();
|
|
|
|
if (candidatePropertyDef != null)
|
|
{
|
|
return candidatePropertyDef.SetMethod.ChangeDeclaringType(typeRef);
|
|
}
|
|
}
|
|
|
|
throw Assert.CreateException();
|
|
}
|
|
|
|
FieldReference FindLocalField(
|
|
TypeReference specificTypeRef, string fieldName)
|
|
{
|
|
foreach (var typeRef in specificTypeRef.GetSpecificBaseTypesAndSelf())
|
|
{
|
|
var candidateFieldDef = typeRef.Resolve().Fields
|
|
.Where(x => x.Name == fieldName).SingleOrDefault();
|
|
|
|
if (candidateFieldDef != null)
|
|
{
|
|
return candidateFieldDef.ChangeDeclaringType(typeRef);
|
|
}
|
|
}
|
|
|
|
throw Assert.CreateException();
|
|
}
|
|
|
|
bool TryFindLocalMethod(
|
|
TypeReference specificTypeRef, string methodName, out TypeReference declaringTypeRef, out MethodReference methodRef)
|
|
{
|
|
foreach (var typeRef in specificTypeRef.GetSpecificBaseTypesAndSelf())
|
|
{
|
|
var candidateMethodDef = typeRef.Resolve().Methods
|
|
.Where(x => x.Name == methodName).SingleOrDefault();
|
|
|
|
if (candidateMethodDef != null)
|
|
{
|
|
declaringTypeRef = typeRef;
|
|
methodRef = candidateMethodDef.ChangeDeclaringType(typeRef);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
declaringTypeRef = null;
|
|
methodRef = null;
|
|
return false;
|
|
}
|
|
|
|
void AddObjectInstructions(
|
|
List<Instruction> instructions,
|
|
object identifier)
|
|
{
|
|
if (identifier == null)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
|
}
|
|
else if (identifier is string)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldstr, (string)identifier));
|
|
}
|
|
else if (identifier is int)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, (int)identifier));
|
|
instructions.Add(Instruction.Create(OpCodes.Box, _module.Import(typeof(int))));
|
|
}
|
|
else if (identifier.GetType().IsEnum)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, (int)identifier));
|
|
instructions.Add(Instruction.Create(OpCodes.Box, _module.Import(identifier.GetType())));
|
|
}
|
|
else
|
|
{
|
|
throw Assert.CreateException(
|
|
"Cannot process values with type '{0}' currently. Feel free to add support for this and submit a pull request to github.", identifier.GetType());
|
|
}
|
|
}
|
|
|
|
TypeReference CreateGenericInstanceIfNecessary(
|
|
Type memberType, Collection<GenericParameter> genericParams)
|
|
{
|
|
if (!memberType.ContainsGenericParameters)
|
|
{
|
|
return _module.Import(memberType);
|
|
}
|
|
|
|
if (memberType.IsGenericParameter)
|
|
{
|
|
return genericParams[memberType.GenericParameterPosition];
|
|
}
|
|
|
|
if (memberType.IsArray)
|
|
{
|
|
return new ArrayType(
|
|
CreateGenericInstanceIfNecessary(memberType.GetElementType(), genericParams), memberType.GetArrayRank());
|
|
}
|
|
|
|
var genericMemberType = memberType.GetGenericTypeDefinition();
|
|
|
|
var genericInstance = new GenericInstanceType(_module.Import(genericMemberType));
|
|
|
|
foreach (var arg in memberType.GenericArguments())
|
|
{
|
|
genericInstance.GenericArguments.Add(
|
|
CreateGenericInstanceIfNecessary(arg, genericParams));
|
|
}
|
|
|
|
return genericInstance;
|
|
}
|
|
|
|
void AddInjectableMemberInstructions(
|
|
List<Instruction> instructions,
|
|
InjectableInfo injectableInfo, string name,
|
|
TypeDefinition typeDef, TypeReference genericTypeDef,
|
|
MethodDefinition methodDef)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldftn, methodDef.ChangeDeclaringType(genericTypeDef)));
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _funcMemberSetter));
|
|
|
|
EmitNewInjectableInfoInstructions(
|
|
instructions, injectableInfo, typeDef);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _injectMemberInfoConstructor));
|
|
}
|
|
|
|
void AddInjectableMethodInstructions(
|
|
List<Instruction> instructions,
|
|
ReflectionTypeInfo.InjectMethodInfo injectMethod,
|
|
TypeDefinition typeDef, TypeReference genericTypeDef,
|
|
MethodDefinition methodDef)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldnull));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldftn, methodDef.ChangeDeclaringType(genericTypeDef)));
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _funcPostInject));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, injectMethod.Parameters.Count));
|
|
instructions.Add(Instruction.Create(OpCodes.Newarr, _injectableInfoType));
|
|
|
|
for (int i = 0; i < injectMethod.Parameters.Count; i++)
|
|
{
|
|
var injectableInfo = injectMethod.Parameters[i].InjectableInfo;
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Dup));
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, i));
|
|
|
|
EmitNewInjectableInfoInstructions(
|
|
instructions, injectableInfo, typeDef);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Stelem_Ref));
|
|
}
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldstr, injectMethod.MethodInfo.Name));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _injectMethodInfoConstructor));
|
|
}
|
|
|
|
void EmitNewInjectableInfoInstructions(
|
|
List<Instruction> instructions,
|
|
InjectableInfo injectableInfo,
|
|
TypeDefinition typeDef)
|
|
{
|
|
if (injectableInfo.Optional)
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4_1));
|
|
}
|
|
else
|
|
{
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4_0));
|
|
}
|
|
|
|
AddObjectInstructions(instructions, injectableInfo.Identifier);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldstr, injectableInfo.MemberName));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldtoken, CreateGenericInstanceIfNecessary(injectableInfo.MemberType, typeDef.GenericParameters)));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Call, _getTypeFromHandleMethod));
|
|
|
|
AddObjectInstructions(instructions, injectableInfo.DefaultValue);
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Ldc_I4, (int)injectableInfo.SourceType));
|
|
|
|
instructions.Add(Instruction.Create(OpCodes.Newobj, _injectableInfoConstructor));
|
|
}
|
|
}
|
|
}
|