Exemple #1
0
        /// <summary>
        /// Need to do this so we retain pointer from original first instruction to new first instruction (nop)
        /// for dbg file to point to it so VS debugger will step into weaved methods
        /// </summary>
        /// <param name="ilProcessor"></param>
        /// <param name="firstInstruction"></param>
        /// <returns></returns>
        private static Instruction RejigFirstInstruction(ILProcessor ilProcessor, Instruction firstInstruction)
        {
            /*
             * From:
             * opcode operand <-- pdb first line pointer
             *
             * To:
             * nop <-- pdb first line pointer
             * opcode operand <-- cloned second acting as first
             *
             */
            // clone first instruction which will be used as actual
            var clonedSecond = CloneInstruction(firstInstruction);

            clonedSecond.Offset++;

            var sampleNop = ilProcessor.Create(OpCodes.Nop);

            // change actual first instruction to NOP
            firstInstruction.OpCode  = sampleNop.OpCode;
            firstInstruction.Operand = sampleNop.Operand;

            // append second instruction which now is same as first one used to be at the start of this method
            // and actual first one is nop
            firstInstruction.Append(clonedSecond, ilProcessor);

            // return cloned second as new first instruction
            return(clonedSecond);
        }
Exemple #2
0
        public static Instruction AppendBoxIfNecessary(this Instruction instruction, ILProcessor processor, TypeReference typeReference)
        {
            if (typeReference.IsValueType || typeReference.IsGenericParameter)
            {
                return(instruction.Append(processor.Create(OpCodes.Box, typeReference), processor));
            }

            return(instruction);
        }
Exemple #3
0
        public static Instruction AppendBoxAndResolveRefIfNecessary(this Instruction instruction, ILProcessor processor,
                                                                    TypeReference typeReference)
        {
            // some of the following code is partially based on IL injection blog post from Girish Jain
            if (typeReference.IsByReference)
            {
                // it might throw an exception, prefer for weaving to fail in such case
                var referencedTypeSpec = (TypeSpecification)typeReference;

                var byRefLoadOpCode = GetByRefLoadOpCode(referencedTypeSpec.ElementType.MetadataType);

                if (byRefLoadOpCode.HasValue)
                {
                    return(instruction
                           .Append(processor.Create(byRefLoadOpCode.Value), processor)
                           .Append(processor.Create(OpCodes.Box, referencedTypeSpec.ElementType), processor));
                }
                else if (referencedTypeSpec.ElementType.IsValueType)
                {
                    // no "known" mapping from referenced metadata type
                    // check if we have value type and default to Ldobj
                    return(instruction
                           .Append(processor.Create(OpCodes.Ldobj, referencedTypeSpec.ElementType), processor)
                           .Append(processor.Create(OpCodes.Box, referencedTypeSpec.ElementType), processor));
                }

                // It is a ref reference type so just use reference the pointer
                return(instruction.Append(processor.Create(OpCodes.Ldind_Ref), processor));
            }
            else if (typeReference.IsValueType || typeReference.IsGenericParameter)
            {
                // If it is a value type then you need to box the instance as we are going
                // to add it to an array which is of type object (reference type)
                // ------------------------------------------------------------

                // Box the parameter type
                return(instruction.Append(processor.Create(OpCodes.Box, typeReference), processor));
            }

            return(instruction);
        }
        private void WeavePropertySetter(MethodDefinition setter, MethodReference propertyGet)
        {
            setter.Body.InitLocals = true;
            setter.Body.SimplifyMacros();

            if (propertyGet.DeclaringType.HasGenericParameters)
            {
                propertyGet = propertyGet.MakeHostInstanceGeneric(propertyGet.DeclaringType.GenericParameters.Cast <TypeReference>().ToArray());
            }

            Instruction firstInstruction = setter.Body.Instructions.First();
            ILProcessor processor        = setter.Body.GetILProcessor();

            // Add local variables
            int cacheKeyIndex = setter.AddVariable(ModuleDefinition.TypeSystem.String);

            // Generate CacheKeyTemplate
            string cacheKey = CreateCacheKeyString(setter);

            Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);

            // Create set cache key
            current = current.AppendStloc(processor, cacheKeyIndex);

            current = InjectCacheKeyCreatedCode(setter, current, processor, cacheKeyIndex);

            if (LogDebugOutput)
            {
                current = AppendDebugWrite(current, processor, "Clearing cache.");
            }

            if (!propertyGet.Resolve().IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            current
            .Append(processor.Create(OpCodes.Call, setter.Module.Import(propertyGet)), processor)
            .AppendLdloc(processor, cacheKeyIndex)
            .Append(processor.Create(OpCodes.Callvirt, setter.Module.Import(
                                         CacheTypeGetRemoveMethod(propertyGet.ReturnType.Resolve(), CacheTypeRemoveMethodName))), processor);

            setter.Body.OptimizeMacros();
        }
        private void WeavePropertySetter(MethodDefinition setter, MethodDefinition propertyGet)
        {
            setter.Body.InitLocals = true;
            setter.Body.SimplifyMacros();

            Instruction firstInstruction = setter.Body.Instructions.First();
            ILProcessor processor        = setter.Body.GetILProcessor();

            // Add local variables
            int cacheKeyIndex = setter.AddVariable <string>();

            // Generate CacheKeyTemplate
            string cacheKey = CreateCacheKeyString(setter);

            Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);

            // Create set cache key
            current = current.AppendStloc(processor, cacheKeyIndex);

            current = InjectCacheKeyCreatedCode(setter, current, processor, cacheKeyIndex);

            if (LogDebugOutput)
            {
                current = current.AppendDebugWrite(processor, "Clearing cache.", ModuleDefinition);
            }

            if (!setter.IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            current.Append(processor.Create(OpCodes.Call, setter.Module.Import(propertyGet)), processor)
            .AppendLdloc(processor, cacheKeyIndex)
            .Append(
                processor.Create(OpCodes.Callvirt,
                                 setter.Module.Import(CacheTypeGetRemoveMethod(propertyGet.ReturnType.Resolve(), CacheTypeRemoveMethodName))),
                processor);

            setter.Body.OptimizeMacros();
        }
Exemple #6
0
        private void WeaveMethod(MethodDefinition methodDefinition, MethodDefinition propertyGet)
        {
            methodDefinition.Body.InitLocals = true;

            methodDefinition.Body.SimplifyMacros();

            Instruction firstInstruction = methodDefinition.Body.Instructions.First();

            ICollection <Instruction> returnInstructions =
                methodDefinition.Body.Instructions.ToList().Where(x => x.OpCode == OpCodes.Ret).ToList();

            if (returnInstructions.Count == 0)
            {
                LogWarning(string.Format("Method {0} does not contain any return statement. Skip weaving of method {0}.",
                                         methodDefinition.Name));
                return;
            }

            // Add local variables
            int cacheKeyIndex    = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.String);
            int resultIndex      = methodDefinition.AddVariable(methodDefinition.ReturnType);
            int objectArrayIndex = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.Object.MakeArrayType());

            ILProcessor processor = methodDefinition.Body.GetILProcessor();

            // Generate CacheKeyTemplate
            string cacheKey = CreateCacheKeyString(methodDefinition);

            Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);

            current = SetCacheKeyLocalVariable(current, methodDefinition, processor, cacheKeyIndex, objectArrayIndex);

            current = InjectCacheKeyCreatedCode(methodDefinition, current, processor, cacheKeyIndex);

            TypeDefinition propertyGetReturnTypeDefinition = propertyGet.ReturnType.Resolve();

            if (!methodDefinition.IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            MethodReference methodReferenceContain =
                methodDefinition.Module.Import(CacheTypeGetContainsMethod(propertyGetReturnTypeDefinition,
                                                                          CacheTypeContainsMethodName));

            current =
                current.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
                .AppendLdloc(processor, cacheKeyIndex)
                .Append(processor.Create(OpCodes.Callvirt, methodReferenceContain), processor)
                .Append(processor.Create(OpCodes.Brfalse, firstInstruction), processor);

            // False branche (store value in cache of each return instruction)
            foreach (Instruction returnInstruction in returnInstructions)
            {
                returnInstruction.Previous.AppendStloc(processor, resultIndex);

                if (LogDebugOutput)
                {
                    AppendDebugWrite(returnInstruction.Previous, processor, "Storing to cache.");
                }

                if (!methodDefinition.IsStatic)
                {
                    returnInstruction.Previous.AppendLdarg(processor, 0);
                }

                MethodReference methodReferenceReturn =
                    methodDefinition.Module.Import(CacheTypeGetStoreMethod(propertyGetReturnTypeDefinition, CacheTypeStoreMethodName));

                returnInstruction.Previous.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)),
                                                  processor)
                .AppendLdloc(processor, cacheKeyIndex)
                .AppendLdloc(processor, resultIndex)
                .AppendBoxIfNecessary(processor, methodDefinition.ReturnType)
                .Append(processor.Create(OpCodes.Callvirt, methodReferenceReturn), processor)
                .AppendLdloc(processor, resultIndex);
            }

            if (LogDebugOutput)
            {
                current = AppendDebugWrite(current, processor, "Loading from cache.");
            }

            if (!methodDefinition.IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            // Start of branche true
            MethodReference methodReferenceRetrieve =
                methodDefinition.Module.Import(CacheTypeGetRetrieveMethod(propertyGetReturnTypeDefinition,
                                                                          CacheTypeRetrieveMethodName)).MakeGeneric(new[] { methodDefinition.ReturnType });

            current.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
            .AppendLdloc(processor, cacheKeyIndex)
            .Append(processor.Create(OpCodes.Callvirt, methodReferenceRetrieve), processor)
            .AppendStloc(processor, resultIndex)
            .Append(processor.Create(OpCodes.Br, returnInstructions.Last().Previous), processor);

            methodDefinition.Body.OptimizeMacros();
        }
Exemple #7
0
 public static Instruction AppendStloc(this Instruction instruction, ILProcessor processor, int index)
 {
     return(instruction.Append(processor.Create(OpCodes.Stloc, index), processor));
 }
Exemple #8
0
 public static Instruction AppendLdstr(this Instruction instruction, ILProcessor processor, string str)
 {
     return(instruction.Append(processor.Create(OpCodes.Ldstr, str), processor));
 }
Exemple #9
0
 public static Instruction AppendLdcI4(this Instruction instruction, ILProcessor processor, int value)
 {
     return(instruction.Append(processor.Create(OpCodes.Ldc_I4, value), processor));
 }
Exemple #10
0
        public static Instruction AppendLd(this Instruction instruction, ILProcessor processor, CustomAttributeArgument argument)
        {
            // Tested with the following types (see ECMA-334, 24.1.3 attribute parameter types)
            // bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
            // object
            // System.Type
            // An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (§17.2)
            // Single-dimensional arrays of the above types
            TypeReference argumentType = argument.Type;

            switch (argumentType.MetadataType)
            {
            case MetadataType.ValueType:
                if (argumentType.Resolve().IsEnum == false)
                {
                    throw new ArgumentException("Type Enum expected.", "argument");
                }

                // Get underlying Enum type
                argumentType = argument.Type.Resolve().Fields.First(field => field.Name == "value__").FieldType;
                break;

            case MetadataType.Object:
                return(instruction.AppendLd(processor, (CustomAttributeArgument)argument.Value));

            case MetadataType.Array:
                CustomAttributeArgument[] values = (CustomAttributeArgument[])argument.Value;

                instruction = instruction
                              .AppendLdcI4(processor, values.Length)
                              .Append(processor.Create(OpCodes.Newarr, argument.Type.GetElementType()), processor);

                TypeReference arrayType = argument.Type.GetElementType();

                for (int i = 0; i < values.Length; i++)
                {
                    instruction = instruction
                                  .AppendDup(processor)
                                  .AppendLdcI4(processor, i)
                                  .AppendLd(processor, values[i]);

                    if (arrayType == ReferenceFinder.Weaver.ModuleDefinition.TypeSystem.Object)
                    {
                        instruction = instruction.AppendBoxIfNecessary(processor, ((CustomAttributeArgument)values[i].Value).Type);
                    }
                    else if (argumentType.Resolve().IsEnum)
                    {
                        // Get underlying Enum type
                        arrayType = argument.Type.Resolve().Fields.First(field => field.Name == "value__").FieldType;
                    }

                    if (arrayType.IsValueType)
                    {
                        switch (arrayType.MetadataType)
                        {
                        case MetadataType.Boolean:
                        case MetadataType.SByte:
                        case MetadataType.Byte:
                            instruction = instruction.Append(processor.Create(OpCodes.Stelem_I1), processor);
                            break;

                        case MetadataType.Char:
                        case MetadataType.Int16:
                        case MetadataType.UInt16:
                            instruction = instruction.Append(processor.Create(OpCodes.Stelem_I2), processor);
                            break;

                        case MetadataType.Int32:
                        case MetadataType.UInt32:
                            instruction = instruction.Append(processor.Create(OpCodes.Stelem_I4), processor);
                            break;

                        case MetadataType.Int64:
                        case MetadataType.UInt64:
                            instruction = instruction.Append(processor.Create(OpCodes.Stelem_I8), processor);
                            break;

                        case MetadataType.Single:
                            instruction = instruction.Append(processor.Create(OpCodes.Stelem_R4), processor);
                            break;

                        case MetadataType.Double:
                            instruction = instruction.Append(processor.Create(OpCodes.Stelem_R8), processor);
                            break;

                        default:
                            throw new ArgumentException("Unrecognized array value type.", "argument");
                        }
                    }
                    else
                    {
                        instruction = instruction.Append(processor.Create(OpCodes.Stelem_Ref), processor);
                    }
                }

                return(instruction);
            }

            switch (argumentType.MetadataType)
            {
            case MetadataType.Boolean:
            case MetadataType.SByte:
                return(instruction.Append(processor.Create(OpCodes.Ldc_I4_S, Convert.ToSByte(argument.Value)), processor));

            case MetadataType.Int16:
                return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt16(argument.Value)), processor));

            case MetadataType.Int32:
                return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt32(argument.Value)), processor));

            case MetadataType.Int64:
                return(instruction.Append(processor.Create(OpCodes.Ldc_I8, Convert.ToInt64(argument.Value)), processor));

            case MetadataType.Byte:
                return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToInt32(argument.Value)), processor));

            case MetadataType.UInt16:
                unchecked
                {
                    return(instruction.Append(processor.Create(OpCodes.Ldc_I4, (Int16)(UInt16)argument.Value), processor));
                }

            case MetadataType.Char:
                return(instruction.Append(processor.Create(OpCodes.Ldc_I4, Convert.ToChar(argument.Value)), processor));

            case MetadataType.UInt32:
                unchecked
                {
                    return(instruction.Append(processor.Create(OpCodes.Ldc_I4, (Int32)(UInt32)argument.Value), processor));
                }

            case MetadataType.UInt64:
                unchecked
                {
                    return(instruction.Append(processor.Create(OpCodes.Ldc_I8, (Int64)(UInt64)argument.Value), processor));
                }

            case MetadataType.Single:
                return(instruction.Append(processor.Create(OpCodes.Ldc_R4, Convert.ToSingle(argument.Value)), processor));

            case MetadataType.Double:
                return(instruction.Append(processor.Create(OpCodes.Ldc_R8, Convert.ToDouble(argument.Value)), processor));

            case MetadataType.String:
                return(instruction.Append(processor.Create(OpCodes.Ldstr, (string)argument.Value), processor));

            case MetadataType.Class:
                if (argumentType.Resolve().IsDefinition == false)
                {
                    throw new ArgumentException("Type Type expected.", "argument");
                }

                return
                    (instruction
                     .Append(processor.Create(OpCodes.Ldtoken, (TypeReference)argument.Value), processor)
                     .Append(processor.Create(OpCodes.Call, ReferenceFinder.Weaver.ModuleDefinition.ImportMethod(
                                                  ReferenceFinder.SystemTypeGetTypeFromHandleMethod)), processor));

            default:
                throw new ArgumentException("Unrecognized attribute parameter type.", "argument");
            }
        }
Exemple #11
0
 public static Instruction AppendDup(this Instruction instruction, ILProcessor processor)
 {
     return(instruction.Append(processor.Create(OpCodes.Dup), processor));
 }
Exemple #12
0
        private void WeaveMethod(MethodDefinition methodDefinition, CustomAttribute attribute, MethodReference propertyGet)
        {
            methodDefinition.Body.InitLocals = true;

            methodDefinition.Body.SimplifyMacros();

            if (propertyGet.DeclaringType.HasGenericParameters)
            {
                propertyGet = propertyGet.MakeHostInstanceGeneric(propertyGet.DeclaringType.GenericParameters.Cast <TypeReference>().ToArray());
            }

            Instruction firstInstruction = methodDefinition.Body.Instructions.First();

            ICollection <Instruction> returnInstructions =
                methodDefinition.Body.Instructions.ToList().Where(x => x.OpCode == OpCodes.Ret).ToList();

            if (returnInstructions.Count == 0)
            {
                LogWarning(string.Format("Method {0} does not contain any return statement. Skip weaving of method {0}.",
                                         methodDefinition.Name));
                return;
            }

            // Add local variables
            int cacheKeyIndex    = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.String);
            int resultIndex      = methodDefinition.AddVariable(methodDefinition.ReturnType);
            int objectArrayIndex = methodDefinition.AddVariable(ModuleDefinition.TypeSystem.Object.MakeArrayType());

            ILProcessor processor = methodDefinition.Body.GetILProcessor();

            // Generate CacheKeyTemplate
            string cacheKey = CreateCacheKeyString(methodDefinition);

            Instruction current = firstInstruction.Prepend(processor.Create(OpCodes.Ldstr, cacheKey), processor);

            current = SetCacheKeyLocalVariable(current, methodDefinition, processor, cacheKeyIndex, objectArrayIndex);

            current = InjectCacheKeyCreatedCode(methodDefinition, current, processor, cacheKeyIndex);

            TypeDefinition propertyGetReturnTypeDefinition = propertyGet.ReturnType.Resolve();

            if (!propertyGet.Resolve().IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            MethodReference methodReferenceContain =
                methodDefinition.Module.Import(CacheTypeGetContainsMethod(propertyGetReturnTypeDefinition,
                                                                          CacheTypeContainsMethodName));

            current = current
                      .Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
                      .AppendLdloc(processor, cacheKeyIndex)
                      .Append(processor.Create(OpCodes.Callvirt, methodReferenceContain), processor)
                      .Append(processor.Create(OpCodes.Brfalse, firstInstruction), processor);

            // False branche (store value in cache of each return instruction)
            foreach (Instruction returnInstruction in returnInstructions)
            {
                returnInstruction.Previous.AppendStloc(processor, resultIndex);

                if (LogDebugOutput)
                {
                    AppendDebugWrite(returnInstruction.Previous, processor, "Storing to cache.");
                }

                if (!propertyGet.Resolve().IsStatic)
                {
                    returnInstruction.Previous.AppendLdarg(processor, 0);
                }

                MethodReference methodReferenceStore =
                    methodDefinition.Module.Import(CacheTypeGetStoreMethod(propertyGetReturnTypeDefinition, CacheTypeStoreMethodName));

                returnInstruction.Previous
                .Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
                .AppendLdloc(processor, cacheKeyIndex)
                .AppendLdloc(processor, resultIndex)
                .AppendBoxIfNecessary(processor, methodDefinition.ReturnType);

                // Pass parameters to Store method if supported
                if (methodReferenceStore.Parameters.Count == 3)
                {
                    returnInstruction.Previous
                    .Append(processor.Create(OpCodes.Newobj,
                                             methodDefinition.Module.Import(References.DictionaryConstructor)), processor);

                    foreach (CustomAttributeNamedArgument property in attribute.Properties.Union(attribute.Fields))
                    {
                        returnInstruction.Previous
                        .AppendDup(processor)
                        .AppendLdstr(processor, property.Name)
                        .AppendLd(processor, property.Argument, References)
                        .AppendBoxIfNecessary(processor,
                                              property.Argument.Type != ModuleDefinition.TypeSystem.Object
                                                                        ? property.Argument.Type : ((CustomAttributeArgument)property.Argument.Value).Type)
                        .Append(processor.Create(OpCodes.Callvirt, methodDefinition.Module.Import(References.DictionaryAddMethod)),
                                processor);
                    }
                }

                returnInstruction.Previous
                .Append(processor.Create(OpCodes.Callvirt, methodReferenceStore), processor)
                .AppendLdloc(processor, resultIndex);
            }

            if (LogDebugOutput)
            {
                current = AppendDebugWrite(current, processor, "Loading from cache.");
            }

            if (!propertyGet.Resolve().IsStatic)
            {
                current = current.AppendLdarg(processor, 0);
            }

            // Start of branche true
            MethodReference methodReferenceRetrieve =
                methodDefinition.Module.Import(CacheTypeGetRetrieveMethod(propertyGetReturnTypeDefinition,
                                                                          CacheTypeRetrieveMethodName)).MakeGeneric(new[] { methodDefinition.ReturnType });

            current.Append(processor.Create(OpCodes.Call, methodDefinition.Module.Import(propertyGet)), processor)
            .AppendLdloc(processor, cacheKeyIndex)
            .Append(processor.Create(OpCodes.Callvirt, methodReferenceRetrieve), processor)
            .AppendStloc(processor, resultIndex)
            .Append(processor.Create(OpCodes.Br, returnInstructions.Last().Previous), processor);

            methodDefinition.Body.OptimizeMacros();
        }