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); }
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); }
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)); }