private bool ProcessDelegateAllocation(AssemblyProcessorContext context, MethodDefinition method, Instruction delegateAllocationInstruction)
        {
            // The instruction must be a delegate allocation
            // If not, this might be a static delegate, or some unsupported construct
            if (delegateAllocationInstruction.OpCode != OpCodes.Newobj)
                return false;

            var delegateInstanceConstructor = (MethodReference)delegateAllocationInstruction.Operand;
            var delegateInstanceType = delegateInstanceConstructor.DeclaringType;

            // The previous instruction pushes the delegate method onto the stack
            var functionPointerInstruction = delegateAllocationInstruction.Previous;
            if (functionPointerInstruction.OpCode != OpCodes.Ldftn)
                return false;

            var delegateMethod = (MethodReference)functionPointerInstruction.Operand;

            // The previous instruction pushes the target onto the stack
            // If it's the this-parameter, we can create an instance field, and reuse the same delegate
            var loadClosureInstruction = functionPointerInstruction.Previous;
            if (loadClosureInstruction.OpCode == OpCodes.Ldarg_0 && !method.IsStatic)
            {
                // TODO: Handle generic methods/delegates
                // TODO: Handle multiple constructors propertly
                var constructor = method.DeclaringType.Methods.FirstOrDefault(x => x.Name == ".ctor" && !x.HasParameters);
                var retInstruction3 = constructor?.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret);
                if (retInstruction3 == null)
                    return false;

                // Create an instance field for the shared delegate
                var sharedDelegateField = new FieldDefinition($"<delegate>{delegateMethod.Name}", FieldAttributes.Private, delegateInstanceType);
                method.DeclaringType.Fields.Add(sharedDelegateField);

                // Create and store the delegate in constructor
                var ilProcessor5 = constructor.Body.GetILProcessor();
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldarg_0));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldarg_0));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Ldftn, delegateMethod));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Newobj, delegateInstanceConstructor));
                ilProcessor5.InsertBefore(retInstruction3, ilProcessor5.Create(OpCodes.Stfld, sharedDelegateField));

                // Load from field instead of allocating
                var ilProcessor4 = method.Body.GetILProcessor();
                ilProcessor4.Remove(functionPointerInstruction);
                ilProcessor4.Replace(delegateAllocationInstruction, ilProcessor4.Create(OpCodes.Ldfld, sharedDelegateField));

                return true;
            }

            // If the target is a compiler generated closure, we only handle local variable load instructions
            int variableIndex;
            OpCode storeOpCode;
            if (!TryGetStoreOpcode(loadClosureInstruction, out storeOpCode, out variableIndex))
                return false;

            // Find the instruction that stores the closure variable
            var storeClosureInstruction = loadClosureInstruction.Previous;
            VariableReference closureVarible = null;

            while (storeClosureInstruction != null)
            {
                closureVarible = storeClosureInstruction.Operand as VariableReference;
                if (storeClosureInstruction.OpCode == storeOpCode && (closureVarible == null || variableIndex == closureVarible.Index))
                    break;

                storeClosureInstruction = storeClosureInstruction.Previous;
            }
            if (storeClosureInstruction == null)
                return false;

            var closureInstanceType = method.Body.Variables[variableIndex].VariableType;
            var closureType = closureInstanceType.Resolve();
            var genericParameters = closureType.GenericParameters.Cast<TypeReference>().ToArray();

            // Patch closure
            var closure = ProcessClosure(context, closureType, genericParameters);

            // Create delegate field
            var delegateFieldType = ChangeGenericArguments(context, delegateInstanceType, closureInstanceType);
            var delegateField = new FieldDefinition($"<delegate>{delegateMethod.Name}", FieldAttributes.Public, delegateFieldType);
            closureType.Fields.Add(delegateField);
            var localDelegateFieldInstance = delegateField.MakeGeneric(genericParameters);

            // Initialize delegate field (the closure instance (local 0) is already on the stack)
            var delegateConstructorInstance = (MethodReference)delegateAllocationInstruction.Operand;
            var delegateGenericArguments = (delegateFieldType as GenericInstanceType)?.GenericArguments.ToArray() ?? new TypeReference[0];
            var genericDelegateConstructor = context.Assembly.MainModule.ImportReference(delegateConstructorInstance.Resolve()).MakeGeneric(delegateGenericArguments);

            var methodInstance = (MethodReference)functionPointerInstruction.Operand;
            var genericMethod = methodInstance.Resolve().MakeGeneric(closureType.GenericParameters.ToArray());

            if (methodInstance is GenericInstanceMethod)
                throw new NotImplementedException();

            var ilProcessor3 = closure.FactoryMethod.Body.GetILProcessor();
            var returnInstruction = ilProcessor3.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret);
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldloc_0));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldftn, genericMethod));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Newobj, genericDelegateConstructor));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Stfld, localDelegateFieldInstance));
            ilProcessor3.InsertBefore(returnInstruction, ilProcessor3.Create(OpCodes.Ldloc_0));

            var ilProcessor = method.Body.GetILProcessor();

            // Retrieve from pool
            var closureGenericArguments = (closureInstanceType as GenericInstanceType)?.GenericArguments.ToArray() ?? new TypeReference[0];
            var closureAllocation = storeClosureInstruction.Previous;
            if (closureAllocation.OpCode == OpCodes.Newobj)
            {
                // Retrieve closure from pool, instead of allocating
                var acquireClosure = ilProcessor.Create(OpCodes.Callvirt, poolAcquireMethod.MakeGeneric(closureInstanceType));
                ilProcessor.InsertAfter(closureAllocation, acquireClosure);
                ilProcessor.InsertAfter(closureAllocation, ilProcessor.Create(OpCodes.Ldsfld, closure.PoolField.MakeGeneric(closureGenericArguments)));
                closureAllocation.OpCode = OpCodes.Nop; // Change to Nop instead of removing it, as this instruction might be reference somewhere?
                closureAllocation.Operand = null;

                // Add a reference
                ilProcessor.InsertAfter(storeClosureInstruction, ilProcessor.Create(OpCodes.Callvirt, closure.AddReferenceMethod.MakeGeneric(closureGenericArguments)));
                ilProcessor.InsertAfter(storeClosureInstruction, closureVarible == null ? ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve()));

                // TODO: Multiple returns + try/finally
                // Release reference
                var retInstructions = method.Body.Instructions.Where(x => x.OpCode == OpCodes.Ret).ToArray();

                Instruction beforeReturn = closureVarible == null ? ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve());
                Instruction newReturnInstruction = ilProcessor.Create(OpCodes.Ret);
                ilProcessor.Append(beforeReturn);
                ilProcessor.Append(ilProcessor.Create(OpCodes.Ldnull));
                ilProcessor.Append(ilProcessor.Create(OpCodes.Beq, newReturnInstruction));
                ilProcessor.Append(closureVarible == null ? ilProcessor.Create(loadClosureInstruction.OpCode) : ilProcessor.Create(loadClosureInstruction.OpCode, closureVarible.Resolve()));
                ilProcessor.Append(ilProcessor.Create(OpCodes.Callvirt, closure.ReleaseMethod.MakeGeneric(closureGenericArguments)));
                ilProcessor.Append(newReturnInstruction);

                foreach (var retInstruction2 in retInstructions)
                {
                    retInstruction2.OpCode = OpCodes.Br;
                    retInstruction2.Operand = beforeReturn;
                }
            }

            // Get delegate from closure, instead of allocating
            ilProcessor.Remove(functionPointerInstruction);
            ilProcessor.Replace(delegateAllocationInstruction, ilProcessor.Create(OpCodes.Ldfld, delegateField.MakeGeneric(closureGenericArguments))); // Closure object is already on the stack

            return true;
        }
        private ClosureInfo ProcessClosure(AssemblyProcessorContext context, TypeDefinition closureType, TypeReference[] genericParameters)
        {
            ClosureInfo closure;
            if (closures.TryGetValue(closureType, out closure))
                return closure;

            var closureTypeConstructor = closureType.Methods.FirstOrDefault(x => x.Name == ".ctor");
            var closureGenericType = closureType.MakeGenericType(genericParameters);

            // Create factory method for pool items
            var factoryMethod = new MethodDefinition("<Factory>", MethodAttributes.HideBySig | MethodAttributes.Private | MethodAttributes.Static, closureGenericType);
            closureType.Methods.Add(factoryMethod);
            factoryMethod.Body.InitLocals = true;
            factoryMethod.Body.Variables.Add(new VariableDefinition(closureGenericType));
            var factoryMethodProcessor = factoryMethod.Body.GetILProcessor();
            // Create and store closure
            factoryMethodProcessor.Emit(OpCodes.Newobj, closureTypeConstructor.MakeGeneric(genericParameters));
            factoryMethodProcessor.Emit(OpCodes.Stloc_0);
            //// Return closure
            factoryMethodProcessor.Emit(OpCodes.Ldloc_0);
            factoryMethodProcessor.Emit(OpCodes.Ret);

            // Create pool field
            var poolField = new FieldDefinition("<pool>", FieldAttributes.Public | FieldAttributes.Static, poolType.MakeGenericType(closureGenericType));
            closureType.Fields.Add(poolField);
            var poolFieldReference = poolField.MakeGeneric(genericParameters);

            // Initialize pool
            var cctor = GetOrCreateClassConstructor(closureType);
            var ilProcessor2 = cctor.Body.GetILProcessor();
            var retInstruction = cctor.Body.Instructions.FirstOrDefault(x => x.OpCode == OpCodes.Ret);
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Ldnull));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Ldftn, factoryMethod.MakeGeneric(genericParameters)));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Newobj, funcConstructor.MakeGeneric(closureGenericType)));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Newobj, poolConstructor.MakeGeneric(closureGenericType)));
            ilProcessor2.InsertBefore(retInstruction, ilProcessor2.Create(OpCodes.Stsfld, poolFieldReference));

            // Implement IPooledClosure
            closureType.Interfaces.Add(pooledClosureType);

            // Create reference count field
            var countField = new FieldDefinition("<referenceCount>", FieldAttributes.Public, context.Assembly.MainModule.TypeSystem.Int32);
            closureType.Fields.Add(countField);
            var oountFieldReference = countField.MakeGeneric(genericParameters);

            // Create AddReference method
            var addReferenceMethod = new MethodDefinition("AddReference", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, context.Assembly.MainModule.TypeSystem.Void);
            var ilProcessor4 = addReferenceMethod.Body.GetILProcessor();
            ilProcessor4.Emit(OpCodes.Ldarg_0);
            ilProcessor4.Emit(OpCodes.Ldflda, oountFieldReference);
            ilProcessor4.Emit(OpCodes.Call, interlockedIncrementMethod);
            ilProcessor4.Emit(OpCodes.Pop);
            ilProcessor4.Emit(OpCodes.Ret);
            closureType.Methods.Add(addReferenceMethod);

            // Create Release method
            var releaseMethod = new MethodDefinition("Release", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.NewSlot, context.Assembly.MainModule.TypeSystem.Void);
            ilProcessor4 = releaseMethod.Body.GetILProcessor();
            retInstruction = ilProcessor4.Create(OpCodes.Ret);
            // Check decremented reference count
            ilProcessor4.Emit(OpCodes.Ldarg_0);
            ilProcessor4.Emit(OpCodes.Ldflda, oountFieldReference);
            ilProcessor4.Emit(OpCodes.Call, interlockedDecrementMethod);
            ilProcessor4.Emit(OpCodes.Ldc_I4_0);
            ilProcessor4.Emit(OpCodes.Ceq);
            ilProcessor4.Emit(OpCodes.Brfalse_S, retInstruction);
            // Release this to pool
            ilProcessor4.Emit(OpCodes.Ldsfld, poolFieldReference);
            ilProcessor4.Emit(OpCodes.Ldarg_0);
            ilProcessor4.Emit(OpCodes.Callvirt, poolReleaseMethod.MakeGeneric(closureGenericType));
            ilProcessor4.Append(retInstruction);
            closureType.Methods.Add(releaseMethod);

            closures.Add(closureType, closure = new ClosureInfo
            {
                FactoryMethod = factoryMethod,
                AddReferenceMethod = addReferenceMethod,
                ReleaseMethod = releaseMethod,
                PoolField = poolField
            });

            return closure;
        }
Ejemplo n.º 3
0
        public bool Process(AssemblyProcessorContext context)
        {
            var assembly = context.Assembly;
            var mscorlibAssembly = CecilExtensions.FindCorlibAssembly(assembly);
            if (mscorlibAssembly == null)
                throw new InvalidOperationException("Missing mscorlib.dll from assembly");

            // For now, use import, but this can cause mixed framework versions when processing an assembly with a different framework version.
            voidType = assembly.MainModule.TypeSystem.Void;
            stringType = assembly.MainModule.TypeSystem.String;
            objectType = assembly.MainModule.TypeSystem.Object;
            var propertyInfoType = assembly.MainModule.ImportReference(mscorlibAssembly.MainModule.GetTypeResolved(typeof(PropertyInfo).FullName));
            var typeType = mscorlibAssembly.MainModule.GetTypeResolved(typeof(Type).FullName);


            TypeDefinition propertyChangedExtendedEventArgsType;

            AssemblyDefinition siliconStudioCoreAssembly;
            try
            {
                siliconStudioCoreAssembly = assembly.Name.Name == "SiliconStudio.Core"
                    ? assembly
                    : context.AssemblyResolver.Resolve("SiliconStudio.Core");

            }
            catch (Exception)
            {
                return true;
            }

            propertyChangedExtendedEventArgsType = siliconStudioCoreAssembly.MainModule.GetTypes().First(x => x.Name == "PropertyChangedExtendedEventArgs").Resolve();

            var typeTokenInfoEx = mscorlibAssembly.MainModule.GetType("System.Reflection.TypeInfo").Resolve();
            var getPropertyMethod = typeTokenInfoEx.Methods.First(x => x.Name == "GetDeclaredProperty" && x.Parameters.Count == 1);
            var getTypeFromHandleMethod = typeType.Methods.First(x => x.Name == "GetTypeFromHandle");
            var getTokenInfoExMethod = mscorlibAssembly.MainModule.GetType("System.Reflection.IntrospectionExtensions").Resolve().Methods.First(x => x.Name == "GetTypeInfo");
            
            var propertyChangedExtendedEventArgsConstructor = assembly.MainModule.ImportReference(propertyChangedExtendedEventArgsType.Methods.First(x => x.IsConstructor));

            bool modified = false;

            foreach (var type in assembly.MainModule.GetTypes())
            {
                MethodReference getPropertyChangedMethod;

                getPropertyChangedMethod = GetGetPropertyChangedMethod(assembly, type);
                //var propertyChangedField = GetPropertyChangedField(type);
                //if (propertyChangedField == null)
                //    continue;

                var propertyChangedField = GetPropertyChangedField(type);

                if (getPropertyChangedMethod == null && propertyChangedField == null)
                    continue;

                TypeReference propertyChangedFieldType;

                if (getPropertyChangedMethod == null)
                {
                    modified = true;
                    getPropertyChangedMethod = GetOrCreateGetPropertyChangedMethod(assembly, type, propertyChangedField);
                }

                if (propertyChangedField != null)
                {
                    propertyChangedField = assembly.MainModule.ImportReference(propertyChangedField);
                    propertyChangedFieldType = propertyChangedField.FieldType;
                }
                else
                {
                    propertyChangedFieldType = getPropertyChangedMethod.ReturnType;
                }

                // Add generic to declaring type
                if (getPropertyChangedMethod.DeclaringType.HasGenericParameters)
                    getPropertyChangedMethod = getPropertyChangedMethod.MakeGeneric(getPropertyChangedMethod.DeclaringType.GenericParameters.ToArray());

                var propertyChangedInvoke = assembly.MainModule.ImportReference(propertyChangedFieldType.Resolve().Methods.First(x => x.Name == "Invoke"));

                foreach (var property in type.Properties)
                {
                    if (property.SetMethod == null || !property.HasThis)
                        continue;

                    MethodReference propertyGetMethod = property.GetMethod;

                    // Only patch properties that have a public Getter
                    var methodDefinition = propertyGetMethod.Resolve();
                    if ((methodDefinition.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
                    {
                        continue;
                    }

                    // Add generic to declaring type
                    if (propertyGetMethod.DeclaringType.HasGenericParameters)
                        propertyGetMethod = propertyGetMethod.MakeGeneric(propertyGetMethod.DeclaringType.GenericParameters.ToArray());

                    //var versionableAttribute = property.CustomAttributes.FirstOrDefault(x => x.AttributeType.FullName == typeof(VersionableAttribute).FullName);
                    //if (versionableAttribute == null)
                    //    continue;

                    modified = true;

                    FieldReference staticField = new FieldDefinition(property.Name + "PropertyInfo", FieldAttributes.Static | FieldAttributes.Private | FieldAttributes.InitOnly, propertyInfoType);
                    type.Fields.Add((FieldDefinition)staticField);

                    // Add generic to declaring type
                    if (staticField.DeclaringType.HasGenericParameters)
                        staticField = staticField.MakeGeneric(staticField.DeclaringType.GenericParameters.ToArray());

                    // In static constructor, find PropertyInfo and store it in static field
                    {
                        var staticConstructor = type.GetStaticConstructor();
                        if (staticConstructor == null)
                        {
                            staticConstructor = new MethodDefinition(".cctor",
                                                                     MethodAttributes.Static | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
                                                                     voidType);
                            staticConstructor.Body.GetILProcessor().Append(Instruction.Create(OpCodes.Ret));

                            type.Methods.Add(staticConstructor);
                        }


                        VariableReference localTokenEx = null;
                        int localTokenExIndex = 0;
                        for (int i = 0; i < staticConstructor.Body.Variables.Count; i++)
                        {
                            var localVar = staticConstructor.Body.Variables[i];
                            if (localVar.VariableType.FullName == typeTokenInfoEx.FullName)
                            {
                                localTokenEx = localVar;
                                localTokenExIndex = i;
                                break;
                            }
                        }

                        if (localTokenEx == null)
                        {
                            localTokenEx = new VariableDefinition(assembly.MainModule.ImportReference(typeTokenInfoEx));
                            staticConstructor.Body.Variables.Add((VariableDefinition)localTokenEx);
                            localTokenExIndex = staticConstructor.Body.Variables.Count - 1;
                        }

                        var ilProcessor = staticConstructor.Body.GetILProcessor();
                        var returnInstruction = staticConstructor.Body.Instructions.Last();

                        var newReturnInstruction = Instruction.Create(returnInstruction.OpCode);
                        newReturnInstruction.Operand = returnInstruction.Operand;

                        returnInstruction.OpCode = OpCodes.Nop;
                        returnInstruction.Operand = null;

                        // Find PropertyInfo and store it in static field
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldtoken, type));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTypeFromHandleMethod)));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getTokenInfoExMethod)));
                        ilProcessor.Append(LocationToStloc(ilProcessor, localTokenExIndex));
                        ilProcessor.Append(ilProcessor.Create(OpCodes.Ldloca_S, (byte)localTokenExIndex));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldstr, property.Name));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, assembly.MainModule.ImportReference(getPropertyMethod)));
                        ilProcessor.Append(Instruction.Create(OpCodes.Stsfld, staticField));

                        ilProcessor.Append(newReturnInstruction);
                    }
                    
                    {
                        var ilProcessor = property.SetMethod.Body.GetILProcessor();
                        var returnInstruction = property.SetMethod.Body.Instructions.Last();

                        var firstInstruction = property.SetMethod.Body.Instructions[0];

                        if (property.SetMethod.Body.Instructions[0].OpCode != OpCodes.Nop)
                            ilProcessor.InsertBefore(property.SetMethod.Body.Instructions[0], Instruction.Create(OpCodes.Nop));

                        var newReturnInstruction = Instruction.Create(returnInstruction.OpCode);
                        newReturnInstruction.Operand = returnInstruction.Operand;

                        returnInstruction.OpCode = OpCodes.Nop;
                        returnInstruction.Operand = null;

                        var propertyChangedVariable = new VariableDefinition("propertyChanged", assembly.MainModule.ImportReference(propertyChangedFieldType));
                        property.SetMethod.Body.Variables.Add(propertyChangedVariable);

                        var oldValueVariable = new VariableDefinition("oldValue", objectType);
                        property.SetMethod.Body.Variables.Add(oldValueVariable);

                        Instruction jump1, jump2;

                        // Prepend:
                        // var propertyChanged = GetPropertyChanged();
                        // var oldValue = propertyChanged != null ? (object)Property : null;
                        property.SetMethod.Body.SimplifyMacros();
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldarg_0));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Call, getPropertyChangedMethod));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Stloc, propertyChangedVariable));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldloc, propertyChangedVariable));
                        ilProcessor.InsertBefore(firstInstruction, jump1 = Instruction.Create(OpCodes.Brtrue, Instruction.Create(OpCodes.Nop)));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Ldnull));
                        ilProcessor.InsertBefore(firstInstruction, jump2 = Instruction.Create(OpCodes.Br, Instruction.Create(OpCodes.Nop)));
                        ilProcessor.InsertBefore(firstInstruction, (Instruction)(jump1.Operand = Instruction.Create(OpCodes.Ldarg_0)));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Call, propertyGetMethod));
                        if (property.PropertyType.IsValueType)
                            ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Box, property.PropertyType));
                        ilProcessor.InsertBefore(firstInstruction, (Instruction)(jump2.Operand = Instruction.Create(OpCodes.Nop)));
                        ilProcessor.InsertBefore(firstInstruction, Instruction.Create(OpCodes.Stloc, oldValueVariable));

                        // Append:
                        // if (propertyChanged != null)
                        //     propertyChanged(this, new PropertyChangedExtendedEventArgs("Property", oldValue, Property));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, propertyChangedVariable));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldnull));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ceq));
                        ilProcessor.Append(Instruction.Create(OpCodes.Brtrue, newReturnInstruction));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, propertyChangedVariable));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldarg_0));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldsfld, staticField));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldloc, oldValueVariable));
                        ilProcessor.Append(Instruction.Create(OpCodes.Ldarg_0));
                        ilProcessor.Append(Instruction.Create(OpCodes.Call, propertyGetMethod));
                        if (property.PropertyType.IsValueType)
                            ilProcessor.Append(Instruction.Create(OpCodes.Box, property.PropertyType));
                        ilProcessor.Append(Instruction.Create(OpCodes.Newobj, propertyChangedExtendedEventArgsConstructor));
                        ilProcessor.Append(Instruction.Create(OpCodes.Callvirt, propertyChangedInvoke));
                        ilProcessor.Append(Instruction.Create(OpCodes.Nop));
                        ilProcessor.Append(newReturnInstruction);
                        property.SetMethod.Body.OptimizeMacros();
                    }
                }
            }

            return modified;
        }