public OperationInfo(Operation operation, MethodBody body) { Operation = operation; MemberInfo = operation.Instance; IsVariable = operation.OpCode.Name.StartsWith("ldloc") || operation.OpCode.Name.StartsWith("stloc") || operation.OpCode.Name.StartsWith("ldsfld") || operation.OpCode.Name.StartsWith("stsfld") || operation.OpCode.Name.StartsWith("ldfld") || operation.OpCode.Name.StartsWith("stfld"); if (IsVariable) { bool isLoad = operation.OpCode.Name.StartsWith("ldloc"); IsLocalVariable = isLoad || operation.OpCode.Name.StartsWith("stloc"); if (IsLocalVariable) { int localVarIndex; int diff = OpCodes.Stloc_0.Value - OpCodes.Ldloc_0.Value; if (isLoad) { if (operation.OpCode.Value >= OpCodes.Ldloc_0.Value && operation.OpCode.Value <= OpCodes.Ldloc_3.Value) { localVarIndex = Convert.ToInt32(operation.OpCode.Name[^ 1].ToString());
/// <summary> /// Method resolver. /// </summary> /// <param name="context">Method will be compiled.</param> private static void CompileResolve(CompileContext context) { if (context.Method.Name == "MapWithJitex") { MethodBody body = new MethodBody((MethodInfo)context.Method); //All operations on method IList <Operation> operations = body.ReadIL().ToList(); foreach (Operation operation in operations.Where(c => c.OpCode != OpCodes.Nop)) { if (operation.Instance is MethodInfo methodInfo && methodInfo.IsGenericMethod && methodInfo.GetGenericMethodDefinition() == MethodMap) { //Generic type passed: IMapper.Map<Type>(); Type typeDestination = methodInfo.GetGenericArguments().First(); //Variable passed by argument: IMapper.Map<Type>(variable); Operation sourceOperation = null; //Start block from Map call. Operation startMapper = null; //It's necessary find start operation of Mapper.Map call //A basic call will generate these operations: //IL_0000: ldsfld IMapper //IL_0005: ldarg.0 // this //IL_0006: ldfld Variable passed by argument //IL_000b: callvirt IMapper.Map<T>(obj) //IL_0010: ret //We are currently in IL_000b, so we need go until IL_0000. //A way to do this, is check the type of variable|field until find a IMapper. //If variable|field is typeof IMapper, that's start of operation block call, if not, is the variable|field passed by argument. for (int i = operation.Index - 1; i >= 0; i--) { Operation previousOperation = operations[i]; //Operation starts (IL_0000) if (previousOperation.Instance is FieldInfo field && field.FieldType == typeof(IMapper)) { startMapper = previousOperation; break; } //Field passed by argument in our case. //That can be a variable (ldloc), method (callvirt|call), ... if (previousOperation.OpCode.Name.StartsWith("ldfld")) { sourceOperation = previousOperation; } } //Store all properties to make bind. PropertyInfo[] sourceProperties = sourceOperation.Instance.FieldType.GetProperties(); PropertyInfo[] destionationProperties = typeDestination.GetProperties(); //To start bind, we need firstly instantiate our destiny variable. ConstructorInfo defaultConstructorDest = typeDestination.GetConstructor(Type.EmptyTypes); List <byte> ilToReplace = new List <byte> { (byte)OpCodes.Newobj.Value }; //TypeDestination variable = new TypeDestionation(); byte[] defaultCtor = BitConverter.GetBytes(defaultConstructorDest.MetadataToken); ilToReplace.AddRange(defaultCtor); foreach (PropertyInfo sourceProperty in sourceProperties) { //Bind only property with same name (default config to AutoMapper) PropertyInfo destProperty = destionationProperties.FirstOrDefault(p => p.Name == sourceProperty.Name); if (destProperty != null) { //Generate a simple get and set: //variable.Property1 = variablePasseedByArgument.Property1 //Load field (_person) passed by argument on IMapper.Map ilToReplace.Add((byte)OpCodes.Dup.Value); ilToReplace.Add((byte)OpCodes.Ldarg_0.Value); ilToReplace.Add((byte)OpCodes.Ldfld.Value); byte[] fieldToken = BitConverter.GetBytes(sourceOperation.MetadataToken.Value); ilToReplace.AddRange(fieldToken); //Load getter and setter MethodInfo getMethod = sourceProperty.GetGetMethod(); MethodInfo setMethod = destProperty.GetSetMethod(); byte[] getToken = BitConverter.GetBytes(getMethod.MetadataToken); byte[] setToken = BitConverter.GetBytes(setMethod.MetadataToken); //callvirt instance variablePasseedByArgument.Property1 (get) ilToReplace.Add((byte)OpCodes.Callvirt.Value); ilToReplace.AddRange(getToken); //callvirt instance variable.Property1 (set) ilToReplace.Add((byte)OpCodes.Callvirt.Value); ilToReplace.AddRange(setToken); } } //Replace operations from AutoMapper int endMapperIndex = startMapper.ILIndex; //All operations BEFORE IMapper.Map byte[] startIL = body.IL[..endMapperIndex];