private bool AddOrUpdateOnPropertyChangedMethod(PropertyDefinition property)
        {
            var getMethodReference          = _catelType.TypeDefinition.Module.ImportReference(_catelType.AdvancedPropertyChangedEventArgsType.GetProperty("PropertyName").Resolve().GetMethod);
            var stringEqualsMethodReference = _catelType.TypeDefinition.Module.ImportReference(GetSystemObjectEqualsMethodReference(_catelType.TypeDefinition.Module));

            var dependentProperties = _catelType.GetDependentPropertiesFrom(property).ToList();

            if (dependentProperties.Count > 0 && !dependentProperties.All(definition => _catelType.NoWeavingProperties.Contains(definition)))
            {
                var onPropertyChangedMethod = EnsureOnPropertyChangedMethod();
                if (onPropertyChangedMethod == null)
                {
                    FodyEnvironment.LogWarning($"No call to base.OnPropertyChanged(e) or a custom implementation in '{property.DeclaringType.Name}', cannot weave this method to automatically raise on dependent property change notifications");
                    return(false);
                }

                var idx = onPropertyChangedMethod.Body.Instructions.ToList().FindLastIndex(instruction => instruction.OpCode == OpCodes.Ret);
                if (idx > -1)
                {
                    var booleanTypeReference = _catelType.TypeDefinition.Module.ImportReference(_msCoreReferenceFinder.GetCoreTypeReference("Boolean"));
                    if (onPropertyChangedMethod.Body.Variables.ToList().FirstOrDefault(definition => definition.VariableType != booleanTypeReference) == null)
                    {
                        onPropertyChangedMethod.Body.Variables.Add(new VariableDefinition(booleanTypeReference));
                        onPropertyChangedMethod.Body.InitLocals = true;
                    }

                    foreach (var propertyDefinition in dependentProperties)
                    {
                        if (_catelType.NoWeavingProperties.Contains(propertyDefinition))
                        {
                            continue;
                        }

                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldarg_1));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Callvirt, getMethodReference));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldstr, property.Name));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Callvirt, stringEqualsMethodReference));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldc_I4_0));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ceq));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Stloc_0));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldloc_0));

                        var jmpIdx = idx++;
                        onPropertyChangedMethod.Body.Instructions.Insert(jmpIdx, Instruction.Create(OpCodes.Nop));

                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Nop));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldarg_0));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Ldstr, propertyDefinition.Name));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Call, _catelType.RaisePropertyChangedInvoker));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Nop));
                        onPropertyChangedMethod.Body.Instructions.Insert(idx++, Instruction.Create(OpCodes.Nop));

                        onPropertyChangedMethod.Body.Instructions[jmpIdx] = Instruction.Create(OpCodes.Brtrue_S, onPropertyChangedMethod.Body.Instructions[idx - 1]);
                    }
                }
            }

            return(true);
        }
Exemplo n.º 2
0
        private void ProcessProperty(CatelType catelType, CatelTypeProperty modelProperty, CustomAttribute exposeAttribute)
        {
            var modelName             = modelProperty.Name;
            var viewModelPropertyName = (string)exposeAttribute.ConstructorArguments[0].Value;
            var modelPropertyName     = viewModelPropertyName;

            if (exposeAttribute.ConstructorArguments.Count > 1)
            {
                modelPropertyName = (string)(exposeAttribute.ConstructorArguments[1].Value ?? viewModelPropertyName);
            }

            bool isReadOnly         = false;
            var  isReadOnlyProperty = (from property in exposeAttribute.Properties
                                       where string.Equals(property.Name, "IsReadOnly")
                                       select property).FirstOrDefault();

            if (isReadOnlyProperty.Argument.Value != null)
            {
                isReadOnly = (bool)isReadOnlyProperty.Argument.Value;
            }

            // Check property definition on model
            var modelType          = modelProperty.PropertyDefinition.PropertyType;
            var modelPropertyToMap = modelType.GetProperty(modelPropertyName);

            if (modelPropertyToMap == null)
            {
                FodyEnvironment.LogError($"Exposed property '{modelPropertyName}' does not exist on model '{modelType.FullName}', make sure to set the right mapping");
                return;
            }

            var modelPropertyType = modelPropertyToMap.PropertyType;

            var viewModelPropertyDefinition = new PropertyDefinition(viewModelPropertyName, PropertyAttributes.None, FodyEnvironment.ModuleDefinition.Import(modelPropertyType));

            viewModelPropertyDefinition.DeclaringType = catelType.TypeDefinition;

            catelType.TypeDefinition.Properties.Add(viewModelPropertyDefinition);

            viewModelPropertyDefinition.MarkAsCompilerGenerated(_msCoreReferenceFinder);

            var catelTypeProperty = new CatelTypeProperty(catelType.TypeDefinition, viewModelPropertyDefinition);

            catelTypeProperty.IsReadOnly = isReadOnly;

            var catelPropertyWeaver = new CatelPropertyWeaver(catelType, catelTypeProperty, _msCoreReferenceFinder);

            catelPropertyWeaver.Execute(true);

            var stringType           = _msCoreReferenceFinder.GetCoreTypeReference("String");
            var stringTypeDefinition = catelType.TypeDefinition.Module.Import(stringType);

            var attributeConstructor      = catelType.TypeDefinition.Module.Import(ViewModelToModelAttributeTypeDefinition.Constructor(false));
            var viewModelToModelAttribute = new CustomAttribute(attributeConstructor);

            viewModelToModelAttribute.ConstructorArguments.Add(new CustomAttributeArgument(stringTypeDefinition, modelName));
            viewModelToModelAttribute.ConstructorArguments.Add(new CustomAttributeArgument(stringTypeDefinition, modelPropertyName));
            viewModelPropertyDefinition.CustomAttributes.Add(viewModelToModelAttribute);
        }
        public MethodDefinition Execute()
        {
            var debugWriteLineMethod = FindDebugWriteLineMethod();

            if (debugWriteLineMethod == null)
            {
                FodyEnvironment.LogInfo("Can't find Debug.WriteLine, won't be writing debug info during assembly loading");
            }

            var loadMethod = new MethodDefinition("LoadTypesOnStartup", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, _moduleDefinition.ImportReference(_msCoreReferenceFinder.GetCoreTypeReference("Void")));

            var type                    = _msCoreReferenceFinder.GetCoreTypeReference("Type").Resolve();
            var typeImported            = _moduleDefinition.ImportReference(type);
            var getTypeFromHandleMethod = type.Methods.First(x => string.Equals(x.Name, "GetTypeFromHandle"));
            var getTypeFromHandle       = _moduleDefinition.ImportReference(getTypeFromHandleMethod);

            var body = loadMethod.Body;

            body.SimplifyMacros();

            var instructions = body.Instructions;

            if (instructions.Count == 0)
            {
                instructions.Add(Instruction.Create(OpCodes.Ret));
            }

            var referenceSelector = new ReferenceSelector(_moduleWeaver, _moduleDefinition, _configuration);

            // Note: we are looping reversed to easily add try/catch mechanism
            foreach (var assembly in referenceSelector.GetIncludedReferences().Reverse())
            {
                var firstType = assembly.MainModule.Types.FirstOrDefault(x => x.IsClass && x.IsPublic);
                if (firstType != null)
                {
                    FodyEnvironment.LogInfo($"Adding code to force load assembly '{assembly.Name}'");

                    if (debugWriteLineMethod != null)
                    {
                        // L_0001: ldstr "Loading assembly TestAssemblyToReference"
                        //L_0006: call void [System]System.Diagnostics.Debug::WriteLine(string)

                        // Temporarily disabled because we first need to investigate if this is ever useful
                        //instructions.Add(Instruction.Create(OpCodes.Ldstr, string.Format("Loading assembly {0}", assembly.Name)));
                        //instructions.Add(Instruction.Create(OpCodes.Call, debugWriteLineMethod));
                    }

                    // var type = typeof(FirstTypeInAssembly);
                    // ==
                    //L_000a: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

                    var firstTypeImported = _moduleDefinition.ImportReference(firstType);

                    var variable = new VariableDefinition(typeImported);
                    body.Variables.Insert(0, variable);

                    var instructionsToAdd = new[]
                    {
                        Instruction.Create(OpCodes.Ldtoken, firstTypeImported),
                        Instruction.Create(OpCodes.Call, getTypeFromHandle),
                        Instruction.Create(OpCodes.Stloc, variable)
                    };

                    instructions.Insert(0, instructionsToAdd);

                    if (_configuration.WrapInTryCatch)
                    {
                        var firstInstructionAfterInjectedSet = instructions[instructionsToAdd.Length];

                        // Pop means empty catch
                        var emptyCatchInstructions = new[]
                        {
                            Instruction.Create(OpCodes.Leave_S, firstInstructionAfterInjectedSet),
                            Instruction.Create(OpCodes.Pop),
                            Instruction.Create(OpCodes.Leave_S, firstInstructionAfterInjectedSet)
                        };

                        instructions.Insert(instructionsToAdd.Length, emptyCatchInstructions);

                        var tryStartInstruction     = instructionsToAdd.First();
                        var tryEndInstruction       = emptyCatchInstructions.Skip(1).First();
                        var handlerStartInstruction = emptyCatchInstructions.Skip(1).First();
                        var handlerEndInstruction   = firstInstructionAfterInjectedSet;

                        var handler = new ExceptionHandler(ExceptionHandlerType.Catch)
                        {
                            TryStart     = tryStartInstruction,
                            TryEnd       = tryEndInstruction,
                            HandlerStart = handlerStartInstruction,
                            HandlerEnd   = handlerEndInstruction,
                            CatchType    = _moduleDefinition.ImportReference(_msCoreReferenceFinder.GetCoreTypeReference("Exception"))
                        };

                        body.ExceptionHandlers.Insert(0, handler);
                    }
                }
            }

            instructions.Add(Instruction.Create(OpCodes.Ret));

            body.OptimizeMacros();

            //.class public abstract auto ansi sealed beforefieldinit LoadAssembliesOnStartup extends [mscorlib]System.Object
            var typeDefinition = new TypeDefinition(string.Empty, "LoadAssembliesOnStartup", TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.AutoClass | TypeAttributes.AnsiClass | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit,
                                                    _moduleDefinition.ImportReference(_msCoreReferenceFinder.GetCoreTypeReference("System.Object")));

            typeDefinition.Methods.Add(loadMethod);
            _moduleDefinition.Types.Add(typeDefinition);

            return(loadMethod);
        }
Exemplo n.º 4
0
        public void ImportModuleLoader(ModuleDefinition moduleDefinition, MethodReference methodToCall,
                                       MsCoreReferenceFinder msCoreReferenceFinder)
        {
            const MethodAttributes attributes = MethodAttributes.Private
                                                | MethodAttributes.HideBySig
                                                | MethodAttributes.Static
                                                | MethodAttributes.SpecialName
                                                | MethodAttributes.RTSpecialName;

            var moduleClass = moduleDefinition.Types.FirstOrDefault(x => x.Name == "<Module>");

            if (moduleClass is null)
            {
                throw new WeavingException("Found no module class!");
            }

            var cctor = moduleClass.Methods.FirstOrDefault(x => x.Name == ".cctor");

            if (cctor is null)
            {
                cctor = new MethodDefinition(".cctor", attributes, moduleDefinition.ImportReference(msCoreReferenceFinder.GetCoreTypeReference("Void")));
                cctor.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
                moduleClass.Methods.Add(cctor);
            }

            var importedMethodToCall = moduleDefinition.ImportReference(methodToCall);

            var insertLocation = Math.Max(cctor.Body.Instructions.Count - 2, 0);

            cctor.Body.Instructions.Insert(insertLocation, Instruction.Create(OpCodes.Call, importedMethodToCall));
        }