private void WeaveTypeProperties(TypeDefinition typeDefinition)
        {
            var properties = typeDefinition.Properties.ToArray();

            foreach (var propertyDefinition in properties)
            {
                var property = propertyDefinition.TryGetPropertyInfo();
                if (property == null)
                {
                    ModuleWeaver.LogInfo($"Property {propertyDefinition.Name} from type {typeDefinition.FullName} will not be weaved because it was not possible to load its corresponding PropertyInfo");
                    continue;
                }

                if (WeavedProperties.ContainsKey(property))
                {
                    // if the property we're trying to weave was already weaved we'll skip weaving it
                    // all aspects are applied the first time the property is weaved, there's no need to weave it more than once
                    continue;
                }
                else
                {
                    // mark this property so that it won't be weaved again
                    WeavedProperties.Add(property, true);
                }

                var aspects = ModuleWeaver.Setup.GetInterceptPropertyAspects(property).ToArray();
                if (aspects.Length <= 0)
                {
                    ModuleWeaver.LogInfo($"Property {propertyDefinition.Name} from type {typeDefinition.FullName} will not be weaved because no aspect was applied to it");
                    continue;
                }

                // clone the original getter because we're going to rewrite it
                var clonedGetMethod = propertyDefinition.GetMethod != null?MethodWeaverHelper.CloneMethod(typeDefinition, propertyDefinition.GetMethod) : null;

                if (clonedGetMethod != null)
                {
                    typeDefinition.Methods.Add(clonedGetMethod);
                }

                // clone the original setter because we're going to rewrite it
                var clonedSetMethod = propertyDefinition.SetMethod != null?MethodWeaverHelper.CloneMethod(typeDefinition, propertyDefinition.SetMethod) : null;

                if (clonedSetMethod != null)
                {
                    typeDefinition.Methods.Add(clonedSetMethod);
                }

                // creates a Binding class that inherits from PropertyBinding
                // this class is used to invoke the aspects in a way that works with generic methods
                var propertyGenericParameters = (propertyDefinition.GetMethod ?? propertyDefinition.SetMethod).GenericParameters;
                var bindingTypeDef            = MethodWeaverHelper.CreateBindingTypeDef(typeDefinition, typeof(PropertyBinding), propertyDefinition.Name, propertyGenericParameters);

                // creates the static INSTANCE field that will hold the Binding instance, and adds it to our class
                var instanceField = MethodWeaverHelper.CreateBindingInstanceField(bindingTypeDef);
                bindingTypeDef.Fields.Add(instanceField);

                // creates the constructor of and adds it to our Binding class
                var ctor = MethodWeaverHelper.CreateBindingCtor(bindingTypeDef, typeof(PropertyBinding), typeof(IInterceptPropertyAspect));
                bindingTypeDef.Methods.Add(ctor);

                // creates the static constructor and adds it to our Binding class
                var staticCtor = MethodWeaverHelper.CreateBindingStaticCtor(bindingTypeDef, instanceField, ctor, typeof(IInterceptPropertyAspect), aspects);
                bindingTypeDef.Methods.Add(staticCtor);

                // adds our Binding class as a nested type of the class that we're weaving
                typeDefinition.NestedTypes.Add(bindingTypeDef);

                if (clonedGetMethod != null)
                {
                    // creates the ProceedGet method that overrides the abstract method PropertyBinding.ProceedGet
                    var bindingFlags             = BindingFlags.Instance | BindingFlags.NonPublic;
                    var abstractProceedGetMethod = ModuleWeaver.ModuleDefinition.ImportReference(typeof(PropertyBinding).GetMethod("ProceedGet", bindingFlags));
                    var proceedGetMethod         = MethodWeaverHelper.CreateBindingProceedMethod(typeDefinition, bindingTypeDef, typeof(PropertyContext), clonedGetMethod, abstractProceedGetMethod, false);
                    bindingTypeDef.Methods.Add(proceedGetMethod);

                    // rewrites the original method so that it calls PropertyBinding.RunGetter
                    MethodWeaverHelper.RewriteOriginalMethod(typeDefinition, bindingTypeDef, nameof(PropertyBinding.RunGetter), typeof(PropertyBinding), typeof(PropertyContext), propertyDefinition.GetMethod, false);
                }

                if (clonedSetMethod != null)
                {
                    // creates the ProceedGet method that overrides the abstract method PropertyBinding.ProceedSet
                    var bindingFlags             = BindingFlags.Instance | BindingFlags.NonPublic;
                    var abstractProceedSetMethod = ModuleWeaver.ModuleDefinition.ImportReference(typeof(PropertyBinding).GetMethod("ProceedSet", bindingFlags));
                    var proceedSetMethod         = MethodWeaverHelper.CreateBindingProceedMethod(typeDefinition, bindingTypeDef, typeof(PropertyContext), clonedSetMethod, abstractProceedSetMethod, false);
                    bindingTypeDef.Methods.Add(proceedSetMethod);

                    // rewrites the original method so that it calls PropertyBinding.RunSetter
                    MethodWeaverHelper.RewriteOriginalMethod(typeDefinition, bindingTypeDef, nameof(PropertyBinding.RunSetter), typeof(PropertyBinding), typeof(PropertyContext), propertyDefinition.SetMethod, false);
                }
            }
        }
        private void WeaveTypeMethods(TypeDefinition typeDefinition)
        {
            var methods = typeDefinition.Methods.ToArray();

            foreach (var originalMethod in methods)
            {
                // do not weave property getters/setters because this is done by the IntercetPropertyAspectsWeaver
                if (originalMethod.IsGetter || originalMethod.IsSetter)
                {
                    continue;
                }

                var method = originalMethod.TryGetMethodBase();
                if (method == null)
                {
                    ModuleWeaver.LogInfo($"Method {originalMethod.Name} from type {typeDefinition.FullName} will not be weaved because it was not possible to load its corresponding MethodBase");
                    continue;
                }

                if (method.IsAbstract)
                {
                    ModuleWeaver.LogInfo($"Method {originalMethod.Name} from type {typeDefinition.FullName} will not be weaved because it is an abstract method");
                    continue;
                }

                if (WeavedMethods.ContainsKey(method))
                {
                    // if the method we're trying to weave was already weaved we'll skip weaving it
                    // all aspects are applied the first time the method is weaved, there's no need to weave it more than once
                    continue;
                }
                else
                {
                    // mark this method so that it won't be weaved again
                    WeavedMethods.Add(method, true);
                }

                if (method.IsAsyncMethod())
                {
                    // if the method is async (uses the async keyword) we use a different way of getting the aspects of these methods
                    // this is done because aspects of async methods must implement IInterceptAsyncMethodAspect instead of IInterceptMethodAspect
                    var aspects = ModuleWeaver.Setup.GetInterceptAsyncMethodAspects(method).ToArray();
                    if (aspects.Length <= 0)
                    {
                        ModuleWeaver.LogInfo($"Method {originalMethod.Name} from type {typeDefinition.FullName} will not be weaved because no aspect was applied to it");
                        continue;
                    }

                    // clone the original method because we're going to rewrite it
                    var clonedMethod = MethodWeaverHelper.CloneMethod(typeDefinition, originalMethod);
                    typeDefinition.Methods.Add(clonedMethod);

                    // creates a Binding class that inherits from AsyncMethodBinding
                    // this class is used to invoke the aspects in a way that works with generic methods
                    var bindingTypeDef = MethodWeaverHelper.CreateBindingTypeDef(typeDefinition, typeof(AsyncMethodBinding), originalMethod.Name, originalMethod.GenericParameters);

                    // creates the static INSTANCE field that will hold the Binding instance, and adds it to our class
                    var instanceField = MethodWeaverHelper.CreateBindingInstanceField(bindingTypeDef);
                    bindingTypeDef.Fields.Add(instanceField);

                    // creates the constructor of and adds it to our Binding class
                    var ctor = MethodWeaverHelper.CreateBindingCtor(bindingTypeDef, typeof(AsyncMethodBinding), typeof(IInterceptAsyncMethodAspect));
                    bindingTypeDef.Methods.Add(ctor);

                    // creates the static constructor and adds it to our Binding class
                    var staticCtor = MethodWeaverHelper.CreateBindingStaticCtor(bindingTypeDef, instanceField, ctor, typeof(IInterceptAsyncMethodAspect), aspects);
                    bindingTypeDef.Methods.Add(staticCtor);

                    // creates the Proceed method that overrides the abstract method MethodBinding.Proceed/AsyncMethodBinding.Proceed
                    var bindingFlags          = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
                    var abstractProceedMethod = ModuleWeaver.ModuleDefinition.ImportReference(typeof(AsyncMethodBinding).GetMethod("Proceed", bindingFlags));
                    var proceedMethod         = MethodWeaverHelper.CreateBindingProceedMethod(typeDefinition, bindingTypeDef, typeof(AsyncMethodContext), clonedMethod, abstractProceedMethod, true);
                    bindingTypeDef.Methods.Add(proceedMethod);

                    // adds our Binding class as a nested type of the class that we're weaving
                    typeDefinition.NestedTypes.Add(bindingTypeDef);

                    // rewrites the original method so that it calls AsyncMethodBinding.Run
                    MethodWeaverHelper.RewriteOriginalMethod(typeDefinition, bindingTypeDef, nameof(AsyncMethodBinding.Run), typeof(AsyncMethodBinding), typeof(AsyncMethodContext), originalMethod, true);
                }
                else
                {
                    // if the method is NOT async get the aspects that implement IInterceptMethodAspect
                    var aspects = ModuleWeaver.Setup.GetInterceptMethodAspects(method).ToArray();
                    if (aspects.Length <= 0)
                    {
                        ModuleWeaver.LogInfo($"Method {originalMethod.Name} from type {typeDefinition.FullName} will not be weaved because no aspect was applied to it");
                        continue;
                    }

                    // clone the original method because we're going to rewrite it
                    var clonedMethod = MethodWeaverHelper.CloneMethod(typeDefinition, originalMethod);
                    typeDefinition.Methods.Add(clonedMethod);

                    // creates a Binding class that inherits from MethodBinding
                    // this class is used to invoke the aspects in a way that works with generic methods
                    var bindingTypeDef = MethodWeaverHelper.CreateBindingTypeDef(typeDefinition, typeof(MethodBinding), originalMethod.Name, originalMethod.GenericParameters);

                    // creates the static INSTANCE field that will hold the Binding instance, and adds it to our class
                    var instanceField = MethodWeaverHelper.CreateBindingInstanceField(bindingTypeDef);
                    bindingTypeDef.Fields.Add(instanceField);

                    // creates the constructor of and adds it to our Binding class
                    var ctor = MethodWeaverHelper.CreateBindingCtor(bindingTypeDef, typeof(MethodBinding), typeof(IInterceptMethodAspect));
                    bindingTypeDef.Methods.Add(ctor);

                    // creates the static constructor and adds it to our Binding class
                    var staticCtor = MethodWeaverHelper.CreateBindingStaticCtor(bindingTypeDef, instanceField, ctor, typeof(IInterceptMethodAspect), aspects);
                    bindingTypeDef.Methods.Add(staticCtor);

                    // creates the Proceed method that overrides the abstract method MethodBinding.Proceed/AsyncMethodBinding.Proceed
                    var bindingFlags          = System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic;
                    var abstractProceedMethod = ModuleWeaver.ModuleDefinition.ImportReference(typeof(MethodBinding).GetMethod("Proceed", bindingFlags));
                    var proceedMethod         = MethodWeaverHelper.CreateBindingProceedMethod(typeDefinition, bindingTypeDef, typeof(MethodContext), clonedMethod, abstractProceedMethod, false);
                    bindingTypeDef.Methods.Add(proceedMethod);

                    // adds our Binding class as a nested type of the class that we're weaving
                    typeDefinition.NestedTypes.Add(bindingTypeDef);

                    // rewrites the original method so that it calls MethodBinding.Run
                    MethodWeaverHelper.RewriteOriginalMethod(typeDefinition, bindingTypeDef, nameof(MethodBinding.Run), typeof(MethodBinding), typeof(MethodContext), originalMethod, false);
                }
            }
        }