public void Execute(CatelType catelType) { if (catelType.TypeDefinition.IsAbstract) { return; } if (catelType.TypeDefinition.IsEnum) { return; } if (!catelType.TypeDefinition.ImplementsCatelModel()) { return; } if (catelType.TypeDefinition.ImplementsViewModelBase()) { return; } FodyEnvironment.LogDebug("\t\t Adding xml schema for type " + catelType.TypeDefinition.FullName); if (AddXmlSchemaProviderAttribute(catelType)) { AddGetXmlSchemaMethod(catelType); } }
private void UpdateCallsToGetCurrentClassLogger(MethodBody ctorBody) { // Convert this: // // call class [Catel.Core]Catel.Logging.ILog [Catel.Core]Catel.Logging.LogManager::GetCurrentClassLogger() // stsfld class [Catel.Core]Catel.Logging.ILog Catel.Fody.TestAssembly.LoggingClass::AutoLog // // into this: // // ldtoken Catel.Fody.TestAssembly.LoggingClass // call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle) // call class [Catel.Core]Catel.Logging.ILog [Catel.Core]Catel.Logging.LogManager::GetLogger(class [mscorlib]System.Type) // stsfld class [Catel.Core]Catel.Logging.ILog Catel.Fody.TestAssembly.LoggingClass::ManualLog var type = ctorBody.Method.DeclaringType; var instructions = ctorBody.Instructions; for (var i = 0; i < instructions.Count; i++) { var instruction = instructions[i]; var methodReference = instruction.Operand as MethodReference; if (methodReference != null) { FodyEnvironment.LogDebug($"Weaving auto log to specific log for '{type.FullName}'"); if (string.Equals(methodReference.Name, "GetCurrentClassLogger")) { // We have a possible match var getLoggerMethod = GetGetLoggerMethod(methodReference.DeclaringType); if (getLoggerMethod == null) { var point = methodReference.Resolve().GetSequencePoint(instruction); var message = $"Cannot change method call for log '{type.FullName}', the GetLogger(type) method does not exist on the calling type (try to use LogManager.GetCurrentClassLogger())"; if (point != null) { FodyEnvironment.LogWarningPoint(message, point); } else { FodyEnvironment.LogWarning(message); } continue; } var getTypeFromHandle = type.Module.GetMethodAndImport("GetTypeFromHandle"); instructions.RemoveAt(i); instructions.Insert(i, Instruction.Create(OpCodes.Ldtoken, type), Instruction.Create(OpCodes.Call, getTypeFromHandle), Instruction.Create(OpCodes.Call, type.Module.ImportReference(getLoggerMethod))); } } } }
private void Process(List <CatelType> catelTypes) { foreach (var catelType in catelTypes) { foreach (var propertyData in catelType.Properties.ToList()) { var warning = CheckForWarning(propertyData); if (warning != null) { FodyEnvironment.LogDebug($"\t{propertyData.PropertyDefinition.GetName()} {warning} property will be ignored."); catelType.Properties.Remove(propertyData); } } } }
private bool IsPrivateReference(string assemblyName) { var privateReferences = FindPrivateReferences(); var userProfilePath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var nuGetRoot = Path.Combine(userProfilePath, ".nuget", "packages"); // For now, ignore the target version, just check whether the package (version) contains the assembly foreach (var privateReference in privateReferences) { try { // For some packages (such as Fody), there is no /lib folder, in that case we don't need // to check anything var path = Path.Combine(nuGetRoot, privateReference.PackageName, privateReference.Version, "lib"); if (!Directory.Exists(path)) { continue; } FodyEnvironment.LogDebug($"Checking private reference '{privateReference}' using '{path}'"); var isDll = Directory.GetFiles(path, $"{assemblyName}.dll", SearchOption.AllDirectories).Any(); if (isDll) { return(true); } var isExe = Directory.GetFiles(path, $"{assemblyName}.exe", SearchOption.AllDirectories).Any(); if (isExe) { return(true); } } catch (Exception ex) { FodyEnvironment.LogError($"Failed to check private reference '{privateReference}':\n{ex}"); } } return(false); }
private void Process(List <CatelType> catelTypes) { foreach (var catelType in catelTypes) { if (catelType.SetValueInvoker == null) { continue; } if (_configuration.WeaveCalculatedProperties) { var onPropertyChangedWeaver = new OnPropertyChangedWeaver(catelType, _msCoreReferenceFinder); onPropertyChangedWeaver.Execute(); } FodyEnvironment.LogDebug("\t" + catelType.TypeDefinition.FullName); foreach (var propertyData in catelType.Properties) { if (AlreadyContainsCallToMember(propertyData.PropertyDefinition, catelType.GetValueInvoker.Name) || AlreadyContainsCallToMember(propertyData.PropertyDefinition, catelType.SetValueInvoker.Name)) { FodyEnvironment.LogDebug($"\t{propertyData.PropertyDefinition.GetName()} already has GetValue and/or SetValue functionality. Property will be ignored."); continue; } var body = propertyData.PropertyDefinition.SetMethod.Body; body.SimplifyMacros(); var propertyWeaver = new CatelPropertyWeaver(catelType, propertyData, _msCoreReferenceFinder); propertyWeaver.Execute(); body.InitLocals = true; body.OptimizeMacros(); } } }
private void RemoveObsoleteCodeForArgumentExpression(MethodDefinition method, Collection <Instruction> instructions, TypeDefinition displayClassType) { // Display class is used when there are still calls to load a field from the display class if (instructions.UsesType(displayClassType, OpCodes.Ldfld, OpCodes.Ldftn)) { return; } FodyEnvironment.LogDebug($"Method '{method.GetFullName()}' no longer uses display class '{displayClassType.GetFullName()}', removing the display class from the method"); // Remove display class from container if (method.DeclaringType.NestedTypes.Contains(displayClassType)) { method.DeclaringType.NestedTypes.Remove(displayClassType); } // Remove display class - variables for (var i = 0; i < method.Body.Variables.Count; i++) { var variable = method.Body.Variables[i]; if (string.Equals(variable.VariableType.Name, displayClassType.Name)) { method.Body.Variables.RemoveAt(i--); } } // Remove display class creation, can be either: // // Msbuild // L_0000: newobj instance void Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass1a::.ctor() // L_0005: stloc.0 // // Roslyn // L_0000: newobj instance void Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass1a::.ctor() // L_0005: dup for (var i = 0; i < instructions.Count; i++) { var innerInstruction = instructions[i]; if (innerInstruction.OpCode == OpCodes.Newobj) { var remove = false; var methodReference = innerInstruction.Operand as MethodReference; if (methodReference != null) { if (string.Equals(methodReference.DeclaringType.Name, displayClassType.Name)) { remove = true; } } var methodDefinition = innerInstruction.Operand as MethodDefinition; if (methodDefinition != null) { if (string.Equals(methodDefinition.DeclaringType.Name, displayClassType.Name)) { remove = true; } } if (remove) { // Delete 2 instructions, same location since remove will move everything 1 place up instructions.RemoveAt(i); // Special case in .net core if (instructions[i].OpCode == OpCodes.Dup) { instructions.RemoveAt(i); } instructions.RemoveAt(i); } } } //// Remove all assignments to the display class //// ldarg.0 //// stfld class MyClass/<>c__DisplayClass0_0`1<!!T>::myArgument //for (var i = 0; i < instructions.Count; i++) //{ // var innerInstruction = instructions[i]; //} // Remove display class allocation and assigments // L_0014: ldloc.0 (can also be dup) // L_0015: ldarg.3 // L_0016: stfld object Catel.Fody.TestAssembly.ArgumentChecksAsExpressionsClass/<>c__DisplayClass28::myObject3 for (var i = 0; i < instructions.Count; i++) { var innerInstruction = instructions[i]; if (innerInstruction.UsesType(displayClassType, OpCodes.Stfld)) { // Remove the stfld + 2 previous operations instructions.RemoveAt(i); instructions.RemoveAt(i - 1); instructions.RemoveAt(i - 2); i -= 3; } } // Remove duplicate nop instructions at the start of a method if (instructions.Count > 0) { var startInstruction = instructions[0]; if (startInstruction.IsOpCode(OpCodes.Nop)) { instructions.RemoveAt(0); } } }
private void ProcessMethod(MethodDefinition method) { if (method.Body == null) { return; } // Note: very important to only simplify/optimize methods that we actually change, otherwise some Mono.Cecil bugs // will appear on the surface Collection <Instruction> instructions = null; var methodFullName = method.GetFullName(); FodyEnvironment.LogDebug($"Processing method '{methodFullName}'"); // Step 1) Convert attributes // TODO: how to handle async/await here? for (var i = method.Parameters.Count - 1; i >= 0; i--) { var parameter = method.Parameters[i]; for (var j = parameter.CustomAttributes.Count - 1; j >= 0; j--) { var customAttribute = parameter.CustomAttributes[j]; var attributeFullName = customAttribute.AttributeType.FullName; if (ArgumentMethodCallWeaverBase.WellKnownWeavers.ContainsKey(attributeFullName)) { if (instructions == null) { method.Body.SimplifyMacros(); instructions = method.Body.Instructions; } ArgumentMethodCallWeaverBase.WellKnownWeavers[attributeFullName].Execute(_typeDefinition, method, parameter, customAttribute, 0); parameter.RemoveAttribute(attributeFullName); } else if (attributeFullName.StartsWith("Catel.Fody")) { FodyEnvironment.LogErrorPoint($"Weaving of parameter '{method.GetFullName()}' of methods '{parameter.Name}' with attribute '{attributeFullName}' is not (yet) supported, please use a different method", method.GetFirstSequencePoint()); } } } // Step 2) Convert expressions to normal calls var displayClasses = new List <TypeDefinition>(); // Go backwards to keep the order of the arguments correct (because argument checks are injected at the beginnen of the ctor) if (instructions != null || ContainsArgumentChecks(method)) { if (instructions == null) { method.Body.SimplifyMacros(); instructions = method.Body.Instructions; } for (var i = instructions.Count - 1; i >= 0; i--) { var instruction = instructions[i]; if (IsSupportedExpressionArgumentCheck(method, instruction)) { var fullKey = ((MethodReference)instruction.Operand).GetFullName(); var parameterOrField = GetParameterOrFieldForExpressionArgumentCheck(method, instructions, instruction); if (parameterOrField == null) { FodyEnvironment.LogWarning($"Cannot weave at least one argument of method '{method.GetFullName()}'"); continue; } if (!ExpressionChecksToAttributeMappings.ContainsKey(fullKey)) { return; } var customAttribute = ExpressionChecksToAttributeMappings[fullKey](method, instructions, instruction); if (customAttribute == null) { FodyEnvironment.LogWarningPoint($"Expression argument method transformation in '{method.GetFullName()}' to '{fullKey}' is not (yet) supported. To ensure the best performance, either rewrite this into a non-expression argument check or create a PR for Catel.Fody to enable support :-)", method.Body.Instructions.GetSequencePoint(instruction)); continue; } var removedInfo = RemoveArgumentWeavingCall(method, instructions, instruction); if (!displayClasses.Contains(removedInfo.Item1)) { displayClasses.Add(removedInfo.Item1); } var weaver = ArgumentMethodCallWeaverBase.WellKnownWeavers[customAttribute.AttributeType.FullName]; if (!weaver.Execute(_typeDefinition, method, parameterOrField, customAttribute, removedInfo.Item2)) { // We failed, the build should fail now return; } // Reset counter, start from the beginning i = instructions.Count - 1; } } // Step 3) Clean up unnecessary code if (displayClasses.Count > 0) { foreach (var displayClass in displayClasses) { RemoveObsoleteCodeForArgumentExpression(method, instructions, displayClass); } } } if (instructions != null) { method.Body.OptimizeMacros(); } }