예제 #1
0
 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}");
 }
예제 #2
0
            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));
                    }
                }
            }
예제 #3
0
            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));
                    }
                }
            }
예제 #4
0
        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);
                    }
                }
            }
        }
예제 #5
0
 /// <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)
 {
 }