Esempio n. 1
0
 private static void ProcessClass(TypeDefinition classDefinition, SystemReferences systemReferences, ILogger logger, IDictionary <MethodDefinition, MethodDefinition> weavedMethods)
 {
     foreach (var property in classDefinition.Properties)
     {
         ProcessProperty(property, systemReferences, logger, weavedMethods);
     }
 }
Esempio n. 2
0
        private static void ProcessProperty(PropertyDefinition property, SystemReferences systemReferences, ILogger logger, IDictionary <MethodDefinition, MethodDefinition> weavedMethods)
        {
            if (!ConsumeLazyAttribute(property, out var threadingMode))
            {
                return;
            }

            if (property.HasParameters)
            {
                throw new WeavingException($"Unsupported property {property} => property has parameters");
            }

            if (property.SetMethod != null)
            {
                throw new WeavingException($"Unsupported property {property} => property has setter");
            }

            var originalMethod = property.GetMethod;

            if (originalMethod == null || !originalMethod.HasBody)
            {
                throw new WeavingException($"Unsupported property {property} => property has no getter");
            }

            var isStatic = originalMethod.IsStatic;

            var classDefinition = originalMethod.DeclaringType;

            logger.LogInfo($"Weave Lazy into {property.FullName}");

            var propertyType = property.PropertyType;

            var lazyTypeReference = systemReferences.LazyTypeReference;
            var funcTypeReference = systemReferences.FuncTypeReference;

            var genericLazyTypeInstance = lazyTypeReference.MakeGenericInstanceType(propertyType);
            var genericFuncTypeInstance = funcTypeReference.MakeGenericInstanceType(propertyType);

            var lazyFieldName  = $"<{property.Name}>_Lazy_Fody_BackingField";
            var lazyMethodName = $"<{property.Name}>_Lazy_Fody_Method";

            // replace the property getter with a new method: { return _Lazy_Fody_BackingField.Value }

            // -- create field of type: System.Lazy<property type>
            var lazyField = new FieldDefinition(lazyFieldName, FieldAttributes.Private, genericLazyTypeInstance);

            if (isStatic)
            {
                lazyField.IsStatic = true;
            }
            classDefinition.Fields.Add(lazyField);

            // -- create
            var originalMethodName = originalMethod.Name;
            var wrapperMethod      = new MethodDefinition(originalMethodName, originalMethod.Attributes, originalMethod.ReturnType);

            var lazyValueGetter = systemReferences.LazyValueGetterReference.OnGenericType(genericLazyTypeInstance);

            var wrapperMethodInstructions = wrapperMethod.Body.Instructions;

            // -- rename old method
            originalMethod.IsSpecialName = false;
            originalMethod.Name          = lazyMethodName;

            MethodReference originalMethodReference = originalMethod;
            FieldReference  lazyFieldReference      = lazyField;

            if (classDefinition.HasGenericParameters)
            {
                var classReference = classDefinition.MakeGenericInstanceType(classDefinition.GenericParameters.OfType <TypeReference>().ToArray());
                originalMethodReference = originalMethod.OnGenericType(classReference);
                lazyFieldReference      = new FieldReference(lazyField.Name, lazyField.FieldType, classReference);
            }

            if (isStatic)
            {
                wrapperMethodInstructions.AddRange(
                    Instruction.Create(OpCodes.Ldsfld, lazyFieldReference),
                    Instruction.Create(OpCodes.Call, lazyValueGetter),
                    Instruction.Create(OpCodes.Ret));
            }
            else
            {
                wrapperMethodInstructions.AddRange(
                    Instruction.Create(OpCodes.Ldarg_0),
                    Instruction.Create(OpCodes.Ldfld, lazyFieldReference),
                    Instruction.Create(OpCodes.Callvirt, lazyValueGetter),
                    Instruction.Create(OpCodes.Ret)
                    );
            }


            // -- add new wrapper method
            property.GetMethod = wrapperMethod;
            classDefinition.Methods.Add(wrapperMethod);

            // -- inject initialization of lazy field into constructor(s)
            var funcConstructor = systemReferences.FuncConstructorReference.OnGenericType(genericFuncTypeInstance);
            var lazyConstructor = systemReferences.LazyConstructorReference.OnGenericType(genericLazyTypeInstance);

            if (isStatic)
            {
                classDefinition.InsertIntoStaticConstructor(
                    Instruction.Create(OpCodes.Ldnull),
                    Instruction.Create(OpCodes.Ldftn, originalMethodReference),
                    Instruction.Create(OpCodes.Newobj, funcConstructor),
                    Instruction.Create(OpCodes.Ldc_I4, threadingMode),
                    Instruction.Create(OpCodes.Newobj, lazyConstructor),
                    Instruction.Create(OpCodes.Stsfld, lazyFieldReference));
            }
            else
            {
                classDefinition.InsertIntoConstructors(() => new[] {
                    Instruction.Create(OpCodes.Ldarg_0),
                    Instruction.Create(OpCodes.Ldarg_0),
                    Instruction.Create(OpCodes.Ldftn, originalMethodReference),
                    Instruction.Create(OpCodes.Newobj, funcConstructor),
                    Instruction.Create(OpCodes.Ldc_I4, threadingMode),
                    Instruction.Create(OpCodes.Newobj, lazyConstructor),
                    Instruction.Create(OpCodes.Stfld, lazyFieldReference)
                });
            }

            weavedMethods[originalMethod] = wrapperMethod;
        }
Esempio n. 3
0
        internal static void Process(this ModuleDefinition moduleDefinition, ILogger logger, SystemReferences coreReferences)
        {
            var allTypes = moduleDefinition.GetTypes();

            var allClasses = allTypes
                             .Where(x => x.IsClass && (x.BaseType != null))
                             .ToArray();

            var weavedMethods = new Dictionary <MethodDefinition, MethodDefinition>();

            foreach (var classDefinition in allClasses)
            {
                ProcessClass(classDefinition, coreReferences, logger, weavedMethods);
            }

            var injectedMethods = new HashSet <MethodDefinition>(weavedMethods.Values);

            foreach (var classDefinition in allClasses)
            {
                PostProcessClass(classDefinition, logger, weavedMethods, injectedMethods);
            }
        }