private void InsertAndBoxConstant(Injector injector, object constant, TypeReference type, TypeReference boxType = null) { if (type.IsType <string>()) { injector.Insert(OpCodes.Ldstr, (string)constant); } else if (type.IsType <int>()) { injector.Insert(OpCodes.Ldc_I4, (int)constant); } else if (type.IsType <long>()) { injector.Insert(OpCodes.Ldc_I8, (long)constant); } else if (type.IsType <double>()) { injector.Insert(OpCodes.Ldc_R8, (double)constant); } else if (type.IsType <float>()) { injector.Insert(OpCodes.Ldc_R4, (float)constant); } else if (type.IsType <short>()) { injector.Insert(OpCodes.Ldc_I4, (short)constant); } else if (type.IsType <byte>()) { injector.Insert(OpCodes.Ldc_I4, (byte)constant); } else if (type.IsType <uint>()) { injector.Insert(OpCodes.Ldc_I4, (int)(uint)constant); } else if (type.IsType <ulong>()) { injector.Insert(OpCodes.Ldc_I8, (long)(ulong)constant); } else if (type.IsType <ushort>()) { injector.Insert(OpCodes.Ldc_I4, (ushort)constant); } else if (type.IsType <sbyte>()) { injector.Insert(OpCodes.Ldc_I4, (sbyte)constant); } if (boxType != null) { injector.Insert(Instruction.Create(OpCodes.Box, boxType)); } Logger.Warning($"Unknown constant type {constant.GetType().FullName}"); }
private void Emit(CilCodegenInstruction instruction) { if (instruction is CilMarkTargetInstruction) { pendingTargets.Add(((CilMarkTargetInstruction)instruction).Target); } else if (instruction is CilOpInstruction) { var opInsn = (CilOpInstruction)instruction; // Emit the instruction. Emit(opInsn.Op); // Add an entry to the patch list if necessary. if (opInsn.Patch != null) { patches.Add(opInsn); } } else if (instruction is CilExceptionHandlerInstruction) { var handlerInsn = (CilExceptionHandlerInstruction)instruction; // Create the actual exception handler. var handler = new Mono.Cecil.Cil.ExceptionHandler(handlerInsn.Type); if (handlerInsn.Type == Mono.Cecil.Cil.ExceptionHandlerType.Catch) { handler.CatchType = handlerInsn.CatchType; } Processor.Body.ExceptionHandlers.Add(handler); // Emit the try block's contents. Record the last instruction // prior to the try block. We'll use it to find the first instruction // inside the try block when we add a handler to the var preTryInstruction = Processor.Body.Instructions.LastOrDefault(); foreach (var insn in handlerInsn.TryBlock) { Emit(insn); } // Similarly, record the last instruction of the 'try' block and then // proceed by emitting the handler block. var lastTryInstruction = Processor.Body.Instructions.LastOrDefault(); foreach (var insn in handlerInsn.HandlerBlock) { Emit(insn); } // Populate the exception handler's start/end fields. handler.TryStart = preTryInstruction == null ? Processor.Body.Instructions.First() : preTryInstruction.Next; handler.TryEnd = lastTryInstruction == null ? Processor.Body.Instructions.First() : lastTryInstruction.Next; handler.HandlerStart = handler.TryEnd; handler.HandlerEnd = Processor.Create(OpCodes.Nop); Processor.Append(handler.HandlerEnd); } else if (instruction is CilLoadRegisterInstruction) { var loadInsn = (CilLoadRegisterInstruction)instruction; var reg = RegisterAllocation.GetRegister(loadInsn.Value); if (reg.IsParameter) { Emit(CilInstruction.Create(OpCodes.Ldarg, reg.ParameterOrNull)); } else { IncrementUseCount(reg.VariableOrNull); Emit(CilInstruction.Create(OpCodes.Ldloc, reg.VariableOrNull)); } } else if (instruction is CilAddressOfRegisterInstruction) { var addressOfInsn = (CilAddressOfRegisterInstruction)instruction; var reg = RegisterAllocation.GetRegister(addressOfInsn.Value); if (reg.IsParameter) { Emit(CilInstruction.Create(OpCodes.Ldarga, reg.ParameterOrNull)); } else { IncrementUseCount(reg.VariableOrNull); Emit(CilInstruction.Create(OpCodes.Ldloca, reg.VariableOrNull)); } } else { var storeInsn = (CilStoreRegisterInstruction)instruction; if (RegisterAllocation.Allocation.ContainsKey(storeInsn.Value)) { var reg = RegisterAllocation.GetRegister(storeInsn.Value); if (reg.IsParameter) { Emit(CilInstruction.Create(OpCodes.Starg, reg.ParameterOrNull)); } else { IncrementUseCount(reg.VariableOrNull); Emit(CilInstruction.Create(OpCodes.Stloc, reg.VariableOrNull)); } } else { Emit(CilInstruction.Create(OpCodes.Pop)); } } }
private void Emit(CilCodegenInstruction instruction) { if (instruction is CilMarkTargetInstruction) { pendingTargets.Add(((CilMarkTargetInstruction)instruction).Target); } else if (instruction is CilOpInstruction) { var opInsn = (CilOpInstruction)instruction; // Emit the instruction. Emit(opInsn.Op); // Add an entry to the patch list if necessary. if (opInsn.Patch != null) { patches.Add(opInsn); } } else if (instruction is CilTryStartMarker) { var tag = new BasicBlockTag("try-start"); pendingTargets.Add(tag); pendingTryHandlers.Push(tag); } else if (instruction is CilHandlerStartMarker) { var tryStart = pendingTryHandlers.Pop(); var catchStart = new BasicBlockTag("catch-start"); pendingTargets.Add(catchStart); pendingHandlers.Push( Tuple.Create( tryStart, catchStart, ((CilHandlerStartMarker)instruction).Handler)); } else if (instruction is CilHandlerEndMarker) { var handlerTriple = pendingHandlers.Pop(); var tryStart = handlerTriple.Item1; var handlerStart = handlerTriple.Item2; var handler = handlerTriple.Item3; Processor.Body.ExceptionHandlers.Add(handler); // Populate the exception handler's start/end fields. handler.TryStart = branchTargets[tryStart]; handler.TryEnd = branchTargets[handlerStart]; handler.HandlerStart = handler.TryEnd; handler.HandlerEnd = Processor.Create(OpCodes.Nop); Processor.Append(handler.HandlerEnd); } else if (instruction is CilLoadRegisterInstruction) { var loadInsn = (CilLoadRegisterInstruction)instruction; var reg = RegisterAllocation.GetRegister(loadInsn.Value); if (reg.IsParameter) { Emit(CilInstruction.Create(OpCodes.Ldarg, reg.ParameterOrNull)); } else { IncrementUseCount(reg.VariableOrNull); Emit(CilInstruction.Create(OpCodes.Ldloc, reg.VariableOrNull)); } } else if (instruction is CilAddressOfRegisterInstruction) { var addressOfInsn = (CilAddressOfRegisterInstruction)instruction; var reg = RegisterAllocation.GetRegister(addressOfInsn.Value); if (reg.IsParameter) { Emit(CilInstruction.Create(OpCodes.Ldarga, reg.ParameterOrNull)); } else { IncrementUseCount(reg.VariableOrNull); Emit(CilInstruction.Create(OpCodes.Ldloca, reg.VariableOrNull)); } } else { var storeInsn = (CilStoreRegisterInstruction)instruction; if (RegisterAllocation.Allocation.ContainsKey(storeInsn.Value)) { var reg = RegisterAllocation.GetRegister(storeInsn.Value); if (reg.IsParameter) { Emit(CilInstruction.Create(OpCodes.Starg, reg.ParameterOrNull)); } else { IncrementUseCount(reg.VariableOrNull); Emit(CilInstruction.Create(OpCodes.Stloc, reg.VariableOrNull)); } } else { Emit(CilInstruction.Create(OpCodes.Pop)); } } }
private void ProcessMethod(TypeDefinition type, MethodDefinition method, ICodeGenerator generator) { List <ParameterDefinition> dependencyParameters = method.Parameters.Where( p => p.CustomAttributes.Any(a => a.AttributeType.IsType(Import.AutoDI.DependencyAttributeType))).ToList(); List <PropertyDefinition> dependencyProperties = method.IsConstructor ? type.Properties.Where(p => p.CustomAttributes.Any(a => a.AttributeType.IsType(Import.AutoDI.DependencyAttributeType))).ToList() : new List <PropertyDefinition>(); if (dependencyParameters.Any() || dependencyProperties.Any()) { Logger.Debug($"Processing method '{method.Name}' for '{method.DeclaringType.FullName}'", DebugLogLevel.Verbose); var injector = new Injector(method); IMethodGenerator methodGenerator = generator?.Method(method); foreach (ParameterDefinition parameter in dependencyParameters) { if (!parameter.IsOptional) { Logger.Info( $"Constructor parameter {parameter.ParameterType.Name} {parameter.Name} is marked with {Import.AutoDI.DependencyAttributeType.FullName} but is not an optional parameter. In {type.FullName}."); } if (parameter.Constant != null) { Logger.Warning( $"Constructor parameter {parameter.ParameterType.Name} {parameter.Name} in {type.FullName} does not have a null default value. AutoDI will only resolve dependencies that are null"); } var initInstruction = Instruction.Create(OpCodes.Ldarg, parameter); var storeInstruction = Instruction.Create(OpCodes.Starg, parameter); ResolveDependency(parameter.ParameterType, parameter, new[] { initInstruction }, null, storeInstruction, parameter.Name); } foreach (PropertyDefinition property in dependencyProperties) { FieldDefinition backingField = null; //Store the return from the resolve method in the method parameter if (property.SetMethod == null) { //NB: Constant string, compiler detail... yuck yuck and double duck backingField = property.DeclaringType.Fields.FirstOrDefault(f => f.Name == $"<{property.Name}>k__BackingField"); if (backingField == null) { Logger.Warning( $"{property.FullName} is marked with {Import.AutoDI.DependencyAttributeType.FullName} but cannot be set. Dependency properties must either be auto properties or have a setter"); continue; } } //injector.Insert(OpCodes.Call, property.GetMethod); ResolveDependency(property.PropertyType, property, new[] { Instruction.Create(OpCodes.Ldarg_0), Instruction.Create(OpCodes.Call, property.GetMethod), }, Instruction.Create(OpCodes.Ldarg_0), property.SetMethod != null ? Instruction.Create(OpCodes.Call, property.SetMethod) : Instruction.Create(OpCodes.Stfld, backingField), property.Name); } methodGenerator?.Append($"//We now return you to your regularly scheduled method{Environment.NewLine}"); method.Body.OptimizeMacros(); void ResolveDependency(TypeReference dependencyType, ICustomAttributeProvider source, Instruction[] loadSource, Instruction resolveAssignmentTarget, Instruction setResult, string dependencyName) { //Push dependency parameter onto the stack if (methodGenerator != null) { methodGenerator.Append($"if ({dependencyName} == null)", loadSource.First()); methodGenerator.Append(Environment.NewLine + "{" + Environment.NewLine); } injector.Insert(loadSource); var afterParam = Instruction.Create(OpCodes.Nop); //Push null onto the stack injector.Insert(OpCodes.Ldnull); //Push 1 if the values are equal, 0 if they are not equal injector.Insert(OpCodes.Ceq); //Branch if the value is false (0), the dependency was set by the caller we wont replace it injector.Insert(OpCodes.Brfalse_S, afterParam); //Push the dependency resolver onto the stack if (resolveAssignmentTarget != null) { injector.Insert(resolveAssignmentTarget); } //Create parameters array var dependencyAttribute = source.CustomAttributes.First(x => x.AttributeType.IsType(Import.AutoDI.DependencyAttributeType)); var values = (dependencyAttribute.ConstructorArguments?.FirstOrDefault().Value as CustomAttributeArgument[]) ?.Select(x => x.Value) .OfType <CustomAttributeArgument>() .ToArray(); //Create array of appropriate length Instruction loadArraySize = injector.Insert(OpCodes.Ldc_I4, values?.Length ?? 0); if (methodGenerator != null) { methodGenerator.Append($" {dependencyName} = GlobalDI.GetService<{dependencyType.FullNameCSharp()}>();", resolveAssignmentTarget ?? loadArraySize); methodGenerator.Append(Environment.NewLine); } injector.Insert(OpCodes.Newarr, ModuleDefinition.ImportReference(Import.System.Object)); if (values?.Length > 0) { for (int i = 0; i < values.Length; ++i) { injector.Insert(OpCodes.Dup); //Push the array index to insert injector.Insert(OpCodes.Ldc_I4, i); //Insert constant value with any boxing/conversion needed InsertObjectConstant(injector, values[i].Value, values[i].Type.Resolve()); //Push the object into the array at index injector.Insert(OpCodes.Stelem_Ref); } } //Call the resolve method var getServiceMethod = new GenericInstanceMethod(Import.AutoDI.GlobalDI.GetService) { GenericArguments = { ModuleDefinition.ImportReference(dependencyType) } }; injector.Insert(OpCodes.Call, getServiceMethod); //Set the return from the resolve method into the parameter injector.Insert(setResult); injector.Insert(afterParam); if (methodGenerator != null) { methodGenerator.Append("}", afterParam); methodGenerator.Append(Environment.NewLine); } } } }
/// <summary> /// Creates a CIL instruction that is emitted as-is. /// </summary> /// <param name="op">The opcode to emit.</param> public CilOpInstruction(OpCode op) : this(CilInstruction.Create(op), null) { }